/****************************************************************************** * Product: Adempiere ERP & CRM Smart Business Solution * * Copyright (C) 1999-2006 ComPiere, Inc. All Rights Reserved. * * 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. * * For the text or an alternative of this public license, you may reach us * * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA * * or via info@compiere.org or http://www.compiere.org/license.html * *****************************************************************************/ package org.compiere.model; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.text.DecimalFormat; import java.text.NumberFormat; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Date; import java.util.Properties; import java.util.Vector; import java.util.logging.Level; import org.adempiere.exceptions.AdempiereException; import org.adempiere.exceptions.DBException; import org.compiere.db.CConnection; import org.compiere.util.CLogMgt; import org.compiere.util.CLogger; import org.compiere.util.DB; import org.compiere.util.Env; import org.compiere.util.Ini; import org.compiere.util.Trx; /** * Sequence Model. * @see org.compiere.process.SequenceCheck * @author Jorg Janke * @version $Id: MSequence.java,v 1.3 2006/07/30 00:58:04 jjanke Exp $ */ public class MSequence extends X_AD_Sequence { /** * */ private static final long serialVersionUID = -1204207754819125876L; /** Log Level for Next ID Call */ private static final Level LOGLEVEL = Level.ALL; private static final int QUERY_TIME_OUT = 10; private static final String NoYearNorMonth = "-"; public static int getNextID (int AD_Client_ID, String TableName) { return getNextID(AD_Client_ID, TableName, null); } /** * * Get next number for Key column = 0 is Error. * @param AD_Client_ID client * @param TableName table name * @param trxName deprecated. * @return next no or (-1=not found, -2=error) */ public static int getNextID (int AD_Client_ID, String TableName, String trxName) { if (TableName == null || TableName.length() == 0) throw new IllegalArgumentException("TableName missing"); int retValue = -1; // Check AdempiereSys boolean adempiereSys = Ini.isPropertyBool(Ini.P_ADEMPIERESYS); if (adempiereSys && AD_Client_ID > 11) adempiereSys = false; // if (CLogMgt.isLevel(LOGLEVEL)) s_log.log(LOGLEVEL, TableName + " - AdempiereSys=" + adempiereSys + " [" + trxName + "]"); //begin vpj-cd e-evolution 09/02/2005 PostgreSQL String selectSQL = null; if (DB.isOracle() == false) { selectSQL = "SELECT CurrentNext, CurrentNextSys, IncrementNo, AD_Sequence_ID " + "FROM AD_Sequence " + "WHERE Name=?" + " AND IsActive='Y' AND IsTableID='Y' AND IsAutoSequence='Y' " + " FOR UPDATE OF AD_Sequence "; } else { selectSQL = "SELECT CurrentNext, CurrentNextSys, IncrementNo, AD_Sequence_ID " + "FROM AD_Sequence " + "WHERE Name=?" + " AND IsActive='Y' AND IsTableID='Y' AND IsAutoSequence='Y' "; } Connection conn = null; PreparedStatement pstmt = null; ResultSet rs = null; for (int i = 0; i < 3; i++) { try { conn = DB.getConnectionID(); // Error if (conn == null) return -1; pstmt = conn.prepareStatement(selectSQL, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE); pstmt.setString(1, TableName); // if (DB.getDatabase().isQueryTimeoutSupported()) pstmt.setQueryTimeout(QUERY_TIME_OUT); rs = pstmt.executeQuery(); if (CLogMgt.isLevelFinest()) s_log.finest("AC=" + conn.getAutoCommit() + ", RO=" + conn.isReadOnly() + " - Isolation=" + conn.getTransactionIsolation() + "(" + Connection.TRANSACTION_READ_COMMITTED + ") - RSType=" + pstmt.getResultSetType() + "(" + ResultSet.TYPE_SCROLL_SENSITIVE + "), RSConcur=" + pstmt.getResultSetConcurrency() + "(" + ResultSet.CONCUR_UPDATABLE + ")"); if (rs.next()) { // Get the table MTable table = MTable.get(Env.getCtx(), TableName); int AD_Sequence_ID = rs.getInt(4); boolean gotFromHTTP = false; // If maintaining official dictionary try to get the ID from http official server if (adempiereSys) { String isUseCentralizedID = MSysConfig.getValue("DICTIONARY_ID_USE_CENTRALIZED_ID", "Y"); // defaults to Y if ( ( ! isUseCentralizedID.equals("N") ) && ( ! isExceptionCentralized(TableName) ) ) { // get ID from http site retValue = getNextOfficialID_HTTP(TableName); if (retValue > 0) { PreparedStatement updateSQL; updateSQL = conn.prepareStatement("UPDATE AD_Sequence SET CurrentNextSys = ? + 1 WHERE AD_Sequence_ID = ?"); try { updateSQL.setInt(1, retValue); updateSQL.setInt(2, AD_Sequence_ID); updateSQL.executeUpdate(); } finally { updateSQL.close(); } } gotFromHTTP = true; } } boolean queryProjectServer = false; if (table.getColumn("EntityType") != null) queryProjectServer = true; if (!queryProjectServer && MSequence.Table_Name.equalsIgnoreCase(TableName)) queryProjectServer = true; // If not official dictionary try to get the ID from http custom server - if configured if (queryProjectServer && ( ! adempiereSys ) && ( ! isExceptionCentralized(TableName) ) ) { String isUseProjectCentralizedID = MSysConfig.getValue("PROJECT_ID_USE_CENTRALIZED_ID", "N"); // defaults to N if (isUseProjectCentralizedID.equals("Y")) { // get ID from http site retValue = getNextProjectID_HTTP(TableName); if (retValue > 0) { PreparedStatement updateSQL; updateSQL = conn.prepareStatement("UPDATE AD_Sequence SET CurrentNext = GREATEST(CurrentNext, ? + 1) WHERE AD_Sequence_ID = ?"); try { updateSQL.setInt(1, retValue); updateSQL.setInt(2, AD_Sequence_ID); updateSQL.executeUpdate(); } finally { updateSQL.close(); } } gotFromHTTP = true; } } if (! gotFromHTTP) { PreparedStatement updateSQL; int incrementNo = rs.getInt(3); if (adempiereSys) { updateSQL = conn .prepareStatement("UPDATE AD_Sequence SET CurrentNextSys = CurrentNextSys + ? WHERE AD_Sequence_ID = ?"); retValue = rs.getInt(2); } else { updateSQL = conn .prepareStatement("UPDATE AD_Sequence SET CurrentNext = CurrentNext + ? WHERE AD_Sequence_ID = ?"); retValue = rs.getInt(1); } try { updateSQL.setInt(1, incrementNo); updateSQL.setInt(2, AD_Sequence_ID); updateSQL.executeUpdate(); } finally { updateSQL.close(); } } //if (trx == null) conn.commit(); } else s_log.severe ("No record found - " + TableName); // break; // EXIT } catch (Exception e) { s_log.log(Level.SEVERE, TableName + " - " + e.getMessage(), e); try { if (conn != null) conn.rollback(); } catch (SQLException e1) { } } finally { DB.close(rs, pstmt); pstmt = null; rs = null; if (conn != null) { try { conn.close(); } catch (SQLException e) {} conn = null; } } Thread.yield(); // give it time } //s_log.finest (retValue + " - Table=" + TableName + " [" + trx + "]"); return retValue; } // getNextID /************************************************************************** * Get Document No from table * @param AD_Client_ID client * @param TableName table name * @param trxName optional Transaction Name * @return document no or null */ public static String getDocumentNo (int AD_Client_ID, String TableName, String trxName) { return getDocumentNo(AD_Client_ID, TableName, trxName, null); } /************************************************************************** * Get Document No from table (when the document doesn't have a c_doctype) * @param AD_Client_ID client * @param TableName table name * @param trxName optional Transaction Name * @param PO - used to get the date, org and parse context variables * @return document no or null */ public static String getDocumentNo (int AD_Client_ID, String TableName, String trxName, PO po) { if (TableName == null || TableName.length() == 0) throw new IllegalArgumentException("TableName missing"); MSequence seq = get(Env.getCtx(), TableName, trxName, /*tableID=*/false); if (seq == null || seq.get_ID() == 0) { if (!MSequence.createTableSequence(Env.getCtx(), TableName, trxName, /*tableID=*/false)) throw new AdempiereException("Could not create table sequence"); seq = get(Env.getCtx(), TableName, trxName, /*tableID=*/false); if (seq == null || seq.get_ID() == 0) throw new AdempiereException("Could not find table sequence"); } return getDocumentNoFromSeq(seq, trxName, po); } // getDocumentNo private static String getDocumentNoFromSeq(MSequence seq, String trxName, PO po) { // Check AdempiereSys boolean adempiereSys = Ini.isPropertyBool(Ini.P_ADEMPIERESYS); if (adempiereSys && Env.getAD_Client_ID(Env.getCtx()) > 11) adempiereSys = false; // int AD_Sequence_ID = seq.getAD_Sequence_ID(); boolean isStartNewYear = seq.isStartNewYear(); boolean isStartNewMonth = seq.isStartNewMonth(); String dateColumn = seq.getDateColumn(); boolean isUseOrgLevel = seq.isOrgLevelSequence(); String orgColumn = seq.getOrgColumn(); int startNo = seq.getStartNo(); int incrementNo = seq.getIncrementNo(); String prefix = seq.getPrefix(); String suffix = seq.getSuffix(); String decimalPattern = seq.getDecimalPattern(); String selectSQL = null; if (isStartNewYear || isUseOrgLevel) { selectSQL = "SELECT y.CurrentNext, s.CurrentNextSys " + "FROM AD_Sequence_No y, AD_Sequence s " + "WHERE y.AD_Sequence_ID = s.AD_Sequence_ID " + "AND s.AD_Sequence_ID = ? " + "AND y.CalendarYearMonth = ? " + "AND y.AD_Org_ID = ? " + "AND s.IsActive='Y' AND s.IsTableID='N' AND s.IsAutoSequence='Y' " + "ORDER BY s.AD_Client_ID DESC"; } else { selectSQL = "SELECT s.CurrentNext, s.CurrentNextSys " + "FROM AD_Sequence s " + "WHERE s.AD_Sequence_ID = ? " + "AND s.IsActive='Y' AND s.IsTableID='N' AND s.IsAutoSequence='Y' " + "ORDER BY s.AD_Client_ID DESC"; } if (DB.isPostgreSQL()) { if ( ( isStartNewYear || isUseOrgLevel ) && !adempiereSys ) { selectSQL = selectSQL + " FOR UPDATE OF y"; } else { selectSQL = selectSQL + " FOR UPDATE OF s"; } } Connection conn = null; Trx trx = trxName == null ? null : Trx.get(trxName, true); // String calendarYearMonth = NoYearNorMonth; int docOrg_ID = 0; int next = -1; PreparedStatement pstmt = null; ResultSet rs = null; try { if (trx != null) conn = trx.getConnection(); else conn = DB.getConnectionID(); // Error if (conn == null) return null; if (isStartNewYear) { SimpleDateFormat sdf = null; if (isStartNewMonth) sdf = new SimpleDateFormat("yyyyMM"); else sdf = new SimpleDateFormat("yyyy"); if (po != null && dateColumn != null && dateColumn.length() > 0) { Date docDate = (Date)po.get_Value(dateColumn); calendarYearMonth = sdf.format(docDate); } else { calendarYearMonth = sdf.format(new Date()); } } if (isUseOrgLevel) { if (po != null && orgColumn != null && orgColumn.length() > 0) { docOrg_ID = po.get_ValueAsInt(orgColumn); } } pstmt = conn.prepareStatement(selectSQL, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE); int index = 1; pstmt.setInt(index++, AD_Sequence_ID); if (isUseOrgLevel || isStartNewYear) { pstmt.setString(index++, calendarYearMonth); pstmt.setInt(index++, docOrg_ID); } // if (DB.getDatabase().isQueryTimeoutSupported()) pstmt.setQueryTimeout(QUERY_TIME_OUT); rs = pstmt.executeQuery(); // s_log.fine("AC=" + conn.getAutoCommit() + " -Iso=" + conn.getTransactionIsolation() // + " - Type=" + pstmt.getResultSetType() + " - Concur=" + pstmt.getResultSetConcurrency()); if (rs.next()) { s_log.fine("AD_Sequence_ID="+AD_Sequence_ID); PreparedStatement updateSQL = null; try { if (adempiereSys) { updateSQL = conn.prepareStatement("UPDATE AD_Sequence SET CurrentNextSys = CurrentNextSys + ? WHERE AD_Sequence_ID = ?"); next = rs.getInt(2); } else { String sql; if (isStartNewYear || isUseOrgLevel) sql = "UPDATE AD_Sequence_No SET CurrentNext = CurrentNext + ? WHERE AD_Sequence_ID=? AND CalendarYearMonth=? AND AD_Org_ID=?"; else sql = "UPDATE AD_Sequence SET CurrentNext = CurrentNext + ? WHERE AD_Sequence_ID=?"; updateSQL = conn.prepareStatement(sql); next = rs.getInt(1); } updateSQL.setInt(1, incrementNo); updateSQL.setInt(2, AD_Sequence_ID); if (isStartNewYear || isUseOrgLevel) { updateSQL.setString(3, calendarYearMonth); updateSQL.setInt(4, docOrg_ID); } updateSQL.executeUpdate(); } finally { DB.close(updateSQL); } } else { // did not find sequence no if (isUseOrgLevel || isStartNewYear) { // create sequence (CurrentNo = StartNo + IncrementNo) for this year/month/org and return first number (=StartNo) next = startNo; X_AD_Sequence_No seqno = new X_AD_Sequence_No(Env.getCtx(), 0, trxName); seqno.setAD_Sequence_ID(AD_Sequence_ID); seqno.setAD_Org_ID(docOrg_ID); seqno.setCalendarYearMonth(calendarYearMonth); seqno.setCurrentNext(startNo + incrementNo); seqno.saveEx(); } else // standard { s_log.warning ("(Sequence)- no record found - " + seq); next = -2; } } // Commit if (trx == null) { conn.commit(); } } catch (Exception e) { s_log.log(Level.SEVERE, "(DocType) [" + trxName + "]", e); if (DBException.isTimeout(e)) throw new AdempiereException("GenerateDocumentNoTimeOut", e); else throw new AdempiereException("GenerateDocumentNoError", e); } finally { DB.close(rs, pstmt); // Finish try { if (trx == null && conn != null) { conn.close(); conn = null; } } catch (Exception e) { s_log.log(Level.SEVERE, "(DocType) - finish", e); } } // Error if (next < 0) return null; // create DocumentNo StringBuffer doc = new StringBuffer(); if (prefix != null && prefix.length() > 0) doc.append(Env.parseVariable(prefix, po, trxName, false)); if (decimalPattern != null && decimalPattern.length() > 0) doc.append(new DecimalFormat(decimalPattern).format(next)); else doc.append(next); if (suffix != null && suffix.length() > 0) doc.append(Env.parseVariable(suffix, po, trxName, false)); String documentNo = doc.toString(); s_log.finer (documentNo + " (" + incrementNo + ")" + " - Sequence=" + AD_Sequence_ID + " [" + trx + "]"); return documentNo; } /** * Get Document No based on Document Type * @param C_DocType_ID document type * @param trxName optional Transaction Name * @return document no or null * @deprecated */ public static String getDocumentNo(int C_DocType_ID, String trxName) { return getDocumentNo (C_DocType_ID, trxName, false); } // getDocumentNo /** * Get Document No based on Document Type * @param C_DocType_ID document type * @param trxName optional Transaction Name * @param definite asking for a definitive or temporary sequence * @return document no or null */ public static String getDocumentNo (int C_DocType_ID, String trxName, boolean definite) { return getDocumentNo(C_DocType_ID, trxName, definite, null); } /** * Get Document No based on Document Type * @param C_DocType_ID document type * @param trxName optional Transaction Name * @param definite asking for a definitive or temporary sequence * @param po * @return document no or null */ public static String getDocumentNo (int C_DocType_ID, String trxName, boolean definite, PO po) { if (C_DocType_ID == 0) { s_log.severe ("C_DocType_ID=0"); return null; } MDocType dt = MDocType.get (Env.getCtx(), C_DocType_ID); // wrong for SERVER, but r/o if (dt != null && !dt.isDocNoControlled()) { s_log.finer("DocType_ID=" + C_DocType_ID + " Not DocNo controlled"); return null; } if (definite && ! dt.isOverwriteSeqOnComplete()) { s_log.warning("DocType_ID=" + C_DocType_ID + " Not Sequence Overwrite on Complete"); return null; } if (dt == null || dt.getDocNoSequence_ID() == 0) { s_log.warning ("No Sequence for DocType - " + dt); return null; } if (definite && dt.getDefiniteSequence_ID() == 0) { s_log.warning ("No Definite Sequence for DocType - " + dt); return null; } int seqID = ( definite ? dt.getDefiniteSequence_ID() : dt.getDocNoSequence_ID() ); MSequence seq = new MSequence(Env.getCtx(), seqID, trxName); if (CLogMgt.isLevel(LOGLEVEL)) s_log.log(LOGLEVEL, "DocType_ID=" + C_DocType_ID + " [" + trxName + "]"); return getDocumentNoFromSeq(seq, trxName, po); } // getDocumentNo /************************************************************************** * Check/Initialize Client DocumentNo/Value Sequences * @param ctx context * @param AD_Client_ID client * @param trxName transaction * @return true if no error */ public static boolean checkClientSequences (Properties ctx, int AD_Client_ID, String trxName) { String sql = "SELECT TableName " + "FROM AD_Table t " + "WHERE IsActive='Y' AND IsView='N'" // Get all Tables with DocumentNo or Value + " AND AD_Table_ID IN " + "(SELECT AD_Table_ID FROM AD_Column " + "WHERE ColumnName = 'DocumentNo' OR ColumnName = 'Value')" // Ability to run multiple times + " AND 'DocumentNo_' || TableName NOT IN " + "(SELECT Name FROM AD_Sequence s " + "WHERE s.AD_Client_ID=?)"; int counter = 0; boolean success = true; // PreparedStatement pstmt = null; ResultSet rs = null; try { pstmt = DB.prepareStatement(sql, trxName); pstmt.setInt(1, AD_Client_ID); rs = pstmt.executeQuery(); while (rs.next()) { String tableName = rs.getString(1); s_log.fine("Add: " + tableName); MSequence seq = new MSequence (ctx, AD_Client_ID, tableName, trxName); if (seq.save()) counter++; else { s_log.severe ("Not created - AD_Client_ID=" + AD_Client_ID + " - " + tableName); success = false; } } } catch (Exception e) { s_log.log(Level.SEVERE, sql, e); } finally { DB.close(rs, pstmt); rs = null; pstmt = null; } s_log.info ("AD_Client_ID=" + AD_Client_ID + " - created #" + counter + " - success=" + success); return success; } // checkClientSequences public static boolean createTableSequence (Properties ctx, String TableName, String trxName) { return createTableSequence (ctx, TableName, trxName, true); } /** * Create Table ID Sequence * @param ctx context * @param TableName table name * @param trxName transaction * @return true if created */ public static boolean createTableSequence (Properties ctx, String TableName, String trxName, boolean tableID) { boolean SYSTEM_NATIVE_SEQUENCE = MSysConfig.getBooleanValue("SYSTEM_NATIVE_SEQUENCE",false); MSequence seq = new MSequence (ctx, 0, trxName); if (tableID) seq.setClientOrg(0, 0); else seq.setClientOrg(Env.getAD_Client_ID(Env.getCtx()), 0); if (tableID) { seq.setName(TableName); seq.setDescription("Table " + TableName); } else { seq.setName(PREFIX_DOCSEQ + TableName); seq.setDescription("DocumentNo/Value for Table " + TableName); } seq.setIsTableID(tableID); seq.saveEx(); if (tableID && SYSTEM_NATIVE_SEQUENCE) { int next_id = seq.getCurrentNext(); if (! CConnection.get().getDatabase().createSequence(TableName+"_SQ", 1, 0 , 99999999, next_id, trxName)) return false; } return true; } // createTableSequence /** * Get Sequence * @param ctx context * @param tableName table name * @return Sequence */ public static MSequence get (Properties ctx, String tableName) { return get(ctx, tableName, null); } /* Get the tableID sequence based on the TableName */ public static MSequence get (Properties ctx, String tableName, String trxName) { return get (ctx, tableName, trxName, true); } /** * Get Sequence * @param ctx context * @param tableName table name * @param trxName optional transaction name * @param tableID * @return Sequence */ public static MSequence get (Properties ctx, String tableName, String trxName, boolean tableID) { if (! tableID) { tableName = PREFIX_DOCSEQ + tableName; } String sql = "SELECT * FROM AD_Sequence " + "WHERE UPPER(Name)=?" + " AND IsTableID=?"; if (! tableID) sql = sql + " AND AD_Client_ID=?"; MSequence retValue = null; PreparedStatement pstmt = null; ResultSet rs = null; try { pstmt = DB.prepareStatement (sql, trxName); pstmt.setString(1, tableName.toUpperCase()); pstmt.setString(2, ( tableID ? "Y" : "N" ) ); if (! tableID) pstmt.setInt (3, Env.getAD_Client_ID(Env.getCtx())); rs = pstmt.executeQuery(); if (rs.next ()) retValue = new MSequence (ctx, rs, trxName); if (rs.next()) s_log.log(Level.SEVERE, "More then one sequence for " + tableName); } catch (Exception e) { s_log.log(Level.SEVERE, "get", e); } finally { DB.close(rs, pstmt); rs = null; pstmt = null; } return retValue; } // get /** Sequence for Table Document No's */ private static final String PREFIX_DOCSEQ = "DocumentNo_"; /** Start Number */ public static final int INIT_NO = 1000000; // 1M /** Start System Number */ // public static final int INIT_SYS_NO = 100; // start number for Compiere // public static final int INIT_SYS_NO = 50000; // start number for Adempiere public static final int INIT_SYS_NO = 200000; // start number for iDempiere /** Static Logger */ private static CLogger s_log = CLogger.getCLogger(MSequence.class); /************************************************************************** * Standard Constructor * @param ctx context * @param AD_Sequence_ID id * @param trxName transaction */ public MSequence (Properties ctx, int AD_Sequence_ID, String trxName) { super(ctx, AD_Sequence_ID, trxName); if (AD_Sequence_ID == 0) { // setName (null); // setIsTableID(false); setStartNo (INIT_NO); setCurrentNext (INIT_NO); setCurrentNextSys (INIT_SYS_NO); setIncrementNo (1); setIsAutoSequence (true); setIsAudited(false); setStartNewYear(false); } } // MSequence /** * Load Constructor * @param ctx context * @param rs result set * @param trxName transaction */ public MSequence (Properties ctx, ResultSet rs, String trxName) { super(ctx, rs, trxName); } // MSequence /** * New Document Sequence Constructor * @param ctx context * @param AD_Client_ID owner * @param tableName name * @param trxName transaction */ public MSequence (Properties ctx, int AD_Client_ID, String tableName, String trxName) { this (ctx, 0, trxName); setClientOrg(AD_Client_ID, 0); // Client Ownership setName(PREFIX_DOCSEQ + tableName); setDescription("DocumentNo/Value for Table " + tableName); } // MSequence; /** * New Document Sequence Constructor * @param ctx context * @param AD_Client_ID owner * @param sequenceName name * @param StartNo start * @param trxName trx */ public MSequence (Properties ctx, int AD_Client_ID, String sequenceName, int StartNo, String trxName) { this (ctx, 0, trxName); setClientOrg(AD_Client_ID, 0); // Client Ownership setName(sequenceName); setDescription(sequenceName); setStartNo(StartNo); setCurrentNext(StartNo); setCurrentNextSys(StartNo/10); } // MSequence; /************************************************************************** * Get Next No and increase current next * @return next no to use */ public int getNextID() { int retValue = getCurrentNext(); setCurrentNext(retValue + getIncrementNo()); return retValue; } // getNextNo /** * Validate Table Sequence Values * @return true if updated */ public boolean validateTableIDValue() { if (!isTableID()) return false; String tableName = getName(); int AD_Column_ID = DB.getSQLValue(null, "SELECT MAX(c.AD_Column_ID) " + "FROM AD_Table t" + " INNER JOIN AD_Column c ON (t.AD_Table_ID=c.AD_Table_ID) " + "WHERE t.TableName='" + tableName + "'" + " AND c.ColumnName='" + tableName + "_ID'"); if (AD_Column_ID <= 0) return false; // MSystem system = MSystem.get(getCtx()); int IDRangeEnd = 0; if (system.getIDRangeEnd() != null) IDRangeEnd = system.getIDRangeEnd().intValue(); boolean change = false; String info = null; // Current Next String sql = "SELECT MAX(" + tableName + "_ID) FROM " + tableName; if (IDRangeEnd > 0) sql += " WHERE " + tableName + "_ID < " + IDRangeEnd; int maxTableID = DB.getSQLValue(null, sql); if (maxTableID < INIT_NO) maxTableID = INIT_NO - 1; maxTableID++; // Next if (getCurrentNext() < maxTableID) { setCurrentNext(maxTableID); info = "CurrentNext=" + maxTableID; change = true; } // Get Max System_ID used in Table sql = "SELECT MAX(" + tableName + "_ID) FROM " + tableName + " WHERE " + tableName + "_ID < " + INIT_NO; int maxTableSysID = DB.getSQLValue(null, sql); if (maxTableSysID <= 0) maxTableSysID = INIT_SYS_NO - 1; maxTableSysID++; // Next if (getCurrentNextSys() < maxTableSysID) { setCurrentNextSys(maxTableSysID); if (info == null) info = "CurrentNextSys=" + maxTableSysID; else info += " - CurrentNextSys=" + maxTableSysID; change = true; } if (info != null) log.fine(getName() + " - " + info); return change; } // validate /************************************************************************** * Test * @param args ignored */ static public void main (String[] args) { // int id = getNextID_HTTP("AD_Column"); // if (true) return; org.compiere.Adempiere.startup(true); CLogMgt.setLevel(Level.SEVERE); CLogMgt.setLoggerLevel(Level.SEVERE, null); s_list = new Vector<Integer>(1000); /** Lock Test ** String trxName = "test"; System.out.println(DB.getDocumentNo(115, trxName)); System.out.println(DB.getDocumentNo(116, trxName)); System.out.println(DB.getDocumentNo(117, trxName)); System.out.println(DB.getDocumentNo(118, trxName)); System.out.println(DB.getDocumentNo(118, trxName)); System.out.println(DB.getDocumentNo(117, trxName)); trxName = "test1"; System.out.println(DB.getDocumentNo(115, trxName)); // hangs here as supposed System.out.println(DB.getDocumentNo(116, trxName)); System.out.println(DB.getDocumentNo(117, trxName)); System.out.println(DB.getDocumentNo(118, trxName)); /** **/ /** Time Test */ long time = System.currentTimeMillis(); Thread[] threads = new Thread[10]; for (int i = 0; i < 10; i++) { Runnable r = new GetIDs(i); threads[i] = new Thread(r); threads[i].start(); } for (int i = 0; i < 10; i++) { try { threads[i].join(); } catch (InterruptedException e) { } } time = System.currentTimeMillis() - time; System.out.println("-------------------------------------------"); System.out.println("Size=" + s_list.size() + " (should be 1000)"); Integer[] ia = new Integer[s_list.size()]; s_list.toArray(ia); Arrays.sort(ia); Integer last = null; int duplicates = 0; for (int i = 0; i < ia.length; i++) { if (last != null) { if (last.compareTo(ia[i]) == 0) { // System.out.println(i + ": " + ia[i]); duplicates++; } } last = ia[i]; } System.out.println("-------------------------------------------"); System.out.println("Size=" + s_list.size() + " (should be 1000)"); System.out.println("Duplicates=" + duplicates); System.out.println("Time (ms)=" + time + " - " + ((float)time/s_list.size()) + " each" ); System.out.println("-------------------------------------------"); /** ** try { int retValue = -1; Connection conn = DB.getConnectionRW (); // DriverManager.registerDriver (new oracle.jdbc.driver.OracleDriver()); // Connection conn = DriverManager.getConnection ("jdbc:oracle:thin:@//dev2:1521/dev2", "adempiere", "adempiere"); conn.setAutoCommit(false); String sql = "SELECT CurrentNext, CurrentNextSys, IncrementNo " + "FROM AD_Sequence " + "WHERE Name='AD_Sequence' "; sql += "FOR UPDATE"; // creates ORA-00907: missing right parenthesis // sql += "FOR UPDATE OF CurrentNext, CurrentNextSys"; PreparedStatement pstmt = conn.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE); ResultSet rs = pstmt.executeQuery(); System.out.println("AC=" + conn.getAutoCommit() + ", RO=" + conn.isReadOnly() + " - Isolation=" + conn.getTransactionIsolation() + "(" + Connection.TRANSACTION_READ_COMMITTED + ") - RSType=" + pstmt.getResultSetType() + "(" + ResultSet.TYPE_SCROLL_SENSITIVE + "), RSConcur=" + pstmt.getResultSetConcurrency() + "(" + ResultSet.CONCUR_UPDATABLE + ")"); if (rs.next()) { int IncrementNo = rs.getInt(3); retValue = rs.getInt(1); rs.updateInt(1, retValue + IncrementNo); rs.updateRow(); } else s_log.severe ("no record found"); rs.close(); pstmt.close(); conn.commit(); conn.close(); // System.out.println("Next=" + retValue); } catch (Exception e) { e.printStackTrace (); } System.exit(0); /** ** int AD_Client_ID = 0; int C_DocType_ID = 115; // GL String TableName = "C_Invoice"; String trxName = "x"; Trx trx = Trx.get(trxName, true); System.out.println ("none " + getNextID (0, "Test")); System.out.println ("----------------------------------------------"); System.out.println ("trx1 " + getNextID (0, "Test")); System.out.println ("trx2 " + getNextID (0, "Test")); // trx.rollback(); System.out.println ("trx3 " + getNextID (0, "Test")); // trx.commit(); System.out.println ("trx4 " + getNextID (0, "Test")); // trx.rollback(); // trx.close(); System.out.println ("----------------------------------------------"); System.out.println ("none " + getNextID (0, "Test")); System.out.println ("=============================================="); trx = Trx.get(trxName, true); System.out.println ("none " + getDocumentNo(AD_Client_ID, TableName, null)); System.out.println ("----------------------------------------------"); System.out.println ("trx1 " + getDocumentNo(AD_Client_ID, TableName, trxName)); System.out.println ("trx2 " + getDocumentNo(AD_Client_ID, TableName, trxName)); trx.rollback(); System.out.println ("trx3 " + getDocumentNo(AD_Client_ID, TableName, trxName)); trx.commit(); System.out.println ("trx4 " + getDocumentNo(AD_Client_ID, TableName, trxName)); trx.rollback(); trx.close(); System.out.println ("----------------------------------------------"); System.out.println ("none " + getDocumentNo(AD_Client_ID, TableName, null)); System.out.println ("=============================================="); trx = Trx.get(trxName, true); System.out.println ("none " + getDocumentNo(C_DocType_ID, null)); System.out.println ("----------------------------------------------"); System.out.println ("trx1 " + getDocumentNo(C_DocType_ID, trxName)); System.out.println ("trx2 " + getDocumentNo(C_DocType_ID, trxName)); trx.rollback(); System.out.println ("trx3 " + getDocumentNo(C_DocType_ID, trxName)); trx.commit(); System.out.println ("trx4 " + getDocumentNo(C_DocType_ID, trxName)); trx.rollback(); trx.close(); System.out.println ("----------------------------------------------"); System.out.println ("none " + getDocumentNo(C_DocType_ID, null)); System.out.println ("=============================================="); /** **/ } // main /** Test */ private static Vector<Integer> s_list = null; /** * Test Sequence - Get IDs * * @author Jorg Janke * @version $Id: MSequence.java,v 1.3 2006/07/30 00:58:04 jjanke Exp $ */ public static class GetIDs implements Runnable { /** * Get IDs * @param i */ public GetIDs (int i) { m_i = i; } private int m_i; /** * Run */ public void run() { for (int i = 0; i < 100; i++) { try { int no = DB.getNextID(0, "Test", null); s_list.add(new Integer(no)); // System.out.println("#" + m_i + ": " + no); } catch (Exception e) { System.err.println(e.getMessage()); } } } } // GetIDs /** * Get next number for Key column * @param AD_Client_ID client * @param TableName table name * @param trxName optional Transaction Name * @return next no or (-1=error) */ public static synchronized int getNextOfficialID_HTTP (String TableName) { String website = MSysConfig.getValue("DICTIONARY_ID_WEBSITE"); // "http://developer.adempiere.com/cgi-bin/get_ID"; String prm_USER = MSysConfig.getValue("DICTIONARY_ID_USER"); // "globalqss"; String prm_PASSWORD = MSysConfig.getValue("DICTIONARY_ID_PASSWORD"); // "password_inseguro"; String prm_TABLE = TableName; String prm_ALTKEY = ""; // TODO: generate alt-key based on key of table String prm_COMMENT = MSysConfig.getValue("DICTIONARY_ID_COMMENTS"); String prm_PROJECT = new String("Adempiere"); return getNextID_HTTP(TableName, website, prm_USER, prm_PASSWORD, prm_TABLE, prm_ALTKEY, prm_COMMENT, prm_PROJECT); } /** * Get next number for Key column * @param AD_Client_ID client * @param TableName table name * @param trxName optional Transaction Name * @return next no or (-1=error) */ public static synchronized int getNextProjectID_HTTP (String TableName) { String website = MSysConfig.getValue("PROJECT_ID_WEBSITE"); // "http://developer.adempiere.com/cgi-bin/get_ID"; String prm_USER = MSysConfig.getValue("PROJECT_ID_USER"); // "globalqss"; String prm_PASSWORD = MSysConfig.getValue("PROJECT_ID_PASSWORD"); // "password_inseguro"; String prm_TABLE = TableName; String prm_ALTKEY = ""; // TODO: generate alt-key based on key of table String prm_COMMENT = MSysConfig.getValue("PROJECT_ID_COMMENTS"); String prm_PROJECT = MSysConfig.getValue("PROJECT_ID_PROJECT"); return getNextID_HTTP(TableName, website, prm_USER, prm_PASSWORD, prm_TABLE, prm_ALTKEY, prm_COMMENT, prm_PROJECT); } private static int getNextID_HTTP(String TableName, String website, String prm_USER, String prm_PASSWORD, String prm_TABLE, String prm_ALTKEY, String prm_COMMENT, String prm_PROJECT) { StringBuffer read = new StringBuffer(); int retValue = -1; try { String completeUrl = website + "?" + "USER=" + URLEncoder.encode(prm_USER, "UTF-8") + "&PASSWORD=" + URLEncoder.encode(prm_PASSWORD, "UTF-8") + "&PROJECT=" + URLEncoder.encode(prm_PROJECT, "UTF-8") + "&TABLE=" + URLEncoder.encode(prm_TABLE, "UTF-8") + "&ALTKEY=" + URLEncoder.encode(prm_ALTKEY, "UTF-8") + "&COMMENT=" + URLEncoder.encode(prm_COMMENT, "UTF-8"); // Now use the URL class to parse the user-specified URL into // its various parts: protocol, host, port, filename. Check the protocol URL url = new URL(completeUrl); String protocol = url.getProtocol(); if (!protocol.equals("http")) throw new IllegalArgumentException("URL must use 'http:' protocol"); HttpURLConnection conn = (HttpURLConnection)url.openConnection(); conn.setRequestMethod("GET"); conn.setAllowUserInteraction(false); InputStream is = conn.getInputStream(); // Now read the server's response, and write it to the file byte[] buffer = new byte[4096]; int bytes_read; while((bytes_read = is.read(buffer)) != -1) { for (int i = 0; i < bytes_read; i++) { if (buffer[i] != 10) read.append((char) buffer[i]); } } conn.disconnect(); retValue = Integer.parseInt(read.toString()); if (retValue <= 0) retValue = -1; } catch (Exception e) { // Report any errors that arise System.err.println(e); retValue = -1; } s_log.log(Level.INFO, "getNextID_HTTP - " + TableName + "=" + read + "(" + retValue + ")"); return retValue; } private static boolean isExceptionCentralized(String tableName) { String [] exceptionTables = new String[] { "AD_ACCESSLOG", "AD_ALERTPROCESSORLOG", "AD_CHANGELOG", "AD_ISSUE", "AD_LDAPPROCESSORLOG", "AD_PACKAGE_IMP", "AD_PACKAGE_IMP_BACKUP", "AD_PACKAGE_IMP_DETAIL", "AD_PACKAGE_IMP_INST", "AD_PACKAGE_IMP_PROC", "AD_PINSTANCE", "AD_PINSTANCE_LOG", "AD_PINSTANCE_PARA", "AD_REPLICATION_LOG", "AD_SCHEDULERLOG", "AD_SESSION", "AD_WORKFLOWPROCESSORLOG", "CM_WEBACCESSLOG", "C_ACCTPROCESSORLOG", "K_INDEXLOG", "R_REQUESTPROCESSORLOG", "T_AGING", "T_ALTER_COLUMN", "T_DISTRIBUTIONRUNDETAIL", "T_INVENTORYVALUE", "T_INVOICEGL", "T_REPLENISH", "T_REPORT", "T_REPORTSTATEMENT", "T_SELECTION", "T_SELECTION2", "T_SPOOL", "T_TRANSACTION", "T_TRIALBALANCE" }; for (int i = 0; i < exceptionTables.length; i++) { if (tableName.equalsIgnoreCase(exceptionTables[i])) return true; } // don't log selects or insert/update for exception tables (i.e. AD_Issue, AD_ChangeLog) return false; } @Override protected boolean beforeSave(boolean newRecord) { if (isStartNewMonth() && !isStartNewYear()) setStartNewMonth(false); return true; } /** * Get preliminary document no by year * @param tab * @param AD_Sequence_ID * @return Preliminary document no */ public static String getPreliminaryNo(GridTab tab, int AD_Sequence_ID) { String prelim = null; if (AD_Sequence_ID > 0) { MSequence seq = new MSequence(Env.getCtx(), AD_Sequence_ID, null); boolean startNewYear = seq.isStartNewYear(); boolean startNewMonth = seq.isStartNewMonth(); boolean orgLevelSeq = seq.isOrgLevelSequence(); int currentNext = seq.getCurrentNext(); if (startNewYear || orgLevelSeq) { String cym = NoYearNorMonth; int org = 0; if (startNewYear || startNewMonth) { Date d = (Date)tab.getValue(seq.getDateColumn()); if (d == null) d = new Date(); SimpleDateFormat sdf = null; if (startNewMonth) sdf = new SimpleDateFormat("yyyyMM"); else sdf = new SimpleDateFormat("yyyy"); cym = sdf.format(d); } if (orgLevelSeq) { String orgColumn = seq.getOrgColumn(); Object orgObj = tab.getValue(orgColumn); if (orgObj != null) org = (Integer)orgObj; } String sql = "SELECT CurrentNext FROM AD_Sequence_No WHERE AD_Sequence_ID=? AND CalendarYearMonth=? AND AD_Org_ID=?"; currentNext = DB.getSQLValue(null, sql, AD_Sequence_ID, cym, org); } String decimalPattern = seq.getDecimalPattern(); if (decimalPattern != null && decimalPattern.length() > 0) prelim = new DecimalFormat(decimalPattern).format(currentNext); else prelim = String.valueOf(currentNext); } if (prelim == null) prelim = "?"; return "<" + prelim + ">"; } @Override public String getOrgColumn() { if (super.getOrgColumn() == null) return COLUMNNAME_AD_Org_ID; else return super.getOrgColumn(); } // @fchiappano Codigo copiado desde Libertya para la Facturacion Electronica. /** * LOCALE AR. Setear el siguiente nĂºmero de comprobante de la secuencia * teniendo en cuenta las particularidades del prefijo y el currentnext, * para dejar correctamente seteado el current next con el punto de venta * adecuado. * * @param sequenceID * @param nextNroComprobante * @param trxName * @return */ public static boolean setFiscalDocTypeNextNroComprobante(final int sequenceID, final int nextNroComprobante, final String trxName) { MSequence seq = new MSequence(Env.getCtx(), sequenceID, trxName); int currentNext = seq.getCurrentNext(); if (currentNext != nextNroComprobante) { seq.setCurrentNext(nextNroComprobante); return seq.save(); } return true; } } // MSequence