diff --git a/serverRoot/.classpath b/serverRoot/.classpath new file mode 100644 index 0000000000..93a8c7c246 --- /dev/null +++ b/serverRoot/.classpath @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/serverRoot/.packaging b/serverRoot/.packaging new file mode 100644 index 0000000000..a336b0bb42 --- /dev/null +++ b/serverRoot/.packaging @@ -0,0 +1,2 @@ + + diff --git a/serverRoot/.project b/serverRoot/.project new file mode 100644 index 0000000000..292a281227 --- /dev/null +++ b/serverRoot/.project @@ -0,0 +1,22 @@ + + + serverRoot + + + base + dbPort + looks + server + tools + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/serverRoot/.xdoclet b/serverRoot/.xdoclet new file mode 100644 index 0000000000..a336b0bb42 --- /dev/null +++ b/serverRoot/.xdoclet @@ -0,0 +1,2 @@ + + diff --git a/serverRoot/RUN_build.bat b/serverRoot/RUN_build.bat new file mode 100644 index 0000000000..444db2175d --- /dev/null +++ b/serverRoot/RUN_build.bat @@ -0,0 +1,19 @@ +@Title Build Adempiere Root +@Rem $Header: /cvsroot/adempiere/serverRoot/RUN_build.bat,v 1.11 2005/09/16 00:50:14 jjanke Exp $ + +@CALL ..\utils_dev\myDevEnv.bat +@IF NOT %ADEMPIERE_ENV%==Y GOTO NOBUILD + +@echo Cleanup ... +@"%JAVA_HOME%\bin\java" -Dant.home="." %ANT_PROPERTIES% org.apache.tools.ant.Main clean + +@echo Building ... +@"%JAVA_HOME%\bin\java" -Dant.home="." %ANT_PROPERTIES% org.apache.tools.ant.Main main + +@Echo Done ... +@sleep 60 +@exit + +:NOBUILD +@Echo Check myDevEnv.bat (copy from myDevEnvTemplate.bat) +@Pause diff --git a/serverRoot/RUN_build.sh b/serverRoot/RUN_build.sh new file mode 100644 index 0000000000..5f80633c60 --- /dev/null +++ b/serverRoot/RUN_build.sh @@ -0,0 +1,18 @@ +# Module compiling script +# Ported from Windows script Marek Mosiewicz + + +SAVED_DIR=`pwd` #save current dir +cd `dirname $0`/../utils_dev #change dir to place where script resides - doesn not work with sym links +UTILS_DEV=`pwd` #this is adempiere source +cd $SAVED_DIR #back to the saved directory + +. $UTILS_DEV/myDevEnv.sh #call environment +echo done +if [ ! $ADEMPIERE_ENV==Y ] ; then + echo "Can't set developemeent environemnt - check myDevEnv.sh" + exit 1 +fi + +echo running Ant +$JAVA_HOME/bin/java -Dant.home="." $ANT_PROPERTIES org.apache.tools.ant.Main diff --git a/serverRoot/ServerRoot.html b/serverRoot/ServerRoot.html new file mode 100644 index 0000000000..7cc45612f2 --- /dev/null +++ b/serverRoot/ServerRoot.html @@ -0,0 +1,33 @@ + + + +JBuilder Project ServerRoot.jpx + + +

Project ServerRoot Notes

+
+ + + + +
Title: + + +
Author: + + +
Company: + + +
Description: + + +

+

Things to do...

+
    + +
  1. First +
  2. Second +
+ + diff --git a/serverRoot/build.xml b/serverRoot/build.xml new file mode 100644 index 0000000000..bfc474cd19 --- /dev/null +++ b/serverRoot/build.xml @@ -0,0 +1,235 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/serverRoot/documentation.bat b/serverRoot/documentation.bat new file mode 100644 index 0000000000..182c3fd299 --- /dev/null +++ b/serverRoot/documentation.bat @@ -0,0 +1,5 @@ +@Rem API Documentation for Server + +call ..\doc\documentation.bat "src\main\client;src\main\ejb;src\main\server;src\main\servlet" doc -private + +@pause diff --git a/serverRoot/packages.txt b/serverRoot/packages.txt new file mode 100644 index 0000000000..88cb732d7d --- /dev/null +++ b/serverRoot/packages.txt @@ -0,0 +1,4 @@ +org.compiere.client +org.compiere.session +org.compiere.server +org.compiere.web diff --git a/serverRoot/packaging-build.xml b/serverRoot/packaging-build.xml new file mode 100644 index 0000000000..971b488314 --- /dev/null +++ b/serverRoot/packaging-build.xml @@ -0,0 +1,4 @@ + + + + diff --git a/serverRoot/src/ear/application.xml b/serverRoot/src/ear/application.xml new file mode 100644 index 0000000000..21074ce959 --- /dev/null +++ b/serverRoot/src/ear/application.xml @@ -0,0 +1,24 @@ + + + + adempiereRoot + Adempiere Root + + AdempiereSLib.jar + + + Adempiere.jar + + + adempiereRoot.jar + + + + adempiereRoot.war + /admin + + + diff --git a/serverRoot/src/main/ejb/org/compiere/session/ServerBean.java b/serverRoot/src/main/ejb/org/compiere/session/ServerBean.java new file mode 100644 index 0000000000..d97c6ed171 --- /dev/null +++ b/serverRoot/src/main/ejb/org/compiere/session/ServerBean.java @@ -0,0 +1,669 @@ +/****************************************************************************** + * 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.session; + +import java.io.*; +import java.sql.*; +import java.util.*; +import java.util.logging.*; +import javax.ejb.*; +import javax.sql.*; + +import org.compiere.*; +import org.compiere.acct.*; +import org.compiere.model.*; +import org.compiere.process.*; +import org.compiere.util.*; +import org.compiere.wf.*; + +/** + * Adempiere Server Bean. + * + * @ejb.bean name="adempiere/Server" + * display-name="Adempiere Server Session Bean" + * type="Stateless" + * view-type="both" + * transaction-type="Bean" + * jndi-name="adempiere/Server" + * local-jndi-name="adempiere/ServerLocal" + * + * @ejb.ejb-ref ejb-name="adempiere/Server" + * view-type="both" + * ref-name="adempiere/Server" + * @ejb.ejb-ref ejb-name="adempiere/Server" + * view-type="local" + * ref-name="adempiere/ServerLocal" + * + * @author Jorg Janke + * @version $Id: ServerBean.java,v 1.3 2006/07/30 00:53:33 jjanke Exp $ + */ +public class ServerBean implements SessionBean +{ + /** Context */ + private SessionContext m_Context; + /** Logger */ + private static CLogger log = CLogger.getCLogger(ServerBean.class); + // + private static int s_no = 0; + private int m_no = 0; + // + private int m_windowCount = 0; + private int m_postCount = 0; + private int m_processCount = 0; + private int m_workflowCount = 0; + private int m_paymentCount = 0; + private int m_nextSeqCount = 0; + private int m_stmt_rowSetCount = 0; + private int m_stmt_updateCount = 0; + private int m_cacheResetCount = 0; + private int m_updateLOBCount = 0; + + /** + * Get and create Window Model Value Object + * @ejb.interface-method view-type="both" + * + * @param ctx Environment Properties + * @param WindowNo number of this window + * @param AD_Window_ID the internal number of the window, if not 0, AD_Menu_ID is ignored + * @param AD_Menu_ID ine internal menu number, used when AD_Window_ID is 0 + * @return initialized Window Model + */ + public GridWindowVO getWindowVO (Properties ctx, int WindowNo, int AD_Window_ID, int AD_Menu_ID) + { + log.info ("getWindowVO[" + m_no + "] Window=" + AD_Window_ID); + // log.fine(ctx); + GridWindowVO vo = GridWindowVO.create(ctx, WindowNo, AD_Window_ID, AD_Menu_ID); + m_windowCount++; + return vo; + } // getWindowVO + + + /** + * Post Immediate + * @ejb.interface-method view-type="both" + * + * @param ctx Client Context + * @param AD_Client_ID Client ID of Document + * @param AD_Table_ID Table ID of Document + * @param Record_ID Record ID of this document + * @param force force posting + * @param trxName transaction + * @return null, if success or error message + */ + public String postImmediate (Properties ctx, + int AD_Client_ID, int AD_Table_ID, int Record_ID, boolean force, String trxName) + { + log.info ("[" + m_no + "] Table=" + AD_Table_ID + ", Record=" + Record_ID); + m_postCount++; + MAcctSchema[] ass = MAcctSchema.getClientAcctSchema(ctx, AD_Client_ID); + return Doc.postImmediate(ass, AD_Table_ID, Record_ID, force, trxName); + } // postImmediate + + /************************************************************************* + * Get Prepared Statement ResultSet + * @ejb.interface-method view-type="both" + * + * @param info Result info + * @return RowSet + * @throws NotSerializableException + */ + public RowSet pstmt_getRowSet (CStatementVO info) + throws NotSerializableException + { + log.finer("[" + m_no + "]"); + m_stmt_rowSetCount++; + CPreparedStatement pstmt = new CPreparedStatement(info); + return pstmt.remote_getRowSet(); + } // pstmt_getRowSet + + /** + * Get Statement ResultSet + * @ejb.interface-method view-type="both" + * + * @param info Result info + * @return RowSet + */ + public RowSet stmt_getRowSet (CStatementVO info) + { + log.finer("[" + m_no + "]"); + m_stmt_rowSetCount++; + CStatement stmt = new CStatement(info); + return stmt.remote_getRowSet(); + } // stmt_getRowSet + + /** + * Execute Update + * @ejb.interface-method view-type="both" + * + * @param info Result info + * @return row count + */ + public int stmt_executeUpdate (CStatementVO info) + { + log.finer("[" + m_no + "]"); + m_stmt_updateCount++; + if (info.getParameterCount() == 0) + { + CStatement stmt = new CStatement(info); + return stmt.remote_executeUpdate(); + } + CPreparedStatement pstmt = new CPreparedStatement(info); + return pstmt.remote_executeUpdate(); + } // stmt_executeUpdate + + /************************************************************************* + * Get next number for Key column = 0 is Error. + * @ejb.interface-method view-type="both" + * + * @param AD_Client_ID client + * @param TableName table name + * @param trxName optional Transaction Name + * @return next no + */ + public int getNextID (int AD_Client_ID, String TableName, String trxName) + { + int retValue = MSequence.getNextID (AD_Client_ID, TableName, trxName); + log.finer("[" + m_no + "] " + TableName + " = " + retValue); + m_nextSeqCount++; + return retValue; + } // getNextID + + /** + * Get Document No from table + * @ejb.interface-method view-type="both" + * + * @param AD_Client_ID client + * @param TableName table name + * @param trxName optional Transaction Name + * @return document no or null + */ + public String getDocumentNo (int AD_Client_ID, String TableName, String trxName) + { + m_nextSeqCount++; + String dn = MSequence.getDocumentNo (AD_Client_ID, TableName, trxName); + if (dn == null) // try again + dn = MSequence.getDocumentNo (AD_Client_ID, TableName, trxName); + return dn; + } // GetDocumentNo + + /** + * Get Document No based on Document Type + * @ejb.interface-method view-type="both" + * + * @param C_DocType_ID document type + * @param trxName optional Transaction Name + * @return document no or null + */ + public String getDocumentNo (int C_DocType_ID, String trxName) + { + m_nextSeqCount++; + String dn = MSequence.getDocumentNo (C_DocType_ID, trxName); + if (dn == null) // try again + dn = MSequence.getDocumentNo (C_DocType_ID, trxName); + return dn; + } // getDocumentNo + + + /************************************************************************* + * Process Remote + * @ejb.interface-method view-type="both" + * + * @param ctx Context + * @param pi Process Info + * @return resulting Process Info + */ + public ProcessInfo process (Properties ctx, ProcessInfo pi) + { + String className = pi.getClassName(); + log.info(className + " - " + pi); + m_processCount++; + // Get Class + Class clazz = null; + try + { + clazz = Class.forName (className); + } + catch (ClassNotFoundException ex) + { + log.log(Level.WARNING, className, ex); + pi.setSummary ("ClassNotFound", true); + return pi; + } + // Get Process + SvrProcess process = null; + try + { + process = (SvrProcess)clazz.newInstance (); + } + catch (Exception ex) + { + log.log(Level.WARNING, "Instance for " + className, ex); + pi.setSummary ("InstanceError", true); + return pi; + } + // Start Process + Trx trx = Trx.get(Trx.createTrxName("ServerPrc"), true); + try + { + boolean ok = process.startProcess (ctx, pi, trx); + pi = process.getProcessInfo(); + trx.commit(); + trx.close(); + } + catch (Exception ex1) + { + trx.rollback(); + trx.close(); + pi.setSummary ("ProcessError", true); + return pi; + } + return pi; + } // process + + + /************************************************************************* + * Run Workflow (and wait) on Server + * @ejb.interface-method view-type="both" + * + * @param ctx Context + * @param pi Process Info + * @param AD_Workflow_ID id + * @return process info + */ + public ProcessInfo workflow (Properties ctx, ProcessInfo pi, int AD_Workflow_ID) + { + log.info ("[" + m_no + "] " + AD_Workflow_ID); + m_workflowCount++; + MWorkflow wf = MWorkflow.get (ctx, AD_Workflow_ID); + MWFProcess wfProcess = null; + if (pi.isBatch()) + wfProcess = wf.start(pi); // may return null + else + wfProcess = wf.startWait(pi); // may return null + log.fine(pi.toString()); + return pi; + } // workflow + + /** + * Online Payment from Server + * @ejb.interface-method view-type="both" + * Called from MPayment processOnline + * @param ctx Context + * @param C_Payment_ID payment + * @param C_PaymentProcessor_ID processor + * @param trxName transaction + * @return true if approved + */ + public boolean paymentOnline (Properties ctx, + int C_Payment_ID, int C_PaymentProcessor_ID, String trxName) + { + MPayment payment = new MPayment (ctx, C_Payment_ID, trxName); + MPaymentProcessor mpp = new MPaymentProcessor (ctx, C_PaymentProcessor_ID, null); + log.info ("[" + m_no + "] " + payment + " - " + mpp); + m_paymentCount++; + boolean approved = false; + try + { + PaymentProcessor pp = PaymentProcessor.create(mpp, payment); + if (pp == null) + payment.setErrorMessage("No Payment Processor"); + else + { + approved = pp.processCC (); + if (approved) + payment.setErrorMessage(null); + else + payment.setErrorMessage("From " + payment.getCreditCardName() + + ": " + payment.getR_RespMsg()); + } + } + catch (Exception e) + { + log.log(Level.SEVERE, "", e); + payment.setErrorMessage("Payment Processor Error"); + } + payment.save(); + return approved; + } // paymentOnline + + /** + * Create EMail from Server (Request User) + * @ejb.interface-method view-type="both" + * @param ctx Context + * @param AD_Client_ID client + * @param to recipient email address + * @param subject subject + * @param message message + * @return EMail + */ + public EMail createEMail (Properties ctx, int AD_Client_ID, + String to, String subject, String message) + { + MClient client = MClient.get(ctx, AD_Client_ID); + return client.createEMail(to, subject, message); + } // createEMail + + /** + * Create EMail from Server (Request User) + * @ejb.interface-method view-type="both" + * @param ctx Context + * @param AD_Client_ID client + * @param AD_User_ID user to send email from + * @param to recipient email address + * @param subject subject + * @param message message + * @return EMail + */ + public EMail createEMail (Properties ctx, int AD_Client_ID, + int AD_User_ID, + String to, String subject, String message) + { + MClient client = MClient.get(ctx, AD_Client_ID); + MUser from = new MUser (ctx, AD_User_ID, null); + return client.createEMail(from, to, subject, message); + } // createEMail + + + /** + * Create EMail from Server (Request User) + * @ejb.interface-method view-type="both" + * @param AD_Task_ID task + * @return execution trace + */ + public String executeTask (int AD_Task_ID) + { + MTask task = new MTask (Env.getCtx(), AD_Task_ID, null); // Server Context + return task.execute(); + } // executeTask + + + /** + * Cash Reset + * @ejb.interface-method view-type="both" + * + * @param tableName table name + * @param Record_ID record or 0 for all + * @return number of records reset + */ + public int cacheReset (String tableName, int Record_ID) + { + log.config(tableName + " - " + Record_ID); + m_cacheResetCount++; + return CacheMgt.get().reset(tableName, Record_ID); + } // cacheReset + + /** + * LOB update + * @ejb.interface-method view-type="both" + * + * @param sql table name + * @param displayType display type (i.e. BLOB/CLOB) + * @param value the data + * @return true if updated + */ + public boolean updateLOB (String sql, int displayType, Object value) + { + if (sql == null || value == null) + { + log.fine("No sql or data"); + return false; + } + log.fine(sql); + m_updateLOBCount++; + boolean success = true; + Connection con = DB.createConnection(false, Connection.TRANSACTION_READ_COMMITTED); + PreparedStatement pstmt = null; + try + { + pstmt = con.prepareStatement(sql); + if (displayType == DisplayType.TextLong) + pstmt.setString(1, (String)value); + else + pstmt.setBytes(1, (byte[])value); + int no = pstmt.executeUpdate(); + // + pstmt.close(); + pstmt = null; + } + catch (Exception e) + { + log.log(Level.FINE, sql, e); + success = false; + } + // Close Statement + try + { + if (pstmt != null) + pstmt.close(); + pstmt = null; + } + catch (Exception e) + { + pstmt = null; + } + + // Success - commit local trx + if (success) + { + try + { + con.commit(); + con.close(); + con = null; + } + catch (Exception e) + { + log.log(Level.SEVERE, "commit" , e); + success = false; + } + } + // Error - roll back + if (!success) + { + log.severe ("rollback"); + try + { + con.rollback(); + con.close(); + con = null; + } + catch (Exception ee) + { + log.log(Level.SEVERE, "rollback" , ee); + } + } + + // Clean Connection + try + { + if (con != null) + con.close(); + con = null; + } + catch (Exception e) + { + con = null; + } + return success; + } // updateLOB + + + /************************************************************************** + * Describes the instance and its content for debugging purpose + * @ejb.interface-method view-type="both" + * @return Debugging information about the instance and its content + */ + public String getStatus() + { + StringBuffer sb = new StringBuffer("ServerBean["); + sb.append(m_no) + .append("-Window=").append(m_windowCount) + .append(",Post=").append(m_postCount) + .append(",Process=").append(m_processCount) + .append(",NextSeq=").append(m_nextSeqCount) + .append(",Workflow=").append(m_workflowCount) + .append(",Payment=").append(m_paymentCount) + .append(",RowSet=").append(m_stmt_rowSetCount) + .append(",Update=").append(m_stmt_updateCount) + .append(",CacheReset=").append(m_cacheResetCount) + .append(",UpdateLob=").append(m_updateLOBCount) + .append("]"); + return sb.toString(); + } // getStatus + + + /** + * String Representation + * @return info + */ + public String toString() + { + return getStatus(); + } // toString + + + /************************************************************************** + * Create the Session Bean + * @throws EJBException + * @throws CreateException + * @ejb.create-method view-type="both" + */ + public void ejbCreate() throws EJBException, CreateException + { + m_no = ++s_no; + try + { + Adempiere.startup(false); + } + catch (Exception ex) + { + log.log(Level.SEVERE, "ejbCreate", ex); + // throw new CreateException (); + } + log.info ("#" + getStatus()); + } // ejbCreate + + + // ------------------------------------------------------------------------- + // Framework Callbacks + // ------------------------------------------------------------------------- + + /** + * Method setSessionContext + * @param aContext SessionContext + * @throws EJBException + * @see javax.ejb.SessionBean#setSessionContext(SessionContext) + */ + public void setSessionContext (SessionContext aContext) throws EJBException + { + m_Context = aContext; + } // setSessionContext + + /** + * Method ejbActivate + * @throws EJBException + * @see javax.ejb.SessionBean#ejbActivate() + */ + public void ejbActivate() throws EJBException + { + if (log == null) + log = CLogger.getCLogger(getClass()); + log.info ("ejbActivate " + getStatus()); + } // ejbActivate + + /** + * Method ejbPassivate + * @throws EJBException + * @see javax.ejb.SessionBean#ejbPassivate() + */ + public void ejbPassivate() throws EJBException + { + log.info ("ejbPassivate " + getStatus()); + } // ejbPassivate + + /** + * Method ejbRemove + * @throws EJBException + * @see javax.ejb.SessionBean#ejbRemove() + */ + public void ejbRemove() throws EJBException + { + log.info ("ejbRemove " + getStatus()); + } // ejbRemove + + + /************************************************************************** + * Dump SerialVersionUID of class + * @param clazz class + */ + protected static void dumpSVUID (Class clazz) + { + String s = clazz.getName() + + " ==\nstatic final long serialVersionUID = " + + java.io.ObjectStreamClass.lookup(clazz).getSerialVersionUID() + + "L;\n"; + System.out.println (s); + } // dumpSVUID + + /** + * Print UID of used classes. + * R2.5.1h + +org.compiere.process.ProcessInfo == +static final long serialVersionUID = -1993220053515488725L; + +org.compiere.util.CStatementVO == +static final long serialVersionUID = -3393389471515956399L; + +org.compiere.model.MQuery == +static final long serialVersionUID = 1511402030597166113L; + +org.compiere.model.POInfo == +static final long serialVersionUID = -5976719579744948419L; + +org.compiere.model.POInfoColumn == +static final long serialVersionUID = -3983585608504631958L; + +org.compiere.model.MWindowVO == +static final long serialVersionUID = 3802628212531678981L; + +org.compiere.model.MTabVO == +static final long serialVersionUID = 9160212869277319305L; + +org.compiere.model.MFieldVO == +static final long serialVersionUID = 4385061125114436797L; + +org.compiere.model.MLookupInfo == +static final long serialVersionUID = -7958664359250070233L; + + * + * * @param args ignored + */ + public static void main (String[] args) + { + dumpSVUID(ProcessInfo.class); + dumpSVUID(CStatementVO.class); + dumpSVUID(MQuery.class); + dumpSVUID(POInfo.class); + dumpSVUID(POInfoColumn.class); + dumpSVUID(GridWindowVO.class); + dumpSVUID(GridTabVO.class); + dumpSVUID(GridFieldVO.class); + dumpSVUID(MLookupInfo.class); + } // main + +} // ServerBean diff --git a/serverRoot/src/main/ejb/org/compiere/session/StatusBean.java b/serverRoot/src/main/ejb/org/compiere/session/StatusBean.java new file mode 100644 index 0000000000..8e22e10b51 --- /dev/null +++ b/serverRoot/src/main/ejb/org/compiere/session/StatusBean.java @@ -0,0 +1,288 @@ +/****************************************************************************** + * 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.session; + +import java.util.logging.*; +import javax.ejb.*; + +import org.compiere.*; +import org.compiere.db.*; +import org.compiere.util.*; + + +/** + * Adempiere Status Bean + * + * @ejb.bean name="adempiere/Status" + * display-name="Adempiere Status Session Bean" + * type="Stateless" + * view-type="both" + * transaction-type="Bean" + * jndi-name="adempiere/Status" + * local-jndi-name="adempiere/StatusLocal" + * + * @ejb.ejb-ref ejb-name="adempiere/Status" + * view-type="both" + * ref-name="adempiere/Status" + * @ejb.ejb-ref ejb-name="adempiere/Status" + * view-type="local" + * ref-name="adempiere/StatusLocal" + * + * @author Jorg Janke + * @version $Id: StatusBean.java,v 1.3 2006/07/30 00:53:33 jjanke Exp $ + */ +public class StatusBean implements SessionBean +{ + /** Context */ + private SessionContext m_Context; + /** Logging */ + private static CLogger log = CLogger.getCLogger(StatusBean.class); + + private static int s_no = 0; + private int m_no = 0; + // + private int m_versionCount = 0; + private int m_databaseCount = 0; + + + /** + * Get Version (Date) + * @ejb.interface-method view-type="both" + * @return version e.g. 2002-09-02 + */ + public String getDateVersion() + { + m_versionCount++; + log.info ("getDateVersion " + m_versionCount); + return Adempiere.DATE_VERSION; + } // getDateVersion + + /** + * Get Main Version + * @ejb.interface-method view-type="both" + * @return main version - e.g. Version 2.4.3b + */ + public String getMainVersion() + { + return Adempiere.MAIN_VERSION; + } // getMainVersion + + /** + * Get Database Type + * @ejb.interface-method view-type="both" + * @return Database Type + */ + public String getDbType() + { + return CConnection.get().getType(); + } // getDbType + + /** + * Get Database Host + * @ejb.interface-method view-type="both" + * @return Database Host Name + */ + public String getDbHost() + { + m_databaseCount++; + log.info ("getDbHost " + m_databaseCount); + return CConnection.get().getDbHost(); + } // getDbHost + + /** + * Get Database Port + * @ejb.interface-method view-type="both" + * @return Database Port + */ + public int getDbPort() + { + return CConnection.get().getDbPort(); + } // getDbPort + + /** + * Get Database SID + * @ejb.interface-method view-type="both" + * @return Database SID + */ + public String getDbName() + { + return CConnection.get().getDbName(); + } // getDbSID + + /** + * Get Database URL + * @ejb.interface-method view-type="both" + * @return Database URL + */ + public String getConnectionURL() + { + return CConnection.get().getConnectionURL(); + } // getConnectionURL + + /** + * Get Database UID + * @ejb.interface-method view-type="both" + * @return Database User Name + */ + public String getDbUid() + { + return CConnection.get().getDbUid(); + } // getDbUID + + /** + * Get Database PWD + * @ejb.interface-method view-type="both" + * @return Database User Password + */ + public String getDbPwd() + { + return CConnection.get().getDbPwd(); + } // getDbPWD + + /** + * Get Connection Manager Host + * @ejb.interface-method view-type="both" + * @return Connection Manager Host + */ + public String getFwHost() + { + return CConnection.get().getFwHost(); + } // getCMHost + + /** + * Get Connection Manager Port + * @ejb.interface-method view-type="both" + * @return Connection Manager Port + */ + public int getFwPort() + { + return CConnection.get().getFwPort(); + } // getCMPort + + + /************************************************************************** + * Get Version Count + * @ejb.interface-method view-type="both" + * @return number of version inquiries + */ + public int getVersionCount() + { + return m_versionCount; + } // getVersionCount + + /** + * Get Database Count + * @ejb.interface-method view-type="both" + * @return number of database inquiries + */ + public int getDatabaseCount() + { + return m_databaseCount; + } // getVersionCount + + /** + * Describes the instance and its content for debugging purpose + * @ejb.interface-method view-type="both" + * @return Debugging information about the instance and its content + */ + public String getStatus() + { + StringBuffer sb = new StringBuffer("StatusBean[No="); + sb.append(m_no) + .append(",VersionCount=").append(m_versionCount) + .append(",DatabaseCount=").append(m_versionCount) + .append("]"); + return sb.toString(); + } // getStatus + + + /** + * String Representation + * @return info + */ + public String toString() + { + return getStatus(); + } // toString + + + /************************************************************************** + * Create the Session Bean + * @throws EJBException + * @throws CreateException + * @ejb.create-method view-type="both" + */ + public void ejbCreate() throws EJBException, CreateException + { + m_no = ++s_no; + try + { + org.compiere.Adempiere.startup(false); + } + catch (Exception ex) + { + log.log(Level.SEVERE, "", ex); + // throw new CreateException (); + } + log.info("#" + m_no + " - " + getStatus()); + } // ejbCreate + + + // ------------------------------------------------------------------------- + // Framework Callbacks + // ------------------------------------------------------------------------- + + /** + * Set Session Context + * @param aContext context + * @throws EJBException + */ + public void setSessionContext (SessionContext aContext) throws EJBException + { + m_Context = aContext; + } + + /** + * Ejb Activate + * @throws EJBException + */ + public void ejbActivate() throws EJBException + { + if (log == null) + log = CLogger.getCLogger(getClass()); + log.fine("ejbActivate"); + } + + /** + * Ejb Passivate + * @throws EJBException + */ + public void ejbPassivate() throws EJBException + { + log.fine("ejbPassivate"); + } + + /** + * Ejb Remove + * @throws EJBException + */ + public void ejbRemove() throws EJBException + { + log.fine("ejbRemove"); + } + +} // StatusBean diff --git a/serverRoot/src/main/server/org/compiere/acct/Doc.java b/serverRoot/src/main/server/org/compiere/acct/Doc.java new file mode 100644 index 0000000000..569b5317f1 --- /dev/null +++ b/serverRoot/src/main/server/org/compiere/acct/Doc.java @@ -0,0 +1,2162 @@ +/****************************************************************************** + * 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.acct; + +import java.lang.reflect.*; +import java.math.*; +import java.sql.*; +import java.util.*; +import java.util.logging.*; + +import org.compiere.model.*; +import org.compiere.process.*; +import org.compiere.report.*; +import org.compiere.util.*; + +/** + * Posting Document Root. + * + *
+ *  Table               Base Document Types (C_DocType.DocBaseType & AD_Reference_ID=183)
+ *      Class           AD_Table_ID
+ *  ------------------  ------------------------------
+ *  C_Invoice:          ARI, ARC, ARF, API, APC
+ *      Doc_Invoice     318 - has C_DocType_ID
+ *
+ *  C_Payment:          ARP, APP
+ *      Doc_Payment     335 - has C_DocType_ID
+ *
+ *  C_Order:            SOO, POO,  POR (Requisition)
+ *      Doc_Order       259 - has C_DocType_ID
+ *
+ *  M_InOut:            MMS, MMR
+ *      Doc_InOut       319 - DocType derived
+ *
+ *  M_Inventory:        MMI
+ *      Doc_Inventory   321 - DocType fixed
+ *
+ *  M_Movement:         MMM
+ *      Doc_Movement    323 - DocType fixed
+ *
+ *  M_Production:       MMP
+ *      Doc_Production  325 - DocType fixed
+ *
+ *  C_BankStatement:    CMB
+ *      Doc_Bank        392 - DocType fixed
+ *
+ *  C_Cash:             CMC
+ *      Doc_Cash        407 - DocType fixed
+ *
+ *  C_Allocation:       CMA
+ *      Doc_Allocation  390 - DocType fixed
+ *
+ *  GL_Journal:         GLJ
+ *      Doc_GLJournal   224 = has C_DocType_ID
+ *
+ *  Matching Invoice    MXI
+ *      M_MatchInv      472 - DocType fixed
+ *
+ *  Matching PO         MXP
+ *      M_MatchPO       473 - DocType fixed
+ *
+ *	Project Issue		PJI
+ *		C_ProjectIssue	623 - DocType fixed
+ *	
+ *  
+ * @author Jorg Janke + * @version $Id: Doc.java,v 1.6 2006/07/30 00:53:33 jjanke Exp $ + */ +public abstract class Doc +{ + /** AD_Table_ID's of documents */ + public static int[] documentsTableID = new int[] { + MInvoice.Table_ID, // C_Invoice + MAllocationHdr.Table_ID, // C_Allocation + MCash.Table_ID, // C_Cash + MBankStatement.Table_ID, // C_BankStatement + MOrder.Table_ID, // C_Order + MPayment.Table_ID, // C_Payment + MInOut.Table_ID, // M_InOut + MInventory.Table_ID, // M_Inventory + MMovement.Table_ID, // M_Movement + X_M_Production.Table_ID, // M_Production + MJournal.Table_ID, // GL_Journal + MMatchInv.Table_ID, // M_MatchInv + MMatchPO.Table_ID, // M_MatchPO + MProjectIssue.Table_ID, // C_ProjectIssue + MRequisition.Table_ID // M_Requisition + }; + + /** Table Names of documents */ + public static String[] documentsTableName = new String[] { + MInvoice.Table_Name, // C_Invoice + MAllocationHdr.Table_Name, // C_Allocation + MCash.Table_Name, // C_Cash + MBankStatement.Table_Name, // C_BankStatement + MOrder.Table_Name, // C_Order + MPayment.Table_Name, // C_Payment + MInOut.Table_Name, // M_InOut + MInventory.Table_Name, // M_Inventory + MMovement.Table_Name, // M_Movement + X_M_Production.Table_Name, // M_Production + MJournal.Table_Name, // GL_Journal + MMatchInv.Table_Name, // M_MatchInv + MMatchPO.Table_Name, // M_MatchPO + MProjectIssue.Table_Name, // C_ProjectIssue + MRequisition.Table_Name // M_Requisition + }; + + /************************************************************************** + * Document Types + * -------------- + * C_DocType.DocBaseType & AD_Reference_ID=183 + * C_Invoice: ARI, ARC, ARF, API, APC + * C_Payment: ARP, APP + * C_Order: SOO, POO + * M_Transaction: MMI, MMM, MMS, MMR + * C_BankStatement: CMB + * C_Cash: CMC + * C_Allocation: CMA + * GL_Journal: GLJ + * C_ProjectIssue PJI + * M_Requisition POR + **************************************************************************/ + + /** AR Invoices - ARI */ + public static final String DOCTYPE_ARInvoice = MDocType.DOCBASETYPE_ARInvoice; + /** AR Credit Memo */ + public static final String DOCTYPE_ARCredit = "ARC"; + /** AR Receipt */ + public static final String DOCTYPE_ARReceipt = "ARR"; + /** AR ProForma */ + public static final String DOCTYPE_ARProForma = "ARF"; + /** AP Invoices */ + public static final String DOCTYPE_APInvoice = "API"; + /** AP Credit Memo */ + public static final String DOCTYPE_APCredit = "APC"; + /** AP Payment */ + public static final String DOCTYPE_APPayment = "APP"; + /** CashManagement Bank Statement */ + public static final String DOCTYPE_BankStatement = "CMB"; + /** CashManagement Cash Journals */ + public static final String DOCTYPE_CashJournal = "CMC"; + /** CashManagement Allocations */ + public static final String DOCTYPE_Allocation = "CMA"; + /** Material Shipment */ + public static final String DOCTYPE_MatShipment = "MMS"; + /** Material Receipt */ + public static final String DOCTYPE_MatReceipt = "MMR"; + /** Material Inventory */ + public static final String DOCTYPE_MatInventory = "MMI"; + /** Material Movement */ + public static final String DOCTYPE_MatMovement = "MMM"; + /** Material Production */ + public static final String DOCTYPE_MatProduction = "MMP"; + /** Match Invoice */ + public static final String DOCTYPE_MatMatchInv = "MXI"; + /** Match PO */ + public static final String DOCTYPE_MatMatchPO = "MXP"; + /** GL Journal */ + public static final String DOCTYPE_GLJournal = "GLJ"; + /** Purchase Order */ + public static final String DOCTYPE_POrder = "POO"; + /** Sales Order */ + public static final String DOCTYPE_SOrder = "SOO"; + /** Project Issue */ + public static final String DOCTYPE_ProjectIssue = "PJI"; + /** Purchase Requisition */ + public static final String DOCTYPE_PurchaseRequisition = "POR"; + + + + // Posting Status - AD_Reference_ID=234 // + /** Document Status */ + public static final String STATUS_NotPosted = "N"; + /** Document Status */ + public static final String STATUS_NotBalanced = "b"; + /** Document Status */ + public static final String STATUS_NotConvertible = "c"; + /** Document Status */ + public static final String STATUS_PeriodClosed = "p"; + /** Document Status */ + public static final String STATUS_InvalidAccount = "i"; + /** Document Status */ + public static final String STATUS_PostPrepared = "y"; + /** Document Status */ + public static final String STATUS_Posted = "Y"; + /** Document Status */ + public static final String STATUS_Error = "E"; + + + /** + * Create Posting document + * @param ass accounting schema + * @param AD_Table_ID Table ID of Documents + * @param Record_ID record ID to load + * @param trxName transaction name + * @return Document or null + */ + public static Doc get (MAcctSchema[] ass, int AD_Table_ID, int Record_ID, String trxName) + { + String TableName = null; + for (int i = 0; i < documentsTableID.length; i++) + { + if (documentsTableID[i] == AD_Table_ID) + { + TableName = documentsTableName[i]; + break; + } + } + if (TableName == null) + { + s_log.severe("Not found AD_Table_ID=" + AD_Table_ID); + return null; + } + // + Doc doc = null; + StringBuffer sql = new StringBuffer("SELECT * FROM ") + .append(TableName) + .append(" WHERE ").append(TableName).append("_ID=? AND Processed='Y'"); + PreparedStatement pstmt = null; + try + { + pstmt = DB.prepareStatement (sql.toString(), trxName); + pstmt.setInt (1, Record_ID); + ResultSet rs = pstmt.executeQuery (); + if (rs.next ()) + { + doc = get (ass, AD_Table_ID, rs, trxName); + } + else + s_log.severe("Not Found: " + TableName + "_ID=" + Record_ID); + rs.close (); + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + s_log.log (Level.SEVERE, sql.toString(), e); + } + try + { + if (pstmt != null) + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + pstmt = null; + } + return doc; + } // get + + /** + * Create Posting document + * @param ass accounting schema + * @param AD_Table_ID Table ID of Documents + * @param rs ResultSet + * @param trxName transaction name + * @return Document + */ + public static Doc get (MAcctSchema[] ass, int AD_Table_ID, ResultSet rs, String trxName) + { + Doc doc = null; + switch (AD_Table_ID) + { + case MInvoice.Table_ID: + doc = new Doc_Invoice (ass, rs, trxName); + break; + case MAllocationHdr.Table_ID: + doc = new Doc_Allocation (ass, rs, trxName); + break; + case MCash.Table_ID: + doc = new Doc_Cash (ass, rs, trxName); + break; + case MBankStatement.Table_ID: + doc = new Doc_Bank (ass, rs, trxName); + break; + case MOrder.Table_ID: + doc = new Doc_Order (ass, rs, trxName); + break; + case MPayment.Table_ID: + doc = new Doc_Payment (ass, rs, trxName); + break; + case MInOut.Table_ID: + doc = new Doc_InOut (ass, rs, trxName); + break; + case MInventory.Table_ID: + doc = new Doc_Inventory (ass, rs, trxName); + break; + case MMovement.Table_ID: + doc = new Doc_Movement (ass, rs, trxName); + break; + case X_M_Production.Table_ID: + doc = new Doc_Production (ass, rs, trxName); + break; + case MJournal.Table_ID: + doc = new Doc_GLJournal (ass, rs, trxName); + break; + case MMatchInv.Table_ID: + doc = new Doc_MatchInv (ass, rs, trxName); + break; + case MMatchPO.Table_ID: + doc = new Doc_MatchPO (ass, rs, trxName); + break; + case MProjectIssue.Table_ID: + doc = new Doc_ProjectIssue (ass, rs, trxName); + break; + case MRequisition.Table_ID: + doc = new Doc_Requisition (ass, rs, trxName); + break; + } + if (doc == null) + s_log.log(Level.SEVERE, "Unknown AD_Table_ID=" + AD_Table_ID); + return doc; + } // get + + /** + * Post Document + * @param ass accounting schemata + * @param AD_Table_ID Transaction table + * @param Record_ID Record ID of this document + * @param force force posting + * @param trxName transaction + * @return null if the document was posted or error message + */ + public static String postImmediate (MAcctSchema[] ass, + int AD_Table_ID, int Record_ID, boolean force, String trxName) + { + Doc doc = get (ass, AD_Table_ID, Record_ID, trxName); + if (doc != null) + return doc.post (force, true); // repost + return "NoDoc"; + } // post + + /** Static Log */ + protected static CLogger s_log = CLogger.getCLogger(Doc.class); + /** Log per Document */ + protected CLogger log = CLogger.getCLogger(getClass()); + + + /************************************************************************** + * Constructor + * @param ass accounting schemata + * @param clazz Document Class + * @param rs result set + * @param defaultDocumentType default document type or null + * @param trxName trx + */ + Doc (MAcctSchema[] ass, Class clazz, ResultSet rs, String defaultDocumentType, String trxName) + { + p_Status = STATUS_Error; + m_ass = ass; + m_ctx = new Properties(m_ass[0].getCtx()); + m_ctx.setProperty("#AD_Client_ID", String.valueOf(m_ass[0].getAD_Client_ID())); + + String className = clazz.getName(); + className = className.substring(className.lastIndexOf('.')+1); + try + { + Constructor constructor = clazz.getConstructor(new Class[]{Properties.class, ResultSet.class, String.class}); + p_po = (PO)constructor.newInstance(new Object[]{m_ctx, rs, trxName}); + } + catch (Exception e) + { + String msg = className + ": " + e.getLocalizedMessage(); + log.severe(msg); + throw new IllegalArgumentException(msg); + } + + // DocStatus + int index = p_po.get_ColumnIndex("DocStatus"); + if (index != -1) + m_DocStatus = (String)p_po.get_Value(index); + + // Document Type + setDocumentType (defaultDocumentType); + m_trxName = trxName; + if (m_trxName == null) + m_trxName = "Post" + m_DocumentType + p_po.get_ID(); + p_po.set_TrxName(m_trxName); + + // Amounts + m_Amounts[0] = Env.ZERO; + m_Amounts[1] = Env.ZERO; + m_Amounts[2] = Env.ZERO; + m_Amounts[3] = Env.ZERO; + } // Doc + + /** Accounting Schema Array */ + private MAcctSchema[] m_ass = null; + /** Properties */ + private Properties m_ctx = null; + /** Transaction Name */ + private String m_trxName = null; + /** The Document */ + protected PO p_po = null; + /** Document Type */ + private String m_DocumentType = null; + /** Document Status */ + private String m_DocStatus = null; + /** Document No */ + private String m_DocumentNo = null; + /** Description */ + private String m_Description = null; + /** GL Category */ + private int m_GL_Category_ID = 0; + /** GL Period */ + private MPeriod m_period = null; + /** Period ID */ + private int m_C_Period_ID = 0; + /** Location From */ + private int m_C_LocFrom_ID = 0; + /** Location To */ + private int m_C_LocTo_ID = 0; + /** Accounting Date */ + private Timestamp m_DateAcct = null; + /** Document Date */ + private Timestamp m_DateDoc = null; + /** Tax Included */ + private boolean m_TaxIncluded = false; + /** Is (Source) Multi-Currency Document - i.e. the document has different currencies + * (if true, the document will not be source balanced) */ + private boolean m_MultiCurrency = false; + /** BP Sales Region */ + private int m_BP_C_SalesRegion_ID = -1; + /** B Partner */ + private int m_C_BPartner_ID = -1; + + /** Bank Account */ + private int m_C_BankAccount_ID = -1; + /** Cach Book */ + private int m_C_CashBook_ID = -1; + /** Currency */ + private int m_C_Currency_ID = -1; + + /** Contained Doc Lines */ + protected DocLine[] p_lines; + + /** Facts */ + private ArrayList m_fact = null; + + /** No Currency in Document Indicator (-1) */ + protected static final int NO_CURRENCY = -2; + + /** Actual Document Status */ + protected String p_Status = null; + /** Error Message */ + protected String p_Error = null; + + + /** + * Get Context + * @return context + */ + protected Properties getCtx() + { + return m_ctx; + } // getCtx + + /** + * Get Table Name + * @return table name + */ + public String get_TableName() + { + return p_po.get_TableName(); + } // get_TableName + + /** + * Get Table ID + * @return table id + */ + public int get_Table_ID() + { + return p_po.get_Table_ID(); + } // get_Table_ID + + /** + * Get Record_ID + * @return record id + */ + public int get_ID() + { + return p_po.get_ID(); + } // get_ID + + /** + * Get Persistent Object + * @return po + */ + protected PO getPO() + { + return p_po; + } // getPO + + /** + * Post Document. + *
+	 *  - try to lock document (Processed='Y' (AND Processing='N' AND Posted='N'))
+	 * 		- if not ok - return false
+	 *          - postlogic (for all Accounting Schema)
+	 *              - create Fact lines
+	 *          - postCommit
+	 *              - commits Fact lines and Document & sets Processing = 'N'
+	 *              - if error - create Note
+	 *  
+ * @param force if true ignore that locked + * @param repost if true ignore that already posted + * @return null if posted error otherwise + */ + public final String post (boolean force, boolean repost) + { + if (m_DocStatus == null) + ; // return "No DocStatus for DocumentNo=" + getDocumentNo(); + else if (m_DocStatus.equals(DocumentEngine.STATUS_Completed) + || m_DocStatus.equals(DocumentEngine.STATUS_Closed) + || m_DocStatus.equals(DocumentEngine.STATUS_Voided) + || m_DocStatus.equals(DocumentEngine.STATUS_Reversed)) + ; + else + return "Invalid DocStatus='" + m_DocStatus + "' for DocumentNo=" + getDocumentNo(); + // + if (p_po.getAD_Client_ID() != m_ass[0].getAD_Client_ID()) + { + String error = "AD_Client_ID Conflict - Document=" + p_po.getAD_Client_ID() + + ", AcctSchema=" + m_ass[0].getAD_Client_ID(); + log.severe(error); + return error; + } + + // Lock Record ---- + StringBuffer sql = new StringBuffer ("UPDATE "); + sql.append(get_TableName()).append( " SET Processing='Y' WHERE ") + .append(get_TableName()).append("_ID=").append(get_ID()) + .append(" AND Processed='Y' AND IsActive='Y'"); + if (!force) + sql.append(" AND (Processing='N' OR Processing IS NULL)"); + if (!repost) + sql.append(" AND Posted='N'"); + if (DB.executeUpdate(sql.toString(), null) == 1) // outside trx + log.info("Locked: " + get_TableName() + "_ID=" + get_ID()); + else + { + log.log(Level.SEVERE, "Resubmit - Cannot lock " + get_TableName() + "_ID=" + + get_ID() + ", Force=" + force + ",RePost=" + repost); + if (force) + return "Cannot Lock - ReSubmit"; + return "Cannot Lock - ReSubmit or RePost with Force"; + } + + p_Error = loadDocumentDetails(); + if (p_Error != null) + return p_Error; + + // Delete existing Accounting + if (repost) + { + if (isPosted() && !isPeriodOpen()) // already posted - don't delete if period closed + { + log.log(Level.SEVERE, toString() + " - Period Closed for already posed document"); + unlock(); + return "PeriodClosed"; + } + // delete it + deleteAcct(); + } + else if (isPosted()) + { + log.log(Level.SEVERE, toString() + " - Document already posted"); + unlock(); + return "AlreadyPosted"; + } + + p_Status = STATUS_NotPosted; + + // Create Fact per AcctSchema + m_fact = new ArrayList(); + + // for all Accounting Schema + boolean OK = true; + try + { + for (int i = 0; OK && i < m_ass.length; i++) + { + // if acct schema has "only" org, skip + boolean skip = false; + if (m_ass[i].getAD_OrgOnly_ID() != 0) + { + if (m_ass[i].getOnlyOrgs() == null) + m_ass[i].setOnlyOrgs(MReportTree.getChildIDs(getCtx(), + 0, MAcctSchemaElement.ELEMENTTYPE_Organization, + m_ass[i].getAD_OrgOnly_ID())); + + // Header Level Org + skip = m_ass[i].isSkipOrg(getAD_Org_ID()); + // Line Level Org + if (p_lines != null) + { + for (int line = 0; skip && line < p_lines.length; line++) + { + skip = m_ass[i].isSkipOrg(p_lines[line].getAD_Org_ID()); + if (!skip) + break; + } + } + } + if (skip) + continue; + // post + log.info("(" + i + ") " + p_po); + p_Status = postLogic (i); + if (!p_Status.equals(STATUS_Posted)) + OK = false; + } + } + catch (Exception e) + { + log.log(Level.SEVERE, "", e); + p_Status = STATUS_Error; + p_Error = e.toString(); + OK = false; + } + + // commitFact + p_Status = postCommit (p_Status); + + // Create Note + if (!p_Status.equals(STATUS_Posted)) + { + // Insert Note + String AD_MessageValue = "PostingError-" + p_Status; + int AD_User_ID = p_po.getUpdatedBy(); + MNote note = new MNote (getCtx(), AD_MessageValue, AD_User_ID, + getAD_Client_ID(), getAD_Org_ID(), null); + note.setRecord(p_po.get_Table_ID(), p_po.get_ID()); + // Reference + note.setReference(toString()); // Document + // Text + StringBuffer Text = new StringBuffer (Msg.getMsg(Env.getCtx(), AD_MessageValue)); + if (p_Error != null) + Text.append(" (").append(p_Error).append(")"); + String cn = getClass().getName(); + Text.append(" - ").append(cn.substring(cn.lastIndexOf('.'))) + .append(" (").append(getDocumentType()) + .append(" - DocumentNo=").append(getDocumentNo()) + .append(", DateAcct=").append(getDateAcct().toString().substring(0,10)) + .append(", Amount=").append(getAmount()) + .append(", Sta=").append(p_Status) + .append(" - PeriodOpen=").append(isPeriodOpen()) + .append(", Balanced=").append(isBalanced()); + note.setTextMsg(Text.toString()); + note.save(); + } + + // dispose facts + for (int i = 0; i < m_fact.size(); i++) + { + Fact fact = m_fact.get(i); + if (fact != null) + fact.dispose(); + } + p_lines = null; + + if (p_Status.equals(STATUS_Posted)) + return null; + return p_Error; + } // post + + /** + * Delete Accounting + * @return number of records + */ + private int deleteAcct() + { + StringBuffer sql = new StringBuffer ("DELETE Fact_Acct WHERE AD_Table_ID=") + .append(get_Table_ID()) + .append(" AND Record_ID=").append(p_po.get_ID()); + int no = DB.executeUpdate(sql.toString(), getTrxName()); + if (no != 0) + log.info("deleted=" + no); + return no; + } // deleteAcct + + /** + * Posting logic for Accounting Schema index + * @param index Accounting Schema index + * @return posting status/error code + */ + private final String postLogic (int index) + { + log.info("(" + index + ") " + p_po); + + // rejectUnbalanced + if (!m_ass[index].isSuspenseBalancing() && !isBalanced()) + return STATUS_NotBalanced; + + // rejectUnconvertible + if (!isConvertible(m_ass[index])) + return STATUS_NotConvertible; + + // rejectPeriodClosed + if (!isPeriodOpen()) + return STATUS_PeriodClosed; + + // createFacts + ArrayList facts = createFacts (m_ass[index]); + if (facts == null) + return STATUS_Error; + for (int f = 0; f < facts.size(); f++) + { + Fact fact = facts.get(f); + if (fact == null) + return STATUS_Error; + m_fact.add(fact); + // + p_Status = STATUS_PostPrepared; + + // check accounts + if (!fact.checkAccounts()) + return STATUS_InvalidAccount; + + // distribute + if (!fact.distribute()) + return STATUS_Error; + + // balanceSource + if (!fact.isSourceBalanced()) + { + fact.balanceSource(); + if (!fact.isSourceBalanced()) + return STATUS_NotBalanced; + } + + // balanceSegments + if (!fact.isSegmentBalanced()) + { + fact.balanceSegments(); + if (!fact.isSegmentBalanced()) + return STATUS_NotBalanced; + } + + // balanceAccounting + if (!fact.isAcctBalanced()) + { + fact.balanceAccounting(); + if (!fact.isAcctBalanced()) + return STATUS_NotBalanced; + } + + } // for all facts + + return STATUS_Posted; + } // postLogic + + /** + * Post Commit. + * Save Facts & Document + * @param status status + * @return Posting Status + */ + private final String postCommit (String status) + { + log.info("Sta=" + status + " DT=" + getDocumentType() + + " ID=" + p_po.get_ID()); + p_Status = status; + + Trx trx = Trx.get(getTrxName(), true); + try + { + // *** Transaction Start *** + // Commit Facts + if (status.equals(STATUS_Posted)) + { + for (int i = 0; i < m_fact.size(); i++) + { + Fact fact = m_fact.get(i); + if (fact == null) + ; + else if (fact.save(getTrxName())) + ; + else + { + log.log(Level.SEVERE, "(fact not saved) ... rolling back"); + trx.rollback(); + trx.close(); + unlock(); + return STATUS_Error; + } + } + } + // Commit Doc + if (!save(getTrxName())) // contains unlock & document status update + { + log.log(Level.SEVERE, "(doc not saved) ... rolling back"); + trx.rollback(); + trx.close(); + unlock(); + return STATUS_Error; + } + // Success + trx.commit(); + trx.close(); + trx = null; + // *** Transaction End *** + } + catch (Exception e) + { + log.log(Level.SEVERE, "... rolling back", e); + status = STATUS_Error; + try { + if (trx != null) + trx.rollback(); + } catch (Exception e2) {} + try { + if (trx != null) + trx.close(); + trx = null; + } catch (Exception e3) {} + unlock(); + } + p_Status = status; + return status; + } // postCommit + + /** + * Get Trx Name and create Transaction + * @return Trx Name + */ + protected String getTrxName() + { + return m_trxName; + } // getTrxName + + /** + * Unlock Document + */ + private void unlock() + { + StringBuffer sql = new StringBuffer ("UPDATE "); + sql.append(get_TableName()).append( " SET Processing='N' WHERE ") + .append(get_TableName()).append("_ID=").append(p_po.get_ID()); + DB.executeUpdate(sql.toString(), null); // outside trx + } // unlock + + + /************************************************************************** + * Load Document Type and GL Info. + * Set p_DocumentType and p_GL_Category_ID + * @return document type + */ + protected String getDocumentType() + { + if (m_DocumentType == null) + setDocumentType(null); + return m_DocumentType; + } // getDocumentType + + /** + * Load Document Type and GL Info. + * Set p_DocumentType and p_GL_Category_ID + * @param DocumentType + */ + protected void setDocumentType (String DocumentType) + { + if (DocumentType != null) + m_DocumentType = DocumentType; + // No Document Type defined + if (m_DocumentType == null && getC_DocType_ID() != 0) + { + String sql = "SELECT DocBaseType, GL_Category_ID FROM C_DocType WHERE C_DocType_ID=?"; + try + { + PreparedStatement pstmt = DB.prepareStatement(sql, null); + pstmt.setInt(1, getC_DocType_ID()); + ResultSet rsDT = pstmt.executeQuery(); + if (rsDT.next()) + { + m_DocumentType = rsDT.getString(1); + m_GL_Category_ID = rsDT.getInt(2); + } + rsDT.close(); + pstmt.close(); + } + catch (SQLException e) + { + log.log(Level.SEVERE, sql, e); + } + } + if (m_DocumentType == null) + { + log.log(Level.SEVERE, "No DocBaseType for C_DocType_ID=" + + getC_DocType_ID() + ", DocumentNo=" + getDocumentNo()); + } + + // We have a document Type, but no GL info - search for DocType + if (m_GL_Category_ID == 0) + { + String sql = "SELECT GL_Category_ID FROM C_DocType " + + "WHERE AD_Client_ID=? AND DocBaseType=?"; + try + { + PreparedStatement pstmt = DB.prepareStatement(sql, null); + pstmt.setInt(1, getAD_Client_ID()); + pstmt.setString(2, m_DocumentType); + ResultSet rsDT = pstmt.executeQuery(); + if (rsDT.next()) + m_GL_Category_ID = rsDT.getInt(1); + rsDT.close(); + pstmt.close(); + } + catch (SQLException e) + { + log.log(Level.SEVERE, sql, e); + } + } + + // Still no GL_Category - get Default GL Category + if (m_GL_Category_ID == 0) + { + String sql = "SELECT GL_Category_ID FROM GL_Category " + + "WHERE AD_Client_ID=? " + + "ORDER BY IsDefault DESC"; + try + { + PreparedStatement pstmt = DB.prepareStatement(sql, null); + pstmt.setInt(1, getAD_Client_ID()); + ResultSet rsDT = pstmt.executeQuery(); + if (rsDT.next()) + m_GL_Category_ID = rsDT.getInt(1); + rsDT.close(); + pstmt.close(); + } + catch (SQLException e) + { + log.log(Level.SEVERE, sql, e); + } + } + // + if (m_GL_Category_ID == 0) + log.log(Level.SEVERE, "No default GL_Category - " + toString()); + + if (m_DocumentType == null) + throw new IllegalStateException("Document Type not found"); + } // setDocumentType + + + /************************************************************************** + * Is the Source Document Balanced + * @return true if (source) baanced + */ + public boolean isBalanced() + { + // Multi-Currency documents are source balanced by definition + if (isMultiCurrency()) + return true; + // + boolean retValue = getBalance().signum() == 0; + if (retValue) + log.fine("Yes " + toString()); + else + log.warning("NO - " + toString()); + return retValue; + } // isBalanced + + /** + * Is Document convertible to currency and Conversion Type + * @param acctSchema accounting schema + * @return true, if vonvertable to accounting currency + */ + public boolean isConvertible (MAcctSchema acctSchema) + { + // No Currency in document + if (getC_Currency_ID() == NO_CURRENCY) + { + log.fine("(none) - " + toString()); + return true; + } + // Get All Currencies + HashSet set = new HashSet(); + set.add(new Integer(getC_Currency_ID())); + for (int i = 0; p_lines != null && i < p_lines.length; i++) + { + int C_Currency_ID = p_lines[i].getC_Currency_ID(); + if (C_Currency_ID != NO_CURRENCY) + set.add(new Integer(C_Currency_ID)); + } + + // just one and the same + if (set.size() == 1 && acctSchema.getC_Currency_ID() == getC_Currency_ID()) + { + log.fine("(same) Cur=" + getC_Currency_ID() + " - " + toString()); + return true; + } + + boolean convertible = true; + Iterator it = set.iterator(); + while (it.hasNext() && convertible) + { + int C_Currency_ID = ((Integer)it.next()).intValue(); + if (C_Currency_ID != acctSchema.getC_Currency_ID()) + { + BigDecimal amt = MConversionRate.getRate (C_Currency_ID, acctSchema.getC_Currency_ID(), + getDateAcct(), getC_ConversionType_ID(), getAD_Client_ID(), getAD_Org_ID()); + if (amt == null) + { + convertible = false; + log.warning ("NOT from C_Currency_ID=" + C_Currency_ID + + " to " + acctSchema.getC_Currency_ID() + + " - " + toString()); + } + else + log.fine("From C_Currency_ID=" + C_Currency_ID); + } + } + + log.fine("Convertible=" + convertible + ", AcctSchema C_Currency_ID=" + acctSchema.getC_Currency_ID() + " - " + toString()); + return convertible; + } // isConvertible + + /** + * Calculate Period from DateAcct. + * m_C_Period_ID is set to -1 of not open to 0 if not found + */ + public void setPeriod() + { + if (m_period != null) + return; + + // Period defined in GL Journal (e.g. adjustment period) + int index = p_po.get_ColumnIndex("C_Period_ID"); + if (index != -1) + { + Integer ii = (Integer)p_po.get_Value(index); + if (ii != null) + m_period = MPeriod.get(getCtx(), ii.intValue()); + } + if (m_period == null) + m_period = MPeriod.get(getCtx(), getDateAcct()); + // Is Period Open? + if (m_period != null + && m_period.isOpen(getDocumentType())) + m_C_Period_ID = m_period.getC_Period_ID(); + else + m_C_Period_ID = -1; + // + log.fine( // + AD_Client_ID + " - " + getDateAcct() + " - " + getDocumentType() + " => " + m_C_Period_ID); + } // setC_Period_ID + + /** + * Get C_Period_ID + * @return period + */ + public int getC_Period_ID() + { + if (m_period == null) + setPeriod(); + return m_C_Period_ID; + } // getC_Period_ID + + /** + * Is Period Open + * @return true if period is open + */ + public boolean isPeriodOpen() + { + setPeriod(); + boolean open = m_C_Period_ID > 0; + if (open) + log.fine("Yes - " + toString()); + else + log.warning("NO - " + toString()); + return open; + } // isPeriodOpen + + /*************************************************************************/ + + /** Amount Type - Invoice - Gross */ + public static final int AMTTYPE_Gross = 0; + /** Amount Type - Invoice - Net */ + public static final int AMTTYPE_Net = 1; + /** Amount Type - Invoice - Charge */ + public static final int AMTTYPE_Charge = 2; + + /** Source Amounts (may not all be used) */ + private BigDecimal[] m_Amounts = new BigDecimal[4]; + /** Quantity */ + private BigDecimal m_qty = null; + + /** + * Get the Amount + * (loaded in loadDocumentDetails) + * + * @param AmtType see AMTTYPE_* + * @return Amount + */ + public BigDecimal getAmount(int AmtType) + { + if (AmtType < 0 || AmtType >= m_Amounts.length) + return null; + return m_Amounts[AmtType]; + } // getAmount + + /** + * Set the Amount + * @param AmtType see AMTTYPE_* + * @param amt Amount + */ + protected void setAmount(int AmtType, BigDecimal amt) + { + if (AmtType < 0 || AmtType >= m_Amounts.length) + return; + if (amt == null) + m_Amounts[AmtType] = Env.ZERO; + else + m_Amounts[AmtType] = amt; + } // setAmount + + /** + * Get Amount with index 0 + * @return Amount (primary document amount) + */ + public BigDecimal getAmount() + { + return m_Amounts[0]; + } // getAmount + + /** + * Set Quantity + * @param qty Quantity + */ + protected void setQty (BigDecimal qty) + { + m_qty = qty; + } // setQty + + /** + * Get Quantity + * @return Quantity + */ + public BigDecimal getQty() + { + if (m_qty == null) + { + int index = p_po.get_ColumnIndex("Qty"); + if (index != -1) + m_qty = (BigDecimal)p_po.get_Value(index); + else + m_qty = Env.ZERO; + } + return m_qty; + } // getQty + + /*************************************************************************/ + + /** Account Type - Invoice - Charge */ + public static final int ACCTTYPE_Charge = 0; + /** Account Type - Invoice - AR */ + public static final int ACCTTYPE_C_Receivable = 1; + /** Account Type - Invoice - AP */ + public static final int ACCTTYPE_V_Liability = 2; + /** Account Type - Invoice - AP Service */ + public static final int ACCTTYPE_V_Liability_Services = 3; + /** Account Type - Invoice - AR Service */ + public static final int ACCTTYPE_C_Receivable_Services = 4; + + /** Account Type - Payment - Unallocated */ + public static final int ACCTTYPE_UnallocatedCash = 10; + /** Account Type - Payment - Transfer */ + public static final int ACCTTYPE_BankInTransit = 11; + /** Account Type - Payment - Selection */ + public static final int ACCTTYPE_PaymentSelect = 12; + /** Account Type - Payment - Prepayment */ + public static final int ACCTTYPE_C_Prepayment = 13; + /** Account Type - Payment - Prepayment */ + public static final int ACCTTYPE_V_Prepayment = 14; + + /** Account Type - Cash - Asset */ + public static final int ACCTTYPE_CashAsset = 20; + /** Account Type - Cash - Transfer */ + public static final int ACCTTYPE_CashTransfer = 21; + /** Account Type - Cash - Expense */ + public static final int ACCTTYPE_CashExpense = 22; + /** Account Type - Cash - Receipt */ + public static final int ACCTTYPE_CashReceipt = 23; + /** Account Type - Cash - Difference */ + public static final int ACCTTYPE_CashDifference = 24; + + /** Account Type - Allocation - Discount Expense (AR) */ + public static final int ACCTTYPE_DiscountExp = 30; + /** Account Type - Allocation - Discount Revenue (AP) */ + public static final int ACCTTYPE_DiscountRev = 31; + /** Account Type - Allocation - Write Off */ + public static final int ACCTTYPE_WriteOff = 32; + + /** Account Type - Bank Statement - Asset */ + public static final int ACCTTYPE_BankAsset = 40; + /** Account Type - Bank Statement - Interest Revenue */ + public static final int ACCTTYPE_InterestRev = 41; + /** Account Type - Bank Statement - Interest Exp */ + public static final int ACCTTYPE_InterestExp = 42; + + /** Inventory Accounts - Differnces */ + public static final int ACCTTYPE_InvDifferences = 50; + /** Inventory Accounts - NIR */ + public static final int ACCTTYPE_NotInvoicedReceipts = 51; + + /** Project Accounts - Assets */ + public static final int ACCTTYPE_ProjectAsset = 61; + /** Project Accounts - WIP */ + public static final int ACCTTYPE_ProjectWIP = 62; + + /** GL Accounts - PPV Offset */ + public static final int ACCTTYPE_PPVOffset = 101; + /** GL Accounts - Commitment Offset */ + public static final int ACCTTYPE_CommitmentOffset = 111; + + + /** + * Get the Valid Combination id for Accounting Schema + * @param AcctType see ACCTTYPE_* + * @param as accounting schema + * @return C_ValidCombination_ID + */ + public int getValidCombination_ID (int AcctType, MAcctSchema as) + { + int para_1 = 0; // first parameter (second is always AcctSchema) + String sql = null; + + /** Account Type - Invoice */ + if (AcctType == ACCTTYPE_Charge) // see getChargeAccount in DocLine + { + int cmp = getAmount(AMTTYPE_Charge).compareTo(Env.ZERO); + if (cmp == 0) + return 0; + else if (cmp < 0) + sql = "SELECT CH_Expense_Acct FROM C_Charge_Acct WHERE C_Charge_ID=? AND C_AcctSchema_ID=?"; + else + sql = "SELECT CH_Revenue_Acct FROM C_Charge_Acct WHERE C_Charge_ID=? AND C_AcctSchema_ID=?"; + para_1 = getC_Charge_ID(); + } + else if (AcctType == ACCTTYPE_V_Liability) + { + sql = "SELECT V_Liability_Acct FROM C_BP_Vendor_Acct WHERE C_BPartner_ID=? AND C_AcctSchema_ID=?"; + para_1 = getC_BPartner_ID(); + } + else if (AcctType == ACCTTYPE_V_Liability_Services) + { + sql = "SELECT V_Liability_Services_Acct FROM C_BP_Vendor_Acct WHERE C_BPartner_ID=? AND C_AcctSchema_ID=?"; + para_1 = getC_BPartner_ID(); + } + else if (AcctType == ACCTTYPE_C_Receivable) + { + sql = "SELECT C_Receivable_Acct FROM C_BP_Customer_Acct WHERE C_BPartner_ID=? AND C_AcctSchema_ID=?"; + para_1 = getC_BPartner_ID(); + } + else if (AcctType == ACCTTYPE_C_Receivable_Services) + { + sql = "SELECT C_Receivable_Services_Acct FROM C_BP_Customer_Acct WHERE C_BPartner_ID=? AND C_AcctSchema_ID=?"; + para_1 = getC_BPartner_ID(); + } + else if (AcctType == ACCTTYPE_V_Prepayment) + { + sql = "SELECT V_Prepayment_Acct FROM C_BP_Vendor_Acct WHERE C_BPartner_ID=? AND C_AcctSchema_ID=?"; + para_1 = getC_BPartner_ID(); + } + else if (AcctType == ACCTTYPE_C_Prepayment) + { + sql = "SELECT C_Prepayment_Acct FROM C_BP_Customer_Acct WHERE C_BPartner_ID=? AND C_AcctSchema_ID=?"; + para_1 = getC_BPartner_ID(); + } + + /** Account Type - Payment */ + else if (AcctType == ACCTTYPE_UnallocatedCash) + { + sql = "SELECT B_UnallocatedCash_Acct FROM C_BankAccount_Acct WHERE C_BankAccount_ID=? AND C_AcctSchema_ID=?"; + para_1 = getC_BankAccount_ID(); + } + else if (AcctType == ACCTTYPE_BankInTransit) + { + sql = "SELECT B_InTransit_Acct FROM C_BankAccount_Acct WHERE C_BankAccount_ID=? AND C_AcctSchema_ID=?"; + para_1 = getC_BankAccount_ID(); + } + else if (AcctType == ACCTTYPE_PaymentSelect) + { + sql = "SELECT B_PaymentSelect_Acct FROM C_BankAccount_Acct WHERE C_BankAccount_ID=? AND C_AcctSchema_ID=?"; + para_1 = getC_BankAccount_ID(); + } + + /** Account Type - Allocation */ + else if (AcctType == ACCTTYPE_DiscountExp) + { + sql = "SELECT a.PayDiscount_Exp_Acct FROM C_BP_Group_Acct a, C_BPartner bp " + + "WHERE a.C_BP_Group_ID=bp.C_BP_Group_ID AND bp.C_BPartner_ID=? AND a.C_AcctSchema_ID=?"; + para_1 = getC_BPartner_ID(); + } + else if (AcctType == ACCTTYPE_DiscountRev) + { + sql = "SELECT PayDiscount_Rev_Acct FROM C_BP_Group_Acct a, C_BPartner bp " + + "WHERE a.C_BP_Group_ID=bp.C_BP_Group_ID AND bp.C_BPartner_ID=? AND a.C_AcctSchema_ID=?"; + para_1 = getC_BPartner_ID(); + } + else if (AcctType == ACCTTYPE_WriteOff) + { + sql = "SELECT WriteOff_Acct FROM C_BP_Group_Acct a, C_BPartner bp " + + "WHERE a.C_BP_Group_ID=bp.C_BP_Group_ID AND bp.C_BPartner_ID=? AND a.C_AcctSchema_ID=?"; + para_1 = getC_BPartner_ID(); + } + + /** Account Type - Bank Statement */ + else if (AcctType == ACCTTYPE_BankAsset) + { + sql = "SELECT B_Asset_Acct FROM C_BankAccount_Acct WHERE C_BankAccount_ID=? AND C_AcctSchema_ID=?"; + para_1 = getC_BankAccount_ID(); + } + else if (AcctType == ACCTTYPE_InterestRev) + { + sql = "SELECT B_InterestRev_Acct FROM C_BankAccount_Acct WHERE C_BankAccount_ID=? AND C_AcctSchema_ID=?"; + para_1 = getC_BankAccount_ID(); + } + else if (AcctType == ACCTTYPE_InterestExp) + { + sql = "SELECT B_InterestExp_Acct FROM C_BankAccount_Acct WHERE C_BankAccount_ID=? AND C_AcctSchema_ID=?"; + para_1 = getC_BankAccount_ID(); + } + + /** Account Type - Cash */ + else if (AcctType == ACCTTYPE_CashAsset) + { + sql = "SELECT CB_Asset_Acct FROM C_CashBook_Acct WHERE C_CashBook_ID=? AND C_AcctSchema_ID=?"; + para_1 = getC_CashBook_ID(); + } + else if (AcctType == ACCTTYPE_CashTransfer) + { + sql = "SELECT CB_CashTransfer_Acct FROM C_CashBook_Acct WHERE C_CashBook_ID=? AND C_AcctSchema_ID=?"; + para_1 = getC_CashBook_ID(); + } + else if (AcctType == ACCTTYPE_CashExpense) + { + sql = "SELECT CB_Expense_Acct FROM C_CashBook_Acct WHERE C_CashBook_ID=? AND C_AcctSchema_ID=?"; + para_1 = getC_CashBook_ID(); + } + else if (AcctType == ACCTTYPE_CashReceipt) + { + sql = "SELECT CB_Receipt_Acct FROM C_CashBook_Acct WHERE C_CashBook_ID=? AND C_AcctSchema_ID=?"; + para_1 = getC_CashBook_ID(); + } + else if (AcctType == ACCTTYPE_CashDifference) + { + sql = "SELECT CB_Differences_Acct FROM C_CashBook_Acct WHERE C_CashBook_ID=? AND C_AcctSchema_ID=?"; + para_1 = getC_CashBook_ID(); + } + + /** Inventory Accounts */ + else if (AcctType == ACCTTYPE_InvDifferences) + { + sql = "SELECT W_Differences_Acct FROM M_Warehouse_Acct WHERE M_Warehouse_ID=? AND C_AcctSchema_ID=?"; + // "SELECT W_Inventory_Acct, W_Revaluation_Acct, W_InvActualAdjust_Acct FROM M_Warehouse_Acct WHERE M_Warehouse_ID=? AND C_AcctSchema_ID=?"; + para_1 = getM_Warehouse_ID(); + } + else if (AcctType == ACCTTYPE_NotInvoicedReceipts) + { + sql = "SELECT NotInvoicedReceipts_Acct FROM C_BP_Group_Acct a, C_BPartner bp " + + "WHERE a.C_BP_Group_ID=bp.C_BP_Group_ID AND bp.C_BPartner_ID=? AND a.C_AcctSchema_ID=?"; + para_1 = getC_BPartner_ID(); + } + + /** Project Accounts */ + else if (AcctType == ACCTTYPE_ProjectAsset) + { + sql = "SELECT PJ_Asset_Acct FROM C_Project_Acct WHERE C_Project_ID=? AND C_AcctSchema_ID=?"; + para_1 = getC_Project_ID(); + } + else if (AcctType == ACCTTYPE_ProjectWIP) + { + sql = "SELECT PJ_WIP_Acct FROM C_Project_Acct WHERE C_Project_ID=? AND C_AcctSchema_ID=?"; + para_1 = getC_Project_ID(); + } + + /** GL Accounts */ + else if (AcctType == ACCTTYPE_PPVOffset) + { + sql = "SELECT PPVOffset_Acct FROM C_AcctSchema_GL WHERE C_AcctSchema_ID=?"; + para_1 = -1; + } + else if (AcctType == ACCTTYPE_CommitmentOffset) + { + sql = "SELECT CommitmentOffset_Acct FROM C_AcctSchema_GL WHERE C_AcctSchema_ID=?"; + para_1 = -1; + } + + else + { + log.severe ("Not found AcctType=" + AcctType); + return 0; + } + // Do we have sql & Parameter + if (sql == null || para_1 == 0) + { + log.severe ("No Parameter for AcctType=" + AcctType + " - SQL=" + sql); + return 0; + } + + // Get Acct + int Account_ID = 0; + try + { + PreparedStatement pstmt = DB.prepareStatement(sql, null); + if (para_1 == -1) // GL Accounts + pstmt.setInt (1, as.getC_AcctSchema_ID()); + else + { + pstmt.setInt (1, para_1); + pstmt.setInt (2, as.getC_AcctSchema_ID()); + } + ResultSet rs = pstmt.executeQuery(); + if (rs.next()) + Account_ID = rs.getInt(1); + rs.close(); + pstmt.close(); + } + catch (SQLException e) + { + log.log(Level.SEVERE, "AcctType=" + AcctType + " - SQL=" + sql, e); + return 0; + } + // No account + if (Account_ID == 0) + { + log.severe ("NO account Type=" + + AcctType + ", Record=" + p_po.get_ID()); + return 0; + } + return Account_ID; + } // getAccount_ID + + /** + * Get the account for Accounting Schema + * @param AcctType see ACCTTYPE_* + * @param as accounting schema + * @return Account + */ + public final MAccount getAccount (int AcctType, MAcctSchema as) + { + int C_ValidCombination_ID = getValidCombination_ID(AcctType, as); + if (C_ValidCombination_ID == 0) + return null; + // Return Account + MAccount acct = MAccount.get (as.getCtx(), C_ValidCombination_ID); + return acct; + } // getAccount + + + /************************************************************************** + * Save to Disk - set posted flag + * @param trxName transaction name + * @return true if saved + */ + private final boolean save (String trxName) + { + log.fine(toString() + "->" + p_Status); + + StringBuffer sql = new StringBuffer("UPDATE "); + sql.append(get_TableName()).append(" SET Posted='").append(p_Status) + .append("',Processing='N' ") + .append("WHERE ") + .append(get_TableName()).append("_ID=").append(p_po.get_ID()); + int no = DB.executeUpdate(sql.toString(), trxName); + return no == 1; + } // save + + /** + * Get DocLine with ID + * @param Record_ID Record ID + * @return DocLine + */ + public DocLine getDocLine (int Record_ID) + { + if (p_lines == null || p_lines.length == 0 || Record_ID == 0) + return null; + + for (int i = 0; i < p_lines.length; i++) + { + if (p_lines[i].get_ID() == Record_ID) + return p_lines[i]; + } + return null; + } // getDocLine + + /** + * String Representation + * @return String + */ + public String toString() + { + return p_po.toString(); + } // toString + + + /** + * Get AD_Client_ID + * @return client + */ + public int getAD_Client_ID() + { + return p_po.getAD_Client_ID(); + } // getAD_Client_ID + + /** + * Get AD_Org_ID + * @return org + */ + public int getAD_Org_ID() + { + return p_po.getAD_Org_ID(); + } // getAD_Org_ID + + /** + * Get Document No + * @return document No + */ + public String getDocumentNo() + { + if (m_DocumentNo != null) + return m_DocumentNo; + int index = p_po.get_ColumnIndex("DocumentNo"); + if (index == -1) + index = p_po.get_ColumnIndex("Name"); + if (index == -1) + throw new UnsupportedOperationException("No DocumentNo"); + m_DocumentNo = (String)p_po.get_Value(index); + return m_DocumentNo; + } // getDocumentNo + + /** + * Get Description + * @return Description + */ + public String getDescription() + { + if (m_Description == null) + { + int index = p_po.get_ColumnIndex("Description"); + if (index != -1) + m_Description = (String)p_po.get_Value(index); + else + m_Description = ""; + } + return m_Description; + } // getDescription + + /** + * Get C_Currency_ID + * @return currency + */ + public int getC_Currency_ID() + { + if (m_C_Currency_ID == -1) + { + int index = p_po.get_ColumnIndex("C_Currency_ID"); + if (index != -1) + { + Integer ii = (Integer)p_po.get_Value(index); + if (ii != null) + m_C_Currency_ID = ii.intValue(); + } + if (m_C_Currency_ID == -1) + m_C_Currency_ID = NO_CURRENCY; + } + return m_C_Currency_ID; + } // getC_Currency_ID + + /** + * Set C_Currency_ID + * @param C_Currency_ID id + */ + public void setC_Currency_ID (int C_Currency_ID) + { + m_C_Currency_ID = C_Currency_ID; + } // setC_Currency_ID + + /** + * Is Multi Currency + * @return mc + */ + public boolean isMultiCurrency() + { + return m_MultiCurrency; + } // isMultiCurrency + + /** + * Set Multi Currency + * @param mc multi currency + */ + protected void setIsMultiCurrency (boolean mc) + { + m_MultiCurrency = mc; + } // setIsMultiCurrency + + /** + * Is Tax Included + * @return tax incl + */ + public boolean isTaxIncluded() + { + return m_TaxIncluded; + } // isTaxIncluded + + /** + * Set Tax Includedy + * @param ti Tax Included + */ + protected void setIsTaxIncluded (boolean ti) + { + m_TaxIncluded = ti; + } // setIsTaxIncluded + + /** + * Get C_ConversionType_ID + * @return ConversionType + */ + public int getC_ConversionType_ID() + { + int index = p_po.get_ColumnIndex("C_ConversionType_ID"); + if (index != -1) + { + Integer ii = (Integer)p_po.get_Value(index); + if (ii != null) + return ii.intValue(); + } + return 0; + } // getC_ConversionType_ID + + /** + * Get GL_Category_ID + * @return categoory + */ + public int getGL_Category_ID() + { + return m_GL_Category_ID; + } // getGL_Category_ID + + /** + * Get GL_Category_ID + * @return categoory + */ + public int getGL_Budget_ID() + { + int index = p_po.get_ColumnIndex("GL_Budget_ID"); + if (index != -1) + { + Integer ii = (Integer)p_po.get_Value(index); + if (ii != null) + return ii.intValue(); + } + return 0; + } // getGL_Budget_ID + + /** + * Get Accounting Date + * @return currency + */ + public Timestamp getDateAcct() + { + if (m_DateAcct != null) + return m_DateAcct; + int index = p_po.get_ColumnIndex("DateAcct"); + if (index != -1) + { + m_DateAcct = (Timestamp)p_po.get_Value(index); + if (m_DateAcct != null) + return m_DateAcct; + } + throw new IllegalStateException("No DateAcct"); + } // getDateAcct + + /** + * Set Date Acct + * @param da accounting date + */ + protected void setDateAcct (Timestamp da) + { + m_DateAcct = da; + } // setDateAcct + + /** + * Get Document Date + * @return currency + */ + public Timestamp getDateDoc() + { + if (m_DateDoc != null) + return m_DateDoc; + int index = p_po.get_ColumnIndex("DateDoc"); + if (index == -1) + index = p_po.get_ColumnIndex("MovementDate"); + if (index != -1) + { + m_DateDoc = (Timestamp)p_po.get_Value(index); + if (m_DateDoc != null) + return m_DateDoc; + } + throw new IllegalStateException("No DateDoc"); + } // getDateDoc + + /** + * Set Date Doc + * @param dd document date + */ + protected void setDateDoc (Timestamp dd) + { + m_DateDoc = dd; + } // setDateDoc + + /** + * Is Document Posted + * @return true if posted + */ + public boolean isPosted() + { + int index = p_po.get_ColumnIndex("Posted"); + if (index != -1) + { + Object posted = p_po.get_Value(index); + if (posted instanceof Boolean) + return ((Boolean)posted).booleanValue(); + if (posted instanceof String) + return "Y".equals(posted); + } + throw new IllegalStateException("No Posted"); + } // isPosted + + /** + * Is Sales Trx + * @return true if posted + */ + public boolean isSOTrx() + { + int index = p_po.get_ColumnIndex("IsSOTrx"); + if (index == -1) + index = p_po.get_ColumnIndex("IsReceipt"); + if (index != -1) + { + Object posted = p_po.get_Value(index); + if (posted instanceof Boolean) + return ((Boolean)posted).booleanValue(); + if (posted instanceof String) + return "Y".equals(posted); + } + return false; + } // isSOTrx + + /** + * Get C_DocType_ID + * @return DocType + */ + public int getC_DocType_ID() + { + int index = p_po.get_ColumnIndex("C_DocType_ID"); + if (index != -1) + { + Integer ii = (Integer)p_po.get_Value(index); + // DocType does not exist - get DocTypeTarget + if (ii != null && ii.intValue() == 0) + { + index = p_po.get_ColumnIndex("C_DocTypeTarget_ID"); + if (index != -1) + ii = (Integer)p_po.get_Value(index); + } + if (ii != null) + return ii.intValue(); + } + return 0; + } // getC_DocType_ID + + /** + * Get header level C_Charge_ID + * @return Charge + */ + public int getC_Charge_ID() + { + int index = p_po.get_ColumnIndex("C_Charge_ID"); + if (index != -1) + { + Integer ii = (Integer)p_po.get_Value(index); + if (ii != null) + return ii.intValue(); + } + return 0; + } // getC_Charge_ID + + /** + * Get SalesRep_ID + * @return SalesRep + */ + public int getSalesRep_ID() + { + int index = p_po.get_ColumnIndex("SalesRep_ID"); + if (index != -1) + { + Integer ii = (Integer)p_po.get_Value(index); + if (ii != null) + return ii.intValue(); + } + return 0; + } // getSalesRep_ID + + /** + * Get C_BankAccount_ID + * @return BankAccount + */ + public int getC_BankAccount_ID() + { + if (m_C_BankAccount_ID == -1) + { + int index = p_po.get_ColumnIndex("C_BankAccount_ID"); + if (index != -1) + { + Integer ii = (Integer)p_po.get_Value(index); + if (ii != null) + m_C_BankAccount_ID = ii.intValue(); + } + if (m_C_BankAccount_ID == -1) + m_C_BankAccount_ID = 0; + } + return m_C_BankAccount_ID; + } // getC_BankAccount_ID + + /** + * Set C_BankAccount_ID + * @param C_BankAccount_ID bank acct + */ + protected void setC_BankAccount_ID (int C_BankAccount_ID) + { + m_C_BankAccount_ID = C_BankAccount_ID; + } // setC_BankAccount_ID + + /** + * Get C_CashBook_ID + * @return CashBook + */ + public int getC_CashBook_ID() + { + if (m_C_CashBook_ID == -1) + { + int index = p_po.get_ColumnIndex("C_CashBook_ID"); + if (index != -1) + { + Integer ii = (Integer)p_po.get_Value(index); + if (ii != null) + m_C_CashBook_ID = ii.intValue(); + } + if (m_C_CashBook_ID == -1) + m_C_CashBook_ID = 0; + } + return m_C_CashBook_ID; + } // getC_CashBook_ID + + /** + * Set C_CashBook_ID + * @param C_CashBook_ID cash book + */ + protected void setC_CashBook_ID (int C_CashBook_ID) + { + m_C_CashBook_ID = C_CashBook_ID; + } // setC_CashBook_ID + + /** + * Get M_Warehouse_ID + * @return Warehouse + */ + public int getM_Warehouse_ID() + { + int index = p_po.get_ColumnIndex("M_Warehouse_ID"); + if (index != -1) + { + Integer ii = (Integer)p_po.get_Value(index); + if (ii != null) + return ii.intValue(); + } + return 0; + } // getM_Warehouse_ID + + + /** + * Get C_BPartner_ID + * @return BPartner + */ + public int getC_BPartner_ID() + { + if (m_C_BPartner_ID == -1) + { + int index = p_po.get_ColumnIndex("C_BPartner_ID"); + if (index != -1) + { + Integer ii = (Integer)p_po.get_Value(index); + if (ii != null) + m_C_BPartner_ID = ii.intValue(); + } + if (m_C_BPartner_ID == -1) + m_C_BPartner_ID = 0; + } + return m_C_BPartner_ID; + } // getC_BPartner_ID + + /** + * Set C_BPartner_ID + * @param C_BPartner_ID bp + */ + protected void setC_BPartner_ID (int C_BPartner_ID) + { + m_C_BPartner_ID = C_BPartner_ID; + } // setC_BPartner_ID + + /** + * Get C_BPartner_Location_ID + * @return BPartner Location + */ + public int getC_BPartner_Location_ID() + { + int index = p_po.get_ColumnIndex("C_BPartner_Location_ID"); + if (index != -1) + { + Integer ii = (Integer)p_po.get_Value(index); + if (ii != null) + return ii.intValue(); + } + return 0; + } // getC_BPartner_Location_ID + + /** + * Get C_Project_ID + * @return Project + */ + public int getC_Project_ID() + { + int index = p_po.get_ColumnIndex("C_Project_ID"); + if (index != -1) + { + Integer ii = (Integer)p_po.get_Value(index); + if (ii != null) + return ii.intValue(); + } + return 0; + } // getC_Project_ID + + /** + * Get C_SalesRegion_ID + * @return Sales Region + */ + public int getC_SalesRegion_ID() + { + int index = p_po.get_ColumnIndex("C_SalesRegion_ID"); + if (index != -1) + { + Integer ii = (Integer)p_po.get_Value(index); + if (ii != null) + return ii.intValue(); + } + return 0; + } // getC_SalesRegion_ID + + /** + * Get C_SalesRegion_ID + * @return Sales Region + */ + public int getBP_C_SalesRegion_ID() + { + if (m_BP_C_SalesRegion_ID == -1) + { + int index = p_po.get_ColumnIndex("C_SalesRegion_ID"); + if (index != -1) + { + Integer ii = (Integer)p_po.get_Value(index); + if (ii != null) + m_BP_C_SalesRegion_ID = ii.intValue(); + } + if (m_BP_C_SalesRegion_ID == -1) + m_BP_C_SalesRegion_ID = 0; + } + return m_BP_C_SalesRegion_ID; + } // getBP_C_SalesRegion_ID + + /** + * Set C_SalesRegion_ID + * @param C_SalesRegion_ID id + */ + protected void setBP_C_SalesRegion_ID (int C_SalesRegion_ID) + { + m_BP_C_SalesRegion_ID = C_SalesRegion_ID; + } // setBP_C_SalesRegion_ID + + /** + * Get C_Activity_ID + * @return Activity + */ + public int getC_Activity_ID() + { + int index = p_po.get_ColumnIndex("C_Activity_ID"); + if (index != -1) + { + Integer ii = (Integer)p_po.get_Value(index); + if (ii != null) + return ii.intValue(); + } + return 0; + } // getC_Activity_ID + + /** + * Get C_Campaign_ID + * @return Campaign + */ + public int getC_Campaign_ID() + { + int index = p_po.get_ColumnIndex("C_Campaign_ID"); + if (index != -1) + { + Integer ii = (Integer)p_po.get_Value(index); + if (ii != null) + return ii.intValue(); + } + return 0; + } // getC_Campaign_ID + + /** + * Get M_Product_ID + * @return Product + */ + public int getM_Product_ID() + { + int index = p_po.get_ColumnIndex("M_Product_ID"); + if (index != -1) + { + Integer ii = (Integer)p_po.get_Value(index); + if (ii != null) + return ii.intValue(); + } + return 0; + } // getM_Product_ID + + /** + * Get AD_OrgTrx_ID + * @return Trx Org + */ + public int getAD_OrgTrx_ID() + { + int index = p_po.get_ColumnIndex("AD_OrgTrx_ID"); + if (index != -1) + { + Integer ii = (Integer)p_po.get_Value(index); + if (ii != null) + return ii.intValue(); + } + return 0; + } // getAD_OrgTrx_ID + + /** + * Get C_LocFrom_ID + * @return loc from + */ + public int getC_LocFrom_ID() + { + return m_C_LocFrom_ID; + } // getC_LocFrom_ID + + /** + * Set C_LocFrom_ID + * @param C_LocFrom_ID loc from + */ + protected void setC_LocFrom_ID(int C_LocFrom_ID) + { + m_C_LocFrom_ID = C_LocFrom_ID; + } // setC_LocFrom_ID + + /** + * Get C_LocTo_ID + * @return loc to + */ + public int getC_LocTo_ID() + { + return m_C_LocTo_ID; + } // getC_LocTo_ID + + /** + * Set C_LocTo_ID + * @param C_LocTo_ID loc to + */ + protected void setC_LocTo_ID(int C_LocTo_ID) + { + m_C_LocTo_ID = C_LocTo_ID; + } // setC_LocTo_ID + + /** + * Get User1_ID + * @return Campaign + */ + public int getUser1_ID() + { + int index = p_po.get_ColumnIndex("User1_ID"); + if (index != -1) + { + Integer ii = (Integer)p_po.get_Value(index); + if (ii != null) + return ii.intValue(); + } + return 0; + } // getUser1_ID + + /** + * Get User2_ID + * @return Campaign + */ + public int getUser2_ID() + { + int index = p_po.get_ColumnIndex("User2_ID"); + if (index != -1) + { + Integer ii = (Integer)p_po.get_Value(index); + if (ii != null) + return ii.intValue(); + } + return 0; + } // getUser2_ID + + + /*************************************************************************/ + // To be overwritten by Subclasses + + /** + * Load Document Details + * @return error message or null + */ + protected abstract String loadDocumentDetails (); + + /** + * Get Source Currency Balance - subtracts line (and tax) amounts from total - no rounding + * @return positive amount, if total header is bigger than lines + */ + public abstract BigDecimal getBalance(); + + /** + * Create Facts (the accounting logic) + * @param as accounting schema + * @return Facts + */ + public abstract ArrayList createFacts (MAcctSchema as); + +} // Doc diff --git a/serverRoot/src/main/server/org/compiere/acct/DocLine.java b/serverRoot/src/main/server/org/compiere/acct/DocLine.java new file mode 100644 index 0000000000..ff6ecd0fc7 --- /dev/null +++ b/serverRoot/src/main/server/org/compiere/acct/DocLine.java @@ -0,0 +1,1016 @@ +/****************************************************************************** + * 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.acct; + +import java.math.*; +import java.sql.*; + +import org.compiere.model.*; +import org.compiere.util.*; + +/** + * Standard Document Line + * + * @author Jorg Janke + * @version $Id: DocLine.java,v 1.2 2006/07/30 00:53:33 jjanke Exp $ + */ +public class DocLine +{ + /** + * Create Document Line + * @param po line persistent object + * @param doc header + */ + public DocLine (PO po, Doc doc) + { + if (po == null) + throw new IllegalArgumentException("PO is null"); + p_po = po; + m_doc = doc; + // + // Document Consistency + if (p_po.getAD_Org_ID() == 0) + p_po.setAD_Org_ID(m_doc.getAD_Org_ID()); + } // DocLine + + /** Persistent Object */ + protected PO p_po = null; + /** Parent */ + private Doc m_doc = null; + /** Log */ + protected CLogger log = CLogger.getCLogger(getClass()); + + /** Qty */ + private BigDecimal m_qty = null; + + // -- GL Amounts + /** Debit Journal Amt */ + private BigDecimal m_AmtSourceDr = Env.ZERO; + /** Credit Journal Amt */ + private BigDecimal m_AmtSourceCr = Env.ZERO; + /** Net Line Amt */ + private BigDecimal m_LineNetAmt = null; + /** List Amount */ + private BigDecimal m_ListAmt = Env.ZERO; + /** Discount Amount */ + private BigDecimal m_DiscountAmt = Env.ZERO; + + /** Converted Amounts */ + private BigDecimal m_AmtAcctDr = null; + private BigDecimal m_AmtAcctCr = null; + /** Acct Schema */ + private int m_C_AcctSchema_ID = 0; + + /** Product Costs */ + private ProductCost m_productCost = null; + /** Production indicator */ + private boolean m_productionBOM = false; + /** Account used only for GL Journal */ + private MAccount m_account = null; + + /** Accounting Date */ + private Timestamp m_DateAcct = null; + /** Document Date */ + private Timestamp m_DateDoc = null; + /** Sales Region */ + private int m_C_SalesRegion_ID = -1; + /** Sales Region */ + private int m_C_BPartner_ID = -1; + /** Location From */ + private int m_C_LocFrom_ID = 0; + /** Location To */ + private int m_C_LocTo_ID = 0; + /** Item */ + private Boolean m_isItem = null; + /** Currency */ + private int m_C_Currency_ID = -1; + /** Conversion Type */ + private int m_C_ConversionType_ID = -1; + /** Period */ + private int m_C_Period_ID = -1; + + /** + * Get Currency + * @return c_Currency_ID + */ + public int getC_Currency_ID () + { + if (m_C_Currency_ID == -1) + { + int index = p_po.get_ColumnIndex("C_Currency_ID"); + if (index != -1) + { + Integer ii = (Integer)p_po.get_Value(index); + if (ii != null) + m_C_Currency_ID = ii.intValue(); + } + if (m_C_Currency_ID <= 0) + m_C_Currency_ID = m_doc.getC_Currency_ID(); + } + return m_C_Currency_ID; + } // getC_Currency_ID + + /** + * Get Conversion Type + * @return C_ConversionType_ID + */ + public int getC_ConversionType_ID () + { + if (m_C_ConversionType_ID == -1) + { + int index = p_po.get_ColumnIndex("C_ConversionType_ID"); + if (index != -1) + { + Integer ii = (Integer)p_po.get_Value(index); + if (ii != null) + m_C_ConversionType_ID = ii.intValue(); + } + if (m_C_ConversionType_ID <= 0) + m_C_ConversionType_ID = m_doc.getC_ConversionType_ID(); + } + return m_C_ConversionType_ID; + } // getC_ConversionType_ID + + /** + * Set C_ConversionType_ID + * @param C_ConversionType_ID id + */ + protected void setC_ConversionType_ID(int C_ConversionType_ID) + { + m_C_ConversionType_ID = C_ConversionType_ID; + } // setC_ConversionType_ID + + /** + * Set Amount (DR) + * @param sourceAmt source amt + */ + public void setAmount (BigDecimal sourceAmt) + { + m_AmtSourceDr = sourceAmt == null ? Env.ZERO : sourceAmt; + m_AmtSourceCr = Env.ZERO; + } // setAmounts + + /** + * Set Amounts + * @param amtSourceDr source amount dr + * @param amtSourceCr source amount cr + */ + public void setAmount (BigDecimal amtSourceDr, BigDecimal amtSourceCr) + { + m_AmtSourceDr = amtSourceDr == null ? Env.ZERO : amtSourceDr; + m_AmtSourceCr = amtSourceCr == null ? Env.ZERO : amtSourceCr; + } // setAmounts + + /** + * Set Converted Amounts + * @param C_AcctSchema_ID acct schema + * @param amtAcctDr acct amount dr + * @param amtAcctCr acct amount cr + */ + public void setConvertedAmt (int C_AcctSchema_ID, BigDecimal amtAcctDr, BigDecimal amtAcctCr) + { + m_C_AcctSchema_ID = C_AcctSchema_ID; + m_AmtAcctDr = amtAcctDr; + m_AmtAcctCr = amtAcctCr; + } // setConvertedAmt + + /** + * Line Net Amount or Dr-Cr + * @return balance + */ + public BigDecimal getAmtSource() + { + return m_AmtSourceDr.subtract(m_AmtSourceCr); + } // getAmount + + /** + * Get (Journal) Line Source Dr Amount + * @return DR source amount + */ + public BigDecimal getAmtSourceDr() + { + return m_AmtSourceDr; + } // getAmtSourceDr + + /** + * Get (Journal) Line Source Cr Amount + * @return CR source amount + */ + public BigDecimal getAmtSourceCr() + { + return m_AmtSourceCr; + } // getAmtSourceCr + + /** + * Line Journal Accounted Dr Amount + * @return DR accounted amount + */ + public BigDecimal getAmtAcctDr() + { + return m_AmtAcctDr; + } // getAmtAcctDr + + /** + * Line Journal Accounted Cr Amount + * @return CR accounted amount + */ + public BigDecimal getAmtAcctCr() + { + return m_AmtAcctCr; + } // getAmtAccrCr + + /** + * Charge Amount + * @return charge amount + */ + public BigDecimal getChargeAmt() + { + int index = p_po.get_ColumnIndex("ChargeAmt"); + if (index != -1) + { + BigDecimal bd = (BigDecimal)p_po.get_Value(index); + if (bd != null) + return bd; + } + return Env.ZERO; + } // getChargeAmt + + /** + * Set Product Amounts + * @param LineNetAmt Line Net Amt + * @param PriceList Price List + * @param Qty Qty for discount calc + */ + public void setAmount (BigDecimal LineNetAmt, BigDecimal PriceList, BigDecimal Qty) + { + m_LineNetAmt = LineNetAmt == null ? Env.ZERO : LineNetAmt; + + if (PriceList != null && Qty != null) + m_ListAmt = PriceList.multiply(Qty); + if (m_ListAmt.equals(Env.ZERO)) + m_ListAmt = m_LineNetAmt; + m_DiscountAmt = m_ListAmt.subtract(m_LineNetAmt); + // + setAmount (m_ListAmt, m_DiscountAmt); + // Log.trace(this,Log.l6_Database, "DocLine_Invoice.setAmount", + // "LineNet=" + m_LineNetAmt + ", List=" + m_ListAmt + ", Discount=" + m_DiscountAmt + // + " => Amount=" + getAmount()); + } // setAmounts + + /** + * Line Discount + * @return discount amount + */ + public BigDecimal getDiscount() + { + return m_DiscountAmt; + } // getDiscount + + /** + * Line List Amount + * @return list amount + */ + public BigDecimal getListAmount() + { + return m_ListAmt; + } // getListAmount + + /** + * Set Line Net Amt Difference + * @param diff difference (to be subtracted) + */ + public void setLineNetAmtDifference (BigDecimal diff) + { + String msg = "Diff=" + diff + + " - LineNetAmt=" + m_LineNetAmt; + m_LineNetAmt = m_LineNetAmt.subtract(diff); + m_DiscountAmt = m_ListAmt.subtract(m_LineNetAmt); + setAmount (m_ListAmt, m_DiscountAmt); + msg += " -> " + m_LineNetAmt; + log.fine(msg); + } // setLineNetAmtDifference + + /************************************************************************** + * Set Accounting Date + * @param dateAcct acct date + */ + public void setDateAcct (Timestamp dateAcct) + { + m_DateAcct = dateAcct; + } // setDateAcct + + /** + * Get Accounting Date + * @return accounting date + */ + public Timestamp getDateAcct () + { + if (m_DateAcct != null) + return m_DateAcct; + int index = p_po.get_ColumnIndex("DateAcct"); + if (index != -1) + { + m_DateAcct = (Timestamp)p_po.get_Value(index); + if (m_DateAcct != null) + return m_DateAcct; + } + m_DateAcct = m_doc.getDateAcct(); + return m_DateAcct; + } // getDateAcct + + /** + * Set Document Date + * @param dateDoc doc date + */ + public void setDateDoc (Timestamp dateDoc) + { + m_DateDoc = dateDoc; + } // setDateDoc + + /** + * Get Document Date + * @return document date + */ + public Timestamp getDateDoc () + { + if (m_DateDoc != null) + return m_DateDoc; + int index = p_po.get_ColumnIndex("DateDoc"); + if (index != -1) + { + m_DateDoc = (Timestamp)p_po.get_Value(index); + if (m_DateDoc != null) + return m_DateDoc; + } + m_DateDoc = m_doc.getDateDoc(); + return m_DateDoc; + } // getDateDoc + + + /************************************************************************** + * Set GL Journal Account + * @param acct account + */ + public void setAccount (MAccount acct) + { + m_account = acct; + } // setAccount + + /** + * Get GL Journal Account + * @return account + */ + public MAccount getAccount() + { + return m_account; + } // getAccount + + /** + * Line Account from Product (or Charge). + * + * @param AcctType see ProductCost.ACCTTYPE_* (0..3) + * @param as Accounting schema + * @return Requested Product Account + */ + public MAccount getAccount (int AcctType, MAcctSchema as) + { + // Charge Account + if (getM_Product_ID() == 0 && getC_Charge_ID() != 0) + { + BigDecimal amt = new BigDecimal (-1); // Revenue (-) + if (!m_doc.isSOTrx()) + amt = new BigDecimal (+1); // Expense (+) + MAccount acct = getChargeAccount(as, amt); + if (acct != null) + return acct; + } + // Product Account + return getProductCost().getAccount (AcctType, as); + } // getAccount + + /** + * Get Charge + * @return C_Charge_ID + */ + protected int getC_Charge_ID() + { + int index = p_po.get_ColumnIndex("C_Charge_ID"); + if (index != -1) + { + Integer ii = (Integer)p_po.get_Value(index); + if (ii != null) + return ii.intValue(); + } + return 0; + } // getC_Charge_ID + + /** + * Get Charge Account + * @param as account schema + * @param amount amount for expense(+)/revenue(-) + * @return Charge Account or null + */ + public MAccount getChargeAccount (MAcctSchema as, BigDecimal amount) + { + int C_Charge_ID = getC_Charge_ID(); + if (C_Charge_ID == 0) + return null; + return MCharge.getAccount(C_Charge_ID, as, amount); + } // getChargeAccount + + /** + * Get Period + * @return C_Period_ID + */ + protected int getC_Period_ID() + { + if (m_C_Period_ID == -1) + { + int index = p_po.get_ColumnIndex("C_Period_ID"); + if (index != -1) + { + Integer ii = (Integer)p_po.get_Value(index); + if (ii != null) + m_C_Period_ID = ii.intValue(); + } + if (m_C_Period_ID == -1) + m_C_Period_ID = 0; + } + return m_C_Period_ID; + } // getC_Period_ID + + /** + * Set C_Period_ID + * @param C_Period_ID id + */ + protected void setC_Period_ID (int C_Period_ID) + { + m_C_Period_ID = C_Period_ID; + } // setC_Period_ID + + /************************************************************************** + * Get (Journal) AcctSchema + * @return C_AcctSchema_ID + */ + public int getC_AcctSchema_ID() + { + return m_C_AcctSchema_ID; + } // getC_AcctSchema_ID + + /** + * Get Line ID + * @return id + */ + public int get_ID() + { + return p_po.get_ID(); + } // get_ID + + /** + * Get AD_Org_ID + * @return org + */ + public int getAD_Org_ID() + { + return p_po.getAD_Org_ID(); + } // getAD_Org_ID + + /** + * Get Order AD_Org_ID + * @return order org if defined + */ + public int getOrder_Org_ID() + { + int C_OrderLine_ID = getC_OrderLine_ID(); + if (C_OrderLine_ID != 0) + { + String sql = "SELECT AD_Org_ID FROM C_OrderLine WHERE C_OrderLine_ID=?"; + int AD_Org_ID = DB.getSQLValue(null, sql, C_OrderLine_ID); + if (AD_Org_ID > 0) + return AD_Org_ID; + } + return getAD_Org_ID(); + } // getOrder_Org_ID + + /** + * Product + * @return M_Product_ID + */ + public int getM_Product_ID() + { + int index = p_po.get_ColumnIndex("M_Product_ID"); + if (index != -1) + { + Integer ii = (Integer)p_po.get_Value(index); + if (ii != null) + return ii.intValue(); + } + return 0; + } // getM_Product_ID + + /** + * Is this an Item Product (vs. not a Service, a charge) + * @return true if product + */ + public boolean isItem() + { + if (m_isItem != null) + return m_isItem.booleanValue(); + + m_isItem = Boolean.FALSE; + if (getM_Product_ID() != 0) + { + MProduct product = MProduct.get(Env.getCtx(), getM_Product_ID()); + if (product.get_ID() == getM_Product_ID() && product.isItem()) + m_isItem = Boolean.TRUE; + } + return m_isItem.booleanValue(); + } // isItem + + /** + * ASI + * @return M_AttributeSetInstance_ID + */ + public int getM_AttributeSetInstance_ID() + { + int index = p_po.get_ColumnIndex("M_AttributeSetInstance_ID"); + if (index != -1) + { + Integer ii = (Integer)p_po.get_Value(index); + if (ii != null) + return ii.intValue(); + } + return 0; + } // getM_AttributeSetInstance_ID + + /** + * Get Warehouse Locator (from) + * @return M_Locator_ID + */ + public int getM_Locator_ID() + { + int index = p_po.get_ColumnIndex("M_Locator_ID"); + if (index != -1) + { + Integer ii = (Integer)p_po.get_Value(index); + if (ii != null) + return ii.intValue(); + } + return 0; + } // getM_Locator_ID + + /** + * Get Warehouse Locator To + * @return M_Locator_ID + */ + public int getM_LocatorTo_ID() + { + int index = p_po.get_ColumnIndex("M_LocatorTo_ID"); + if (index != -1) + { + Integer ii = (Integer)p_po.get_Value(index); + if (ii != null) + return ii.intValue(); + } + return 0; + } // getM_LocatorTo_ID + + /** + * Set Production BOM flag + * @param productionBOM flag + */ + public void setProductionBOM(boolean productionBOM) + { + m_productionBOM = productionBOM; + } // setProductionBOM + + /** + * Is this the BOM to be produced + * @return true if BOM + */ + public boolean isProductionBOM() + { + return m_productionBOM; + } // isProductionBOM + + /** + * Get Production Plan + * @return M_ProductionPlan_ID + */ + public int getM_ProductionPlan_ID() + { + int index = p_po.get_ColumnIndex("M_ProductionPlan_ID"); + if (index != -1) + { + Integer ii = (Integer)p_po.get_Value(index); + if (ii != null) + return ii.intValue(); + } + return 0; + } // getM_ProductionPlan_ID + + /** + * Get Order Line Reference + * @return C_OrderLine_ID + */ + public int getC_OrderLine_ID() + { + int index = p_po.get_ColumnIndex("C_OrderLine_ID"); + if (index != -1) + { + Integer ii = (Integer)p_po.get_Value(index); + if (ii != null) + return ii.intValue(); + } + return 0; + } // getC_OrderLine_ID + + /** + * Get C_LocFrom_ID + * @return loc from + */ + public int getC_LocFrom_ID() + { + return m_C_LocFrom_ID; + } // getC_LocFrom_ID + + /** + * Set C_LocFrom_ID + * @param C_LocFrom_ID loc from + */ + public void setC_LocFrom_ID(int C_LocFrom_ID) + { + m_C_LocFrom_ID = C_LocFrom_ID; + } // setC_LocFrom_ID + + /** + * Get C_LocTo_ID + * @return loc to + */ + public int getC_LocTo_ID() + { + return m_C_LocTo_ID; + } // getC_LocTo_ID + + /** + * Set C_LocTo_ID + * @param C_LocTo_ID loc to + */ + public void setC_LocTo_ID(int C_LocTo_ID) + { + m_C_LocTo_ID = C_LocTo_ID; + } // setC_LocTo_ID + + /** + * Get Product Cost Info + * @return product cost + */ + public ProductCost getProductCost() + { + if (m_productCost == null) + m_productCost = new ProductCost (Env.getCtx(), + getM_Product_ID(), getM_AttributeSetInstance_ID(), p_po.get_TrxName()); + return m_productCost; + } // getProductCost + + /** + * Get Total Product Costs + * @param as accounting schema + * @param AD_Org_ID trx org + * @param zeroCostsOK zero/no costs are OK + * @return costs + */ + public BigDecimal getProductCosts (MAcctSchema as, int AD_Org_ID, boolean zeroCostsOK) + { + ProductCost pc = getProductCost(); + int C_OrderLine_ID = getC_OrderLine_ID(); + String costingMethod = null; + BigDecimal costs = pc.getProductCosts(as, AD_Org_ID, costingMethod, + C_OrderLine_ID, zeroCostsOK); + if (costs != null) + return costs; + return Env.ZERO; + } // getProductCosts + + /** + * Get Product + * @return product or null if no product + */ + public MProduct getProduct() + { + if (m_productCost == null) + m_productCost = new ProductCost (Env.getCtx(), + getM_Product_ID(), getM_AttributeSetInstance_ID(), p_po.get_TrxName()); + if (m_productCost != null) + return m_productCost.getProduct(); + return null; + } // getProduct + + /** + * Get Revenue Recognition + * @return C_RevenueRecognition_ID or 0 + */ + public int getC_RevenueRecognition_ID() + { + MProduct product = getProduct(); + if (product != null) + return product.getC_RevenueRecognition_ID(); + return 0; + } // getC_RevenueRecognition_ID + + /** + * Quantity UOM + * @return Transaction or Storage M_UOM_ID + */ + public int getC_UOM_ID() + { + // Trx UOM + int index = p_po.get_ColumnIndex("C_UOM_ID"); + if (index != -1) + { + Integer ii = (Integer)p_po.get_Value(index); + if (ii != null) + return ii.intValue(); + } + // Storage UOM + MProduct product = getProduct(); + if (product != null) + return product.getC_UOM_ID(); + // + return 0; + } // getC_UOM + + /** + * Quantity + * @param qty transaction Qty + * @param isSOTrx SL order trx (i.e. negative qty) + */ + public void setQty (BigDecimal qty, boolean isSOTrx) + { + if (qty == null) + m_qty = Env.ZERO; + else if (isSOTrx) + m_qty = qty.negate(); + else + m_qty = qty; + getProductCost().setQty (qty); + } // setQty + + /** + * Quantity + * @return transaction Qty + */ + public BigDecimal getQty() + { + return m_qty; + } // getQty + + + + /** + * Description + * @return doc line description + */ + public String getDescription() + { + int index = p_po.get_ColumnIndex("Description"); + if (index != -1) + return (String)p_po.get_Value(index); + return null; + } // getDescription + + /** + * Line Tax + * @return C_Tax_ID + */ + public int getC_Tax_ID() + { + int index = p_po.get_ColumnIndex("C_Tax_ID"); + if (index != -1) + { + Integer ii = (Integer)p_po.get_Value(index); + if (ii != null) + return ii.intValue(); + } + return 0; + } // getC_Tax_ID + + /** + * Get Line Number + * @return line no + */ + public int getLine() + { + int index = p_po.get_ColumnIndex("Line"); + if (index != -1) + { + Integer ii = (Integer)p_po.get_Value(index); + if (ii != null) + return ii.intValue(); + } + return 0; + } // getLine + + /** + * Get BPartner + * @return C_BPartner_ID + */ + public int getC_BPartner_ID() + { + if (m_C_BPartner_ID == -1) + { + int index = p_po.get_ColumnIndex("C_BPartner_ID"); + if (index != -1) + { + Integer ii = (Integer)p_po.get_Value(index); + if (ii != null) + m_C_BPartner_ID = ii.intValue(); + } + if (m_C_BPartner_ID <= 0) + m_C_BPartner_ID = m_doc.getC_BPartner_ID(); + } + return m_C_BPartner_ID; + } // getC_BPartner_ID + + /** + * Set C_BPartner_ID + * @param C_BPartner_ID id + */ + protected void setC_BPartner_ID (int C_BPartner_ID) + { + m_C_BPartner_ID = C_BPartner_ID; + } // setC_BPartner_ID + + + /** + * Get C_BPartner_Location_ID + * @return BPartner Location + */ + public int getC_BPartner_Location_ID() + { + int index = p_po.get_ColumnIndex("C_BPartner_Location_ID"); + if (index != -1) + { + Integer ii = (Integer)p_po.get_Value(index); + if (ii != null) + return ii.intValue(); + } + return m_doc.getC_BPartner_Location_ID(); + } // getC_BPartner_Location_ID + + /** + * Get TrxOrg + * @return AD_OrgTrx_ID + */ + public int getAD_OrgTrx_ID() + { + int index = p_po.get_ColumnIndex("AD_OrgTrx_ID"); + if (index != -1) + { + Integer ii = (Integer)p_po.get_Value(index); + if (ii != null) + return ii.intValue(); + } + return 0; + } // getAD_OrgTrx_ID + + /** + * Get SalesRegion. + * - get Sales Region from BPartner + * @return C_SalesRegion_ID + */ + public int getC_SalesRegion_ID() + { + if (m_C_SalesRegion_ID == -1) // never tried + { + if (getC_BPartner_Location_ID() != 0) + // && m_acctSchema.isAcctSchemaElement(MAcctSchemaElement.ELEMENTTYPE_SalesRegion)) + { + String sql = "SELECT COALESCE(C_SalesRegion_ID,0) FROM C_BPartner_Location WHERE C_BPartner_Location_ID=?"; + m_C_SalesRegion_ID = DB.getSQLValue (null, + sql, getC_BPartner_Location_ID()); + log.fine("C_SalesRegion_ID=" + m_C_SalesRegion_ID + " (from BPL)" ); + if (m_C_SalesRegion_ID == 0) + m_C_SalesRegion_ID = -2; // don't try again + } + else + m_C_SalesRegion_ID = -2; // don't try again + } + if (m_C_SalesRegion_ID < 0) // invalid + return 0; + return m_C_SalesRegion_ID; + } // getC_SalesRegion_ID + + /** + * Get Project + * @return C_Project_ID + */ + public int getC_Project_ID() + { + int index = p_po.get_ColumnIndex("C_Project_ID"); + if (index != -1) + { + Integer ii = (Integer)p_po.get_Value(index); + if (ii != null) + return ii.intValue(); + } + return 0; + } // getC_Project_ID + + /** + * Get Campaign + * @return C_Campaign_ID + */ + public int getC_Campaign_ID() + { + int index = p_po.get_ColumnIndex("C_Campaign_ID"); + if (index != -1) + { + Integer ii = (Integer)p_po.get_Value(index); + if (ii != null) + return ii.intValue(); + } + return 0; + } // getC_Campaign_ID + + /** + * Get Activity + * @return C_Activity_ID + */ + public int getC_Activity_ID() + { + int index = p_po.get_ColumnIndex("C_Activity_ID"); + if (index != -1) + { + Integer ii = (Integer)p_po.get_Value(index); + if (ii != null) + return ii.intValue(); + } + return 0; + } // getC_Activity_ID + + /** + * Get User 1 + * @return user defined 1 + */ + public int getUser1_ID() + { + int index = p_po.get_ColumnIndex("User1_ID"); + if (index != -1) + { + Integer ii = (Integer)p_po.get_Value(index); + if (ii != null) + return ii.intValue(); + } + return 0; + } // getUser1_ID + + /** + * Get User 2 + * @return user defined 2 + */ + public int getUser2_ID() + { + int index = p_po.get_ColumnIndex("User2_ID"); + if (index != -1) + { + Integer ii = (Integer)p_po.get_Value(index); + if (ii != null) + return ii.intValue(); + } + return 0; + } // getUser2_ID + + /** + * String representation + * @return String + */ + public String toString() + { + StringBuffer sb = new StringBuffer("DocLine=["); + sb.append(p_po.get_ID()); + if (getDescription() != null) + sb.append(",").append(getDescription()); + if (getM_Product_ID() != 0) + sb.append(",M_Product_ID=").append(getM_Product_ID()); + sb.append(",Qty=").append(m_qty) + .append(",Amt=").append(getAmtSource()) + .append("]"); + return sb.toString(); + } // toString + +} // DocumentLine diff --git a/serverRoot/src/main/server/org/compiere/acct/DocLine_Allocation.java b/serverRoot/src/main/server/org/compiere/acct/DocLine_Allocation.java new file mode 100644 index 0000000000..3005fb2c2f --- /dev/null +++ b/serverRoot/src/main/server/org/compiere/acct/DocLine_Allocation.java @@ -0,0 +1,142 @@ +/****************************************************************************** + * 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.acct; + +import java.math.*; +import org.compiere.model.*; +import org.compiere.util.*; + +/** + * Allocation Line + * + * @author Jorg Janke + * @version $Id: DocLine_Allocation.java,v 1.2 2006/07/30 00:53:33 jjanke Exp $ + */ +public class DocLine_Allocation extends DocLine +{ + /** + * DocLine_Allocation + * @param line allocation line + * @param doc header + */ + public DocLine_Allocation (MAllocationLine line, Doc doc) + { + super (line, doc); + m_C_Payment_ID = line.getC_Payment_ID(); + m_C_CashLine_ID = line.getC_CashLine_ID(); + m_C_Invoice_ID = line.getC_Invoice_ID(); + m_C_Order_ID = line.getC_Order_ID(); + // + setAmount(line.getAmount()); + m_DiscountAmt = line.getDiscountAmt(); + m_WriteOffAmt = line.getWriteOffAmt(); + m_OverUnderAmt = line.getOverUnderAmt(); + } // DocLine_Allocation + + private int m_C_Invoice_ID; + private int m_C_Payment_ID; + private int m_C_CashLine_ID; + private int m_C_Order_ID; + private BigDecimal m_DiscountAmt; + private BigDecimal m_WriteOffAmt; + private BigDecimal m_OverUnderAmt; + + + /** + * Get Invoice C_Currency_ID + * @return 0 if no invoice -1 if not found + */ + public int getInvoiceC_Currency_ID() + { + if (m_C_Invoice_ID == 0) + return 0; + String sql = "SELECT C_Currency_ID " + + "FROM C_Invoice " + + "WHERE C_Invoice_ID=?"; + return DB.getSQLValue(null, sql, m_C_Invoice_ID); + } // getInvoiceC_Currency_ID + + /** + * String Representation + * @return info + */ + public String toString () + { + StringBuffer sb = new StringBuffer ("DocLine_Allocation["); + sb.append(get_ID()) + .append(",Amt=").append(getAmtSource()) + .append(",Discount=").append(getDiscountAmt()) + .append(",WriteOff=").append(getWriteOffAmt()) + .append(",OverUnderAmt=").append(getOverUnderAmt()) + .append(" - C_Payment_ID=").append(m_C_Payment_ID) + .append(",C_CashLine_ID=").append(m_C_CashLine_ID) + .append(",C_Invoice_ID=").append(m_C_Invoice_ID) + .append("]"); + return sb.toString (); + } // toString + + + /** + * @return Returns the c_Order_ID. + */ + public int getC_Order_ID () + { + return m_C_Order_ID; + } + /** + * @return Returns the discountAmt. + */ + public BigDecimal getDiscountAmt () + { + return m_DiscountAmt; + } + /** + * @return Returns the overUnderAmt. + */ + public BigDecimal getOverUnderAmt () + { + return m_OverUnderAmt; + } + /** + * @return Returns the writeOffAmt. + */ + public BigDecimal getWriteOffAmt () + { + return m_WriteOffAmt; + } + /** + * @return Returns the c_CashLine_ID. + */ + public int getC_CashLine_ID () + { + return m_C_CashLine_ID; + } + /** + * @return Returns the c_Invoice_ID. + */ + public int getC_Invoice_ID () + { + return m_C_Invoice_ID; + } + /** + * @return Returns the c_Payment_ID. + */ + public int getC_Payment_ID () + { + return m_C_Payment_ID; + } +} // DocLine_Allocation diff --git a/serverRoot/src/main/server/org/compiere/acct/DocLine_Bank.java b/serverRoot/src/main/server/org/compiere/acct/DocLine_Bank.java new file mode 100644 index 0000000000..112a24997b --- /dev/null +++ b/serverRoot/src/main/server/org/compiere/acct/DocLine_Bank.java @@ -0,0 +1,122 @@ +/****************************************************************************** + * 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.acct; + +import java.math.*; +import org.compiere.model.*; +import org.compiere.util.*; +//import org.compiere.model.*; + +/** + * Bank Statement Line + * + * @author Jorg Janke + * @version $Id: DocLine_Bank.java,v 1.2 2006/07/30 00:53:33 jjanke Exp $ + */ +public class DocLine_Bank extends DocLine +{ + /** + * Constructor + * @param line statement line + * @param doc header + */ + public DocLine_Bank (MBankStatementLine line, Doc_Bank doc) + { + super (line, doc); + m_C_Payment_ID = line.getC_Payment_ID(); + m_IsReversal = line.isReversal(); + // + m_StmtAmt = line.getStmtAmt(); + m_InterestAmt = line.getInterestAmt(); + m_TrxAmt = line.getTrxAmt(); + // + setDateDoc(line.getValutaDate()); + setC_BPartner_ID(line.getC_BPartner_ID()); + } // DocLine_Bank + + /** Reversal Flag */ + private boolean m_IsReversal = false; + /** Payment */ + private int m_C_Payment_ID = 0; + + private BigDecimal m_TrxAmt = Env.ZERO; + private BigDecimal m_StmtAmt = Env.ZERO; + private BigDecimal m_InterestAmt = Env.ZERO; + + /** + * Get Payment + * @return C_Paymnet_ID + */ + public int getC_Payment_ID() + { + return m_C_Payment_ID; + } // getC_Payment_ID + + /** + * Get AD_Org_ID + * @param payment if true get Org from payment + * @return org + */ + public int getAD_Org_ID (boolean payment) + { + if (payment && getC_Payment_ID() != 0) + { + String sql = "SELECT AD_Org_ID FROM C_Payment WHERE C_Payment_ID=?"; + int id = DB.getSQLValue(null, sql, getC_Payment_ID()); + if (id > 0) + return id; + } + return super.getAD_Org_ID(); + } // getAD_Org_ID + + /** + * Is Reversal + * @return true if reversal + */ + public boolean isReversal() + { + return m_IsReversal; + } // isReversal + + /** + * Get Interest + * @return InterestAmount + */ + public BigDecimal getInterestAmt() + { + return m_InterestAmt; + } // getInterestAmt + + /** + * Get Statement + * @return Starement Amount + */ + public BigDecimal getStmtAmt() + { + return m_StmtAmt; + } // getStrmtAmt + + /** + * Get Transaction + * @return transaction amount + */ + public BigDecimal getTrxAmt() + { + return m_TrxAmt; + } // getTrxAmt + +} // DocLine_Bank diff --git a/serverRoot/src/main/server/org/compiere/acct/DocLine_Cash.java b/serverRoot/src/main/server/org/compiere/acct/DocLine_Cash.java new file mode 100644 index 0000000000..d25c260d48 --- /dev/null +++ b/serverRoot/src/main/server/org/compiere/acct/DocLine_Cash.java @@ -0,0 +1,138 @@ +/****************************************************************************** + * 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.acct; + +import java.math.*; + +import org.compiere.model.*; +import org.compiere.util.*; + +/** + * Cash Journal Line + * + * @author Jorg Janke + * @version $Id: DocLine_Cash.java,v 1.3 2006/07/30 00:53:33 jjanke Exp $ + */ +public class DocLine_Cash extends DocLine +{ + /** + * Constructor + * @param line cash line + * @param doc header + */ + public DocLine_Cash (MCashLine line, Doc_Cash doc) + { + super (line, doc); + m_CashType = line.getCashType(); + m_C_BankAccount_ID = line.getC_BankAccount_ID(); + m_C_Invoice_ID = line.getC_Invoice_ID(); + // + if (m_C_Invoice_ID != 0) + { + MInvoice invoice = MInvoice.get(line.getCtx(), m_C_Invoice_ID); + setC_BPartner_ID(invoice.getC_BPartner_ID()); + } + + // + m_Amount = line.getAmount(); + m_DiscountAmt = line.getDiscountAmt(); + m_WriteOffAmt = line.getWriteOffAmt(); + setAmount(m_Amount); + + + } // DocLine_Cash + + /** Cash Type */ + private String m_CashType = ""; + + // AD_Reference_ID=217 + /** Charge - C */ + public static final String CASHTYPE_CHARGE = "C"; + /** Difference - D */ + public static final String CASHTYPE_DIFFERENCE = "D"; + /** Expense - E */ + public static final String CASHTYPE_EXPENSE = "E"; + /** Onvoice - I */ + public static final String CASHTYPE_INVOICE = "I"; + /** Receipt - R */ + public static final String CASHTYPE_RECEIPT = "R"; + /** Transfer - T */ + public static final String CASHTYPE_TRANSFER = "T"; + + // References + private int m_C_BankAccount_ID = 0; + private int m_C_Invoice_ID = 0; + + // Amounts + private BigDecimal m_Amount = Env.ZERO; + private BigDecimal m_DiscountAmt = Env.ZERO; + private BigDecimal m_WriteOffAmt = Env.ZERO; + + + /** + * Get Cash Type + * @return cash type + */ + public String getCashType() + { + return m_CashType; + } // getCashType + + /** + * Get Bank Account + * @return Bank Account + */ + public int getC_BankAccount_ID() + { + return m_C_BankAccount_ID; + } // getC_BankAccount_ID + + /** + * Get Invoice + * @return C_Invoice_ID + */ + public int getC_Invoice_ID() + { + return m_C_Invoice_ID; + } // getC_Invoice_ID + + /** + * Get Amount + * @return Payment Amount + */ + public BigDecimal getAmount() + { + return m_Amount; + } + /** + * Get Discount + * @return Discount Amount + */ + public BigDecimal getDiscountAmt() + { + return m_DiscountAmt; + } + /** + * Get WriteOff + * @return Write-Off Amount + */ + public BigDecimal getWriteOffAmt() + { + return m_WriteOffAmt; + } + +} // DocLine_Cash diff --git a/serverRoot/src/main/server/org/compiere/acct/DocTax.java b/serverRoot/src/main/server/org/compiere/acct/DocTax.java new file mode 100644 index 0000000000..3a3725f325 --- /dev/null +++ b/serverRoot/src/main/server/org/compiere/acct/DocTax.java @@ -0,0 +1,240 @@ +/****************************************************************************** + * 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.acct; + +import java.math.*; +import java.sql.*; +import org.compiere.model.*; +import java.util.logging.*; +import org.compiere.util.*; + +/** + * Document Tax Line + * + * @author Jorg Janke + * @version $Id: DocTax.java,v 1.3 2006/07/30 00:53:33 jjanke Exp $ + */ +public final class DocTax +{ + /** + * Create Tax + * @param C_Tax_ID tax + * @param name name + * @param rate rate + * @param taxBaseAmt tax base amount + * @param amount amount + * @param salesTax sales tax flag + */ + public DocTax (int C_Tax_ID, String name, BigDecimal rate, + BigDecimal taxBaseAmt, BigDecimal amount, boolean salesTax) + { + m_C_Tax_ID = C_Tax_ID; + m_name = name; + m_rate = rate; + m_amount = amount; + m_salesTax = salesTax; + } // DocTax + + /** Tax ID */ + private int m_C_Tax_ID = 0; + /** Amount */ + private BigDecimal m_amount = null; + /** Tax Rate */ + private BigDecimal m_rate = null; + /** Name */ + private String m_name = null; + /** Base Tax Amt */ + private BigDecimal m_taxBaseAmt = null; + /** Included Tax */ + private BigDecimal m_includedTax = Env.ZERO; + /** Sales Tax */ + private boolean m_salesTax = false; + + /** Logger */ + private static CLogger log = CLogger.getCLogger(DocTax.class); + + + /** Tax Due Acct */ + public static final int ACCTTYPE_TaxDue = 0; + /** Tax Liability */ + public static final int ACCTTYPE_TaxLiability = 1; + /** Tax Credit */ + public static final int ACCTTYPE_TaxCredit = 2; + /** Tax Receivables */ + public static final int ACCTTYPE_TaxReceivables = 3; + /** Tax Expense */ + public static final int ACCTTYPE_TaxExpense = 4; + + /** + * Get Account + * @param AcctType see ACCTTYPE_* + * @param as account schema + * @return Account + */ + public MAccount getAccount (int AcctType, MAcctSchema as) + { + if (AcctType < 0 || AcctType > 4) + return null; + // + String sql = "SELECT T_Due_Acct, T_Liability_Acct, T_Credit_Acct, T_Receivables_Acct, T_Expense_Acct " + + "FROM C_Tax_Acct WHERE C_Tax_ID=? AND C_AcctSchema_ID=?"; + int validCombination_ID = 0; + try + { + PreparedStatement pstmt = DB.prepareStatement(sql, null); + pstmt.setInt(1, m_C_Tax_ID); + pstmt.setInt(2, as.getC_AcctSchema_ID()); + ResultSet rs = pstmt.executeQuery(); + if (rs.next()) + validCombination_ID = rs.getInt(AcctType+1); // 1..5 + rs.close(); + pstmt.close(); + } + catch (SQLException e) + { + log.log(Level.SEVERE, sql, e); + } + if (validCombination_ID == 0) + return null; + return MAccount.get(as.getCtx(), validCombination_ID); + } // getAccount + + /** + * Get Amount + * @return gross amount + */ + public BigDecimal getAmount() + { + return m_amount; + } + + /** + * Get Base Amount + * @return net amount + */ + public BigDecimal getTaxBaseAmt() + { + return m_taxBaseAmt; + } + + /** + * Get Rate + * @return tax rate in percent + */ + public BigDecimal getRate() + { + return m_rate; + } + + /** + * Get Name of Tax + * @return name + */ + public String getName() + { + return m_name; + } + + /** + * Get C_Tax_ID + * @return tax id + */ + public int getC_Tax_ID() + { + return m_C_Tax_ID; + } // getC_Tax_ID + + /** + * Get Description (Tax Name and Base Amount) + * @return tax anme and base amount + */ + public String getDescription() + { + return m_name + " " + m_taxBaseAmt.toString(); + } // getDescription + + /** + * Add to Included Tax + * @param amt amount + */ + public void addIncludedTax (BigDecimal amt) + { + m_includedTax = m_includedTax.add(amt); + } // addIncludedTax + + /** + * Get Included Tax + * @return tax amount + */ + public BigDecimal getIncludedTax() + { + return m_includedTax; + } // getIncludedTax + + /** + * Get Included Tax Difference + * @return tax ampunt - included amount + */ + public BigDecimal getIncludedTaxDifference() + { + return m_amount.subtract(m_includedTax); + } // getIncludedTaxDifference + + /** + * Included Tax differs from tax amount + * @return true if difference + */ + public boolean isIncludedTaxDifference() + { + return Env.ZERO.compareTo(getIncludedTaxDifference()) != 0; + } // isIncludedTaxDifference + + /** + * Get AP Tax Type + * @return AP tax type (Credit or Expense) + */ + public int getAPTaxType() + { + if (isSalesTax()) + return ACCTTYPE_TaxExpense; + return ACCTTYPE_TaxCredit; + } // getAPTaxAcctType + + /** + * Is Sales Tax + * @return sales tax + */ + public boolean isSalesTax() + { + return m_salesTax; + } // isSalesTax + + + /** + * Return String representation + * @return tax anme and base amount + */ + public String toString() + { + StringBuffer sb = new StringBuffer("Tax=("); + sb.append(m_name); + sb.append(" Amt=").append(m_amount); + sb.append(")"); + return sb.toString(); + } // toString + +} // DocTax diff --git a/serverRoot/src/main/server/org/compiere/acct/Doc_Allocation.java b/serverRoot/src/main/server/org/compiere/acct/Doc_Allocation.java new file mode 100644 index 0000000000..4f17ae22bf --- /dev/null +++ b/serverRoot/src/main/server/org/compiere/acct/Doc_Allocation.java @@ -0,0 +1,892 @@ +/****************************************************************************** + * 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.acct; + +import java.math.*; +import java.sql.*; +import java.util.*; +import java.util.logging.*; +import org.apache.taglibs.standard.lang.jstl.*; +import org.compiere.model.*; +import org.compiere.util.*; + +/** + * Post Allocation Documents. + *
+ *  Table:              C_AllocationHdr
+ *  Document Types:     CMA
+ *  
+ * @author Jorg Janke + * @version $Id: Doc_Allocation.java,v 1.6 2006/07/30 00:53:33 jjanke Exp $ + */ +public class Doc_Allocation extends Doc +{ + /** + * Constructor + * @param ass accounting schemata + * @param rs record + * @param trxName trx + */ + protected Doc_Allocation (MAcctSchema[] ass, ResultSet rs, String trxName) + { + super (ass, MAllocationHdr.class, rs, DOCTYPE_Allocation, trxName); + } // Doc_Allocation + + /** Tolearance G&L */ + private static final BigDecimal TOLERANCE = new BigDecimal (0.02); + /** Facts */ + private ArrayList m_facts = null; + + + /** + * Load Specific Document Details + * @return error message or null + */ + protected String loadDocumentDetails () + { + MAllocationHdr alloc = (MAllocationHdr)getPO(); + setDateDoc(alloc.getDateTrx()); + // Contained Objects + p_lines = loadLines(alloc); + return null; + } // loadDocumentDetails + + /** + * Load Invoice Line + * @param alloc header + * @return DocLine Array + */ + private DocLine[] loadLines(MAllocationHdr alloc) + { + ArrayList list = new ArrayList(); + MAllocationLine[] lines = alloc.getLines(false); + for (int i = 0; i < lines.length; i++) + { + MAllocationLine line = lines[i]; + DocLine_Allocation docLine = new DocLine_Allocation(line, this); + + // Get Payment Conversion Rate + if (line.getC_Payment_ID() != 0) + { + MPayment payment = new MPayment (getCtx(), line.getC_Payment_ID(), getTrxName()); + int C_ConversionType_ID = payment.getC_ConversionType_ID(); + docLine.setC_ConversionType_ID(C_ConversionType_ID); + } + // + log.fine(docLine.toString()); + list.add (docLine); + } + + // Return Array + DocLine[] dls = new DocLine[list.size()]; + list.toArray(dls); + return dls; + } // loadLines + + + /************************************************************************** + * Get Source Currency Balance - subtracts line and tax amounts from total - no rounding + * @return positive amount, if total invoice is bigger than lines + */ + public BigDecimal getBalance() + { + BigDecimal retValue = Env.ZERO; + return retValue; + } // getBalance + + /** + * Create Facts (the accounting logic) for + * CMA. + *
+	 *  AR_Invoice_Payment
+	 *      UnAllocatedCash DR
+	 *      or C_Prepayment
+	 *      DiscountExp     DR
+	 *      WriteOff        DR
+	 *      Receivables             CR
+	 *  AR_Invoice_Cash
+	 *      CashTransfer    DR
+	 *      DiscountExp     DR
+	 *      WriteOff        DR
+	 *      Receivables             CR
+	 * 
+	 *  AP_Invoice_Payment
+	 *      Liability       DR
+	 *      DiscountRev             CR
+	 *      WriteOff                CR
+	 *      PaymentSelect           CR
+	 *      or V_Prepayment
+	 *  AP_Invoice_Cash
+	 *      Liability       DR
+	 *      DiscountRev             CR
+	 *      WriteOff                CR
+	 *      CashTransfer            CR
+	 *  CashBankTransfer
+	 *      -
+	 *  ==============================
+	 *  Realized Gain & Loss
+	 * 		AR/AP			DR		CR
+	 * 		Realized G/L	DR		CR
+	 * 
+	 *
+	 *  
+ * Tax needs to be corrected for discount & write-off; + * Currency gain & loss is realized here. + * @param as accounting schema + * @return Fact + */ + public ArrayList createFacts (MAcctSchema as) + { + m_facts = new ArrayList(); + + // create Fact Header + Fact fact = new Fact(this, as, Fact.POST_Actual); + + for (int i = 0; i < p_lines.length; i++) + { + DocLine_Allocation line = (DocLine_Allocation)p_lines[i]; + setC_BPartner_ID(line.getC_BPartner_ID()); + + // CashBankTransfer - all references null and Discount/WriteOff = 0 + if (line.getC_Payment_ID() != 0 + && line.getC_Invoice_ID() == 0 && line.getC_Order_ID() == 0 + && line.getC_CashLine_ID() == 0 && line.getC_BPartner_ID() == 0 + && Env.ZERO.compareTo(line.getDiscountAmt()) == 0 + && Env.ZERO.compareTo(line.getWriteOffAmt()) == 0) + continue; + + // Receivables/Liability Amt + BigDecimal allocationSource = line.getAmtSource() + .add(line.getDiscountAmt()) + .add(line.getWriteOffAmt()); + BigDecimal allocationAccounted = null; // AR/AP balance corrected + + FactLine fl = null; + MAccount bpAcct = null; // Liability/Receivables + // + MPayment payment = null; + if (line.getC_Payment_ID() != 0) + payment = new MPayment (getCtx(), line.getC_Payment_ID(), getTrxName()); + MInvoice invoice = null; + if (line.getC_Invoice_ID() != 0) + invoice = new MInvoice (getCtx(), line.getC_Invoice_ID(), null); + + // No Invoice + if (invoice == null) + { + // Payment Only + if (line.getC_Invoice_ID() == 0 && line.getC_Payment_ID() != 0) + { + fl = fact.createLine (line, getPaymentAcct(as, line.getC_Payment_ID()), + getC_Currency_ID(), line.getAmtSource(), null); + if (fl != null && payment != null) + fl.setAD_Org_ID(payment.getAD_Org_ID()); + } + else + { + p_Error = "Cannot determine SO/PO"; + log.log(Level.SEVERE, p_Error); + return null; + } + } + // Sales Invoice + else if (invoice.isSOTrx()) + { + // Payment/Cash DR + if (line.getC_Payment_ID() != 0) + { + fl = fact.createLine (line, getPaymentAcct(as, line.getC_Payment_ID()), + getC_Currency_ID(), line.getAmtSource(), null); + if (fl != null && payment != null) + fl.setAD_Org_ID(payment.getAD_Org_ID()); + } + else if (line.getC_CashLine_ID() != 0) + { + fl = fact.createLine (line, getCashAcct(as, line.getC_CashLine_ID()), + getC_Currency_ID(), line.getAmtSource(), null); + MCashLine cashLine = new MCashLine (getCtx(), line.getC_CashLine_ID(), getTrxName()); + if (fl != null && cashLine.get_ID() != 0) + fl.setAD_Org_ID(cashLine.getAD_Org_ID()); + } + // Discount DR + if (Env.ZERO.compareTo(line.getDiscountAmt()) != 0) + { + fl = fact.createLine (line, getAccount(Doc.ACCTTYPE_DiscountExp, as), + getC_Currency_ID(), line.getDiscountAmt(), null); + if (fl != null && payment != null) + fl.setAD_Org_ID(payment.getAD_Org_ID()); + } + // Write off DR + if (Env.ZERO.compareTo(line.getWriteOffAmt()) != 0) + { + fl = fact.createLine (line, getAccount(Doc.ACCTTYPE_WriteOff, as), + getC_Currency_ID(), line.getWriteOffAmt(), null); + if (fl != null && payment != null) + fl.setAD_Org_ID(payment.getAD_Org_ID()); + } + + // AR Invoice Amount CR + if (as.isAccrual()) + { + bpAcct = getAccount(Doc.ACCTTYPE_C_Receivable, as); + fl = fact.createLine (line, bpAcct, + getC_Currency_ID(), null, allocationSource); // payment currency + if (fl != null) + allocationAccounted = fl.getAcctBalance().negate(); + if (fl != null && invoice != null) + fl.setAD_Org_ID(invoice.getAD_Org_ID()); + } + else // Cash Based + { + allocationAccounted = createCashBasedAcct (as, fact, + invoice, allocationSource); + } + } + // Purchase Invoice + else + { + allocationSource = allocationSource.negate(); // allocation is negative + // AP Invoice Amount DR + if (as.isAccrual()) + { + bpAcct = getAccount(Doc.ACCTTYPE_V_Liability, as); + fl = fact.createLine (line, bpAcct, + getC_Currency_ID(), allocationSource, null); // payment currency + if (fl != null) + allocationAccounted = fl.getAcctBalance(); + if (fl != null && invoice != null) + fl.setAD_Org_ID(invoice.getAD_Org_ID()); + } + else // Cash Based + { + allocationAccounted = createCashBasedAcct (as, fact, + invoice, allocationSource); + } + + // Discount CR + if (Env.ZERO.compareTo(line.getDiscountAmt()) != 0) + { + fl = fact.createLine (line, getAccount(Doc.ACCTTYPE_DiscountRev, as), + getC_Currency_ID(), null, line.getDiscountAmt().negate()); + if (fl != null && payment != null) + fl.setAD_Org_ID(payment.getAD_Org_ID()); + } + // Write off CR + if (Env.ZERO.compareTo(line.getWriteOffAmt()) != 0) + { + fl = fact.createLine (line, getAccount(Doc.ACCTTYPE_WriteOff, as), + getC_Currency_ID(), null, line.getWriteOffAmt().negate()); + if (fl != null && payment != null) + fl.setAD_Org_ID(payment.getAD_Org_ID()); + } + // Payment/Cash CR + if (line.getC_Payment_ID() != 0) + { + fl = fact.createLine (line, getPaymentAcct(as, line.getC_Payment_ID()), + getC_Currency_ID(), null, line.getAmtSource().negate()); + if (fl != null && payment != null) + fl.setAD_Org_ID(payment.getAD_Org_ID()); + } + else if (line.getC_CashLine_ID() != 0) + { + fl = fact.createLine (line, getCashAcct(as, line.getC_CashLine_ID()), + getC_Currency_ID(), null, line.getAmtSource().negate()); + MCashLine cashLine = new MCashLine (getCtx(), line.getC_CashLine_ID(), getTrxName()); + if (fl != null && cashLine.get_ID() != 0) + fl.setAD_Org_ID(cashLine.getAD_Org_ID()); + } + } + + // VAT Tax Correction + if (invoice != null && as.isTaxCorrection()) + { + BigDecimal taxCorrectionAmt = Env.ZERO; + if (as.isTaxCorrectionDiscount()) + taxCorrectionAmt = line.getDiscountAmt(); + if (as.isTaxCorrectionWriteOff()) + taxCorrectionAmt = taxCorrectionAmt.add(line.getWriteOffAmt()); + // + if (taxCorrectionAmt.signum() != 0) + { + if (!createTaxCorrection(as, fact, line, + getAccount(invoice.isSOTrx() ? Doc.ACCTTYPE_DiscountExp : Doc.ACCTTYPE_DiscountRev, as), + getAccount(Doc.ACCTTYPE_WriteOff, as))) + { + p_Error = "Cannot create Tax correction"; + return null; + } + } + } + + // Realized Gain & Loss + if (invoice != null + && (getC_Currency_ID() != as.getC_Currency_ID() // payment allocation in foreign currency + || getC_Currency_ID() != line.getInvoiceC_Currency_ID())) // allocation <> invoice currency + { + p_Error = createRealizedGainLoss (as, fact, bpAcct, invoice, + allocationSource, allocationAccounted); + if (p_Error != null) + return null; + } + + } // for all lines + + // reset line info + setC_BPartner_ID(0); + // + m_facts.add(fact); + return m_facts; + } // createFact + + /** + * Create Cash Based Acct + * @param as accounting schema + * @param fact fact + * @param invoice invoice + * @param allocationSource allocation amount (incl discount, writeoff) + * @return Accounted Amt + */ + private BigDecimal createCashBasedAcct (MAcctSchema as, Fact fact, MInvoice invoice, + BigDecimal allocationSource) + { + BigDecimal allocationAccounted = Env.ZERO; + // Multiplier + double percent = invoice.getGrandTotal().doubleValue() / allocationSource.doubleValue(); + if (percent > 0.99 && percent < 1.01) + percent = 1.0; + log.config("Multiplier=" + percent + " - GrandTotal=" + invoice.getGrandTotal() + + " - Allocation Source=" + allocationSource); + + // Get Invoice Postings + Doc_Invoice docInvoice = (Doc_Invoice)Doc.get(new MAcctSchema[]{as}, + MInvoice.Table_ID, invoice.getC_Invoice_ID(), getTrxName()); + docInvoice.loadDocumentDetails(); + allocationAccounted = docInvoice.createFactCash(as, fact, new BigDecimal(percent)); + log.config("Allocation Accounted=" + allocationAccounted); + + // Cash Based Commitment Release + if (as.isCreateCommitment() && !invoice.isSOTrx()) + { + MInvoiceLine[] lines = invoice.getLines(); + for (int i = 0; i < lines.length; i++) + { + Fact factC = Doc_Order.getCommitmentRelease(as, this, + lines[i].getQtyInvoiced(), lines[i].getC_InvoiceLine_ID(), new BigDecimal(percent)); + if (factC == null) + return null; + m_facts.add(factC); + } + } // Commitment + + return allocationAccounted; + } // createCashBasedAcct + + + /** + * Get Payment (Unallocated Payment or Payment Selection) Acct of Bank Account + * @param as accounting schema + * @param C_Payment_ID payment + * @return acct + */ + private MAccount getPaymentAcct (MAcctSchema as, int C_Payment_ID) + { + setC_BankAccount_ID(0); + // Doc.ACCTTYPE_UnallocatedCash (AR) or C_Prepayment + // or Doc.ACCTTYPE_PaymentSelect (AP) or V_Prepayment + int accountType = Doc.ACCTTYPE_UnallocatedCash; + // + String sql = "SELECT p.C_BankAccount_ID, d.DocBaseType, p.IsReceipt, p.IsPrepayment " + + "FROM C_Payment p INNER JOIN C_DocType d ON (p.C_DocType_ID=d.C_DocType_ID) " + + "WHERE C_Payment_ID=?"; + PreparedStatement pstmt = null; + try + { + pstmt = DB.prepareStatement (sql, getTrxName()); + pstmt.setInt (1, C_Payment_ID); + ResultSet rs = pstmt.executeQuery (); + if (rs.next ()) + { + setC_BankAccount_ID(rs.getInt(1)); + if (DOCTYPE_APPayment.equals(rs.getString(2))) + accountType = Doc.ACCTTYPE_PaymentSelect; + // Prepayment + if ("Y".equals(rs.getString(4))) // Prepayment + { + if ("Y".equals(rs.getString(3))) // Receipt + accountType = Doc.ACCTTYPE_C_Prepayment; + else + accountType = Doc.ACCTTYPE_V_Prepayment; + } + } + rs.close (); + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + log.log(Level.SEVERE, sql, e); + } + try + { + if (pstmt != null) + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + pstmt = null; + } + + // + if (getC_BankAccount_ID() <= 0) + { + log.log(Level.SEVERE, "NONE for C_Payment_ID=" + C_Payment_ID); + return null; + } + return getAccount (accountType, as); + } // getPaymentAcct + + /** + * Get Cash (Transfer) Acct of CashBook + * @param as accounting schema + * @param C_CashLine_ID + * @return acct + */ + private MAccount getCashAcct (MAcctSchema as, int C_CashLine_ID) + { + String sql = "SELECT c.C_CashBook_ID " + + "FROM C_Cash c, C_CashLine cl " + + "WHERE c.C_Cash_ID=cl.C_Cash_ID AND cl.C_CashLine_ID=?"; + setC_CashBook_ID(DB.getSQLValue(null, sql, C_CashLine_ID)); + if (getC_CashBook_ID() <= 0) + { + log.log(Level.SEVERE, "NONE for C_CashLine_ID=" + C_CashLine_ID); + return null; + } + return getAccount(Doc.ACCTTYPE_CashTransfer, as); + } // getCashAcct + + + /************************************************************************** + * Create Realized Gain & Loss. + * Compares the Accounted Amount of the Invoice to the + * Accounted Amount of the Allocation + * @param as accounting schema + * @param fact fact + * @param acct account + * @param invoice invoice + * @param allocationSource source amt + * @param allocationAccounted acct amt + * @return Error Message or null if OK + */ + private String createRealizedGainLoss (MAcctSchema as, Fact fact, MAccount acct, + MInvoice invoice, BigDecimal allocationSource, BigDecimal allocationAccounted) + { + BigDecimal invoiceSource = null; + BigDecimal invoiceAccounted = null; + // + String sql = "SELECT " + + (invoice.isSOTrx() + ? "SUM(AmtSourceDr), SUM(AmtAcctDr)" // so + : "SUM(AmtSourceCr), SUM(AmtAcctCr)") // po + + " FROM Fact_Acct " + + "WHERE AD_Table_ID=318 AND Record_ID=?" // Invoice + + " AND C_AcctSchema_ID=?" + + " AND PostingType='A'"; + //AND C_Currency_ID=102 + PreparedStatement pstmt = null; + try + { + pstmt = DB.prepareStatement(sql, getTrxName()); + pstmt.setInt(1, invoice.getC_Invoice_ID()); + pstmt.setInt(2, as.getC_AcctSchema_ID()); + ResultSet rs = pstmt.executeQuery(); + if (rs.next()) + { + invoiceSource = rs.getBigDecimal(1); + invoiceAccounted = rs.getBigDecimal(2); + } + rs.close(); + pstmt.close(); + pstmt = null; + } + catch (Exception e) + { + log.log(Level.SEVERE, sql, e); + } + try + { + if (pstmt != null) + pstmt.close(); + pstmt = null; + } + catch (Exception e) + { + pstmt = null; + } + // Requires that Invoice is Posted + if (invoiceSource == null || invoiceAccounted == null) + return "Gain/Loss - Invoice not posted yet"; + // + String description = "Invoice=(" + invoice.getC_Currency_ID() + ")" + invoiceSource + "/" + invoiceAccounted + + " - Allocation=(" + getC_Currency_ID() + ")" + allocationSource + "/" + allocationAccounted; + log.fine(description); + // Allocation not Invoice Currency + if (getC_Currency_ID() != invoice.getC_Currency_ID()) + { + BigDecimal allocationSourceNew = MConversionRate.convert(getCtx(), + allocationSource, getC_Currency_ID(), + invoice.getC_Currency_ID(), getDateAcct(), + invoice.getC_ConversionType_ID(), invoice.getAD_Client_ID(), invoice.getAD_Org_ID()); + if (allocationSourceNew == null) + return "Gain/Loss - No Conversion from Allocation->Invoice"; + String d2 = "Allocation=(" + getC_Currency_ID() + ")" + allocationSource + + "->(" + invoice.getC_Currency_ID() + ")" + allocationSourceNew; + log.fine(d2); + description += " - " + d2; + allocationSource = allocationSourceNew; + } + + BigDecimal acctDifference = null; // gain is negative + // Full Payment in currency + if (allocationSource.compareTo(invoiceSource) == 0) + { + acctDifference = invoiceAccounted.subtract(allocationAccounted); // gain is negative + String d2 = "(full) = " + acctDifference; + log.fine(d2); + description += " - " + d2; + } + else // partial or MC + { + // percent of total payment + double multiplier = allocationSource.doubleValue() / invoiceSource.doubleValue(); + // Reduce Orig Invoice Accounted + invoiceAccounted = invoiceAccounted.multiply(new BigDecimal(multiplier)); + // Difference based on percentage of Orig Invoice + acctDifference = invoiceAccounted.subtract(allocationAccounted); // gain is negative + // ignore Tolerance + if (acctDifference.abs().compareTo(TOLERANCE) < 0) + acctDifference = Env.ZERO; + // Round + int precision = as.getStdPrecision(); + if (acctDifference.scale() > precision) + acctDifference = acctDifference.setScale(precision, BigDecimal.ROUND_HALF_UP); + String d2 = "(partial) = " + acctDifference + " - Multiplier=" + multiplier; + log.fine(d2); + description += " - " + d2; + } + + if (acctDifference.signum() == 0) + { + log.fine("No Difference"); + return null; + } + + MAccount gain = MAccount.get (as.getCtx(), as.getAcctSchemaDefault().getRealizedGain_Acct()); + MAccount loss = MAccount.get (as.getCtx(), as.getAcctSchemaDefault().getRealizedLoss_Acct()); + // + if (invoice.isSOTrx()) + { + FactLine fl = fact.createLine (null, loss, gain, + as.getC_Currency_ID(), acctDifference); + fl.setDescription(description); + fact.createLine (null, acct, + as.getC_Currency_ID(), acctDifference.negate()); + fl.setDescription(description); + } + else + { + fact.createLine (null, acct, + as.getC_Currency_ID(), acctDifference); + FactLine fl = fact.createLine (null, loss, gain, + as.getC_Currency_ID(), acctDifference.negate()); + } + return null; + } // createRealizedGainLoss + + + /************************************************************************** + * Create Tax Correction. + * Requirement: Adjust the tax amount, if you did not receive the full + * amount of the invoice (payment discount, write-off). + * Applies to many countries with VAT. + * Example: + * Invoice: Net $100 + Tax1 $15 + Tax2 $5 = Total $120 + * Payment: $115 (i.e. $5 underpayment) + * Tax Adjustment = Tax1 = 0.63 (15/120*5) Tax2 = 0.21 (5/120/5) + * + * @param as accounting schema + * @param fact fact + * @param line Allocation line + * @param DiscountAccount discount acct + * @param WriteOffAccoint write off acct + * @return true if created + */ + private boolean createTaxCorrection (MAcctSchema as, Fact fact, + DocLine_Allocation line, + MAccount DiscountAccount, MAccount WriteOffAccoint) + { + log.info (line.toString()); + BigDecimal discount = Env.ZERO; + if (as.isTaxCorrectionDiscount()) + discount = line.getDiscountAmt(); + BigDecimal writeOff = Env.ZERO; + if (as.isTaxCorrectionWriteOff()) + writeOff = line.getWriteOffAmt(); + + Doc_AllocationTax tax = new Doc_AllocationTax ( + DiscountAccount, discount, WriteOffAccoint, writeOff); + + // Get Source Amounts with account + String sql = "SELECT * " + + "FROM Fact_Acct " + + "WHERE AD_Table_ID=318 AND Record_ID=?" // Invoice + + " AND C_AcctSchema_ID=?" + + " AND Line_ID IS NULL"; // header lines like tax or total + PreparedStatement pstmt = null; + try + { + pstmt = DB.prepareStatement(sql, getTrxName()); + pstmt.setInt(1, line.getC_Invoice_ID()); + pstmt.setInt(2, as.getC_AcctSchema_ID()); + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) + tax.addInvoiceFact (new MFactAcct(getCtx(), rs, fact.get_TrxName())); + rs.close(); + pstmt.close(); + pstmt = null; + } + catch (Exception e) + { + log.log(Level.SEVERE, sql, e); + } + try + { + if (pstmt != null) + pstmt.close(); + pstmt = null; + } + catch (Exception e) + { + pstmt = null; + } + // Invoice Not posted + if (tax.getLineCount() == 0) + { + log.warning ("Invoice not posted yet - " + line); + return false; + } + // size = 1 if no tax + if (tax.getLineCount() < 2) + return true; + return tax.createEntries (as, fact, line); + + } // createTaxCorrection + +} // Doc_Allocation + +/** + * Allocation Document Tax Handing + * + * @author Jorg Janke + * @version $Id: Doc_Allocation.java,v 1.6 2006/07/30 00:53:33 jjanke Exp $ + */ +class Doc_AllocationTax +{ + /** + * Allocation Tax Adjustment + * @param DiscountAccount discount acct + * @param DiscountAmt discount amt + * @param WriteOffAccount write off acct + * @param WriteOffAmt write off amt + */ + public Doc_AllocationTax (MAccount DiscountAccount, BigDecimal DiscountAmt, + MAccount WriteOffAccount, BigDecimal WriteOffAmt) + { + m_DiscountAccount = DiscountAccount; + m_DiscountAmt = DiscountAmt; + m_WriteOffAccount = WriteOffAccount; + m_WriteOffAmt = WriteOffAmt; + } // Doc_AllocationTax + + private CLogger log = CLogger.getCLogger(getClass()); + + private MAccount m_DiscountAccount; + private BigDecimal m_DiscountAmt; + private MAccount m_WriteOffAccount; + private BigDecimal m_WriteOffAmt; + + private ArrayList m_facts = new ArrayList(); + private int m_totalIndex = 0; + + /** + * Add Invoice Fact Line + * @param fact fact line + */ + public void addInvoiceFact (MFactAcct fact) + { + m_facts.add(fact); + } // addInvoiceLine + + /** + * Get Line Count + * @return number of lines + */ + public int getLineCount() + { + return m_facts.size(); + } // getLineCount + + /** + * Create Accounting Entries + * @param as account schema + * @param fact fact to add lines + * @param line line + * @return true if created + */ + public boolean createEntries (MAcctSchema as, Fact fact, DocLine line) + { + // get total index (the Receivables/Liabilities line) + BigDecimal total = Env.ZERO; + for (int i = 0; i < m_facts.size(); i++) + { + MFactAcct factAcct = (MFactAcct)m_facts.get(i); + if (factAcct.getAmtSourceDr().compareTo(total) > 0) + { + total = factAcct.getAmtSourceDr(); + m_totalIndex = i; + } + if (factAcct.getAmtSourceCr().compareTo(total) > 0) + { + total = factAcct.getAmtSourceCr(); + m_totalIndex = i; + } + } + + MFactAcct factAcct = (MFactAcct)m_facts.get(m_totalIndex); + log.info ("Total Invoice = " + total + " - " + factAcct); + int precision = as.getStdPrecision(); + for (int i = 0; i < m_facts.size(); i++) + { + // No Tax Line + if (i == m_totalIndex) + continue; + + factAcct = (MFactAcct)m_facts.get(i); + log.info (i + ": " + factAcct); + + // Create Tax Account + MAccount taxAcct = factAcct.getMAccount(); + if (taxAcct == null || taxAcct.get_ID() == 0) + { + log.severe ("Tax Account not found/created"); + return false; + } + + + // Discount Amount + if (m_DiscountAmt.signum() != 0) + { + // Original Tax is DR - need to correct it CR + if (Env.ZERO.compareTo(factAcct.getAmtSourceDr()) != 0) + { + BigDecimal amount = calcAmount(factAcct.getAmtSourceDr(), + total, m_DiscountAmt, precision); + if (amount.signum() != 0) + { + fact.createLine (line, m_DiscountAccount, + as.getC_Currency_ID(), amount, null); + fact.createLine (line, taxAcct, + as.getC_Currency_ID(), null, amount); + } + } + // Original Tax is CR - need to correct it DR + else + { + BigDecimal amount = calcAmount(factAcct.getAmtSourceCr(), + total, m_DiscountAmt, precision); + if (amount.signum() != 0) + { + fact.createLine (line, taxAcct, + as.getC_Currency_ID(), amount, null); + fact.createLine (line, m_DiscountAccount, + as.getC_Currency_ID(), null, amount); + } + } + } // Discount + + // WriteOff Amount + if (m_WriteOffAmt.signum() != 0) + { + // Original Tax is DR - need to correct it CR + if (Env.ZERO.compareTo(factAcct.getAmtSourceDr()) != 0) + { + BigDecimal amount = calcAmount(factAcct.getAmtSourceDr(), + total, m_WriteOffAmt, precision); + if (amount.signum() != 0) + { + fact.createLine (line, m_WriteOffAccount, + as.getC_Currency_ID(), amount, null); + fact.createLine (line, taxAcct, + as.getC_Currency_ID(), null, amount); + } + } + // Original Tax is CR - need to correct it DR + else + { + BigDecimal amount = calcAmount(factAcct.getAmtSourceCr(), + total, m_WriteOffAmt, precision); + if (amount.signum() != 0) + { + fact.createLine (line, taxAcct, + as.getC_Currency_ID(), amount, null); + fact.createLine (line, m_WriteOffAccount, + as.getC_Currency_ID(), null, amount); + } + } + } // WriteOff + + } // for all lines + return true; + } // createEntries + + /** + * Calc Amount tax / (total-tax) * amt + * @param tax tax + * @param total total + * @param amt reduction amt + * @param precision precision + * @return tax / total * amt + */ + private BigDecimal calcAmount (BigDecimal tax, BigDecimal total, BigDecimal amt, int precision) + { + log.fine("Amt=" + amt + " - Total=" + total + ", Tax=" + tax); + if (tax.signum() == 0 + || total.signum() == 0 + || amt.signum() == 0) + return Env.ZERO; + // + BigDecimal devisor = total.subtract(tax); + BigDecimal multiplier = tax.divide(devisor, 10, BigDecimal.ROUND_HALF_UP); + BigDecimal retValue = multiplier.multiply(amt); + if (retValue.scale() > precision) + retValue = retValue.setScale(precision, BigDecimal.ROUND_HALF_UP); + log.fine(retValue + " (Mult=" + multiplier + "(Prec=" + precision + ")"); + return retValue; + } // calcAmount + +} // Doc_AllocationTax diff --git a/serverRoot/src/main/server/org/compiere/acct/Doc_Bank.java b/serverRoot/src/main/server/org/compiere/acct/Doc_Bank.java new file mode 100644 index 0000000000..ee67d3b554 --- /dev/null +++ b/serverRoot/src/main/server/org/compiere/acct/Doc_Bank.java @@ -0,0 +1,223 @@ +/****************************************************************************** + * 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.acct; + +import java.math.*; +import java.sql.*; +import java.util.*; + +import org.compiere.model.*; +import org.compiere.util.*; + +/** + * Post Invoice Documents. + *
+ *  Table:              C_BankStatement (392)
+ *  Document Types:     CMB
+ *  
+ * @author Jorg Janke + * @version $Id: Doc_Bank.java,v 1.3 2006/07/30 00:53:33 jjanke Exp $ + */ +public class Doc_Bank extends Doc +{ + /** + * Constructor + * @param ass accounting schemata + * @param rs record + * @param trxName trx + */ + protected Doc_Bank (MAcctSchema[] ass, ResultSet rs, String trxName) + { + super (ass, MBankStatement.class, rs, DOCTYPE_BankStatement, trxName); + } // Doc_Bank + + /** Bank Account */ + private int m_C_BankAccount_ID = 0; + + /** + * Load Specific Document Details + * @return error message or null + */ + protected String loadDocumentDetails () + { + MBankStatement bs = (MBankStatement)getPO(); + setDateDoc(bs.getStatementDate()); + setDateAcct(bs.getStatementDate()); // Overwritten on Line Level + + m_C_BankAccount_ID = bs.getC_BankAccount_ID(); + // Amounts + setAmount(AMTTYPE_Gross, bs.getStatementDifference()); + + // Set Bank Account Info (Currency) + MBankAccount ba = MBankAccount.get (getCtx(), m_C_BankAccount_ID); + setC_Currency_ID (ba.getC_Currency_ID()); + + // Contained Objects + p_lines = loadLines(bs); + log.fine("Lines=" + p_lines.length); + return null; + } // loadDocumentDetails + + /** + * Load Invoice Line. + * @param bs bank statement + * 4 amounts + * AMTTYPE_Payment + * AMTTYPE_Statement2 + * AMTTYPE_Charge + * AMTTYPE_Interest + * @return DocLine Array + */ + private DocLine[] loadLines(MBankStatement bs) + { + ArrayList list = new ArrayList(); + MBankStatementLine[] lines = bs.getLines(false); + for (int i = 0; i < lines.length; i++) + { + MBankStatementLine line = lines[i]; + DocLine_Bank docLine = new DocLine_Bank(line, this); + // Set Date Acct + if (i == 0) + setDateAcct(line.getDateAcct()); + MPeriod period = MPeriod.get(getCtx(), line.getDateAcct()); + if (period != null && period.isOpen(DOCTYPE_BankStatement)) + docLine.setC_Period_ID(period.getC_Period_ID()); + // + list.add(docLine); + } + + // Return Array + DocLine[] dls = new DocLine[list.size()]; + list.toArray(dls); + return dls; + } // loadLines + + + /************************************************************************** + * Get Source Currency Balance - subtracts line amounts from total - no rounding + * @return positive amount, if total invoice is bigger than lines + */ + public BigDecimal getBalance() + { + BigDecimal retValue = Env.ZERO; + StringBuffer sb = new StringBuffer (" ["); + // Total + retValue = retValue.add(getAmount(Doc.AMTTYPE_Gross)); + sb.append(getAmount(Doc.AMTTYPE_Gross)); + // - Lines + for (int i = 0; i < p_lines.length; i++) + { + BigDecimal lineBalance = ((DocLine_Bank)p_lines[i]).getStmtAmt(); + retValue = retValue.subtract(lineBalance); + sb.append("-").append(lineBalance); + } + sb.append("]"); + // + log.fine(toString() + " Balance=" + retValue + sb.toString()); + return retValue; + } // getBalance + + /** + * Create Facts (the accounting logic) for + * CMB. + *
+	 *      BankAsset       DR      CR  (Statement)
+	 *      BankInTransit   DR      CR              (Payment)
+	 *      Charge          DR          (Charge)
+	 *      Interest        DR      CR  (Interest)
+	 *  
+ * @param as accounting schema + * @return Fact + */ + public ArrayList createFacts (MAcctSchema as) + { + // create Fact Header + Fact fact = new Fact(this, as, Fact.POST_Actual); + + // Header -- there may be different currency amounts + + FactLine fl = null; + int AD_Org_ID = getBank_Org_ID(); // Bank Account Org + // Lines + for (int i = 0; i < p_lines.length; i++) + { + DocLine_Bank line = (DocLine_Bank)p_lines[i]; + int C_BPartner_ID = line.getC_BPartner_ID(); + + // BankAsset DR CR (Statement) + fl = fact.createLine(line, + getAccount(Doc.ACCTTYPE_BankAsset, as), + line.getC_Currency_ID(), line.getStmtAmt()); + if (fl != null && AD_Org_ID != 0) + fl.setAD_Org_ID(AD_Org_ID); + if (fl != null && C_BPartner_ID != 0) + fl.setC_BPartner_ID(C_BPartner_ID); + + // BankInTransit DR CR (Payment) + fl = fact.createLine(line, + getAccount(Doc.ACCTTYPE_BankInTransit, as), + line.getC_Currency_ID(), line.getTrxAmt().negate()); + if (fl != null) + { + if (C_BPartner_ID != 0) + fl.setC_BPartner_ID(C_BPartner_ID); + if (AD_Org_ID != 0) + fl.setAD_Org_ID(AD_Org_ID); + else + fl.setAD_Org_ID(line.getAD_Org_ID(true)); // from payment + } + // Charge DR (Charge) + fl = fact.createLine(line, + line.getChargeAccount(as, line.getChargeAmt().negate()), + line.getC_Currency_ID(), line.getChargeAmt().negate(), null); + if (fl != null && C_BPartner_ID != 0) + fl.setC_BPartner_ID(C_BPartner_ID); + + // Interest DR CR (Interest) + if (line.getInterestAmt().signum() < 0) + fl = fact.createLine(line, + getAccount(Doc.ACCTTYPE_InterestExp, as), getAccount(Doc.ACCTTYPE_InterestExp, as), + line.getC_Currency_ID(), line.getInterestAmt().negate()); + else + fl = fact.createLine(line, + getAccount(Doc.ACCTTYPE_InterestRev, as), getAccount(Doc.ACCTTYPE_InterestRev, as), + line.getC_Currency_ID(), line.getInterestAmt().negate()); + if (fl != null && C_BPartner_ID != 0) + fl.setC_BPartner_ID(C_BPartner_ID); + // + // fact.createTaxCorrection(); + } + // + ArrayList facts = new ArrayList(); + facts.add(fact); + return facts; + } // createFact + + /** + * Get AD_Org_ID from Bank Account + * @return AD_Org_ID or 0 + */ + private int getBank_Org_ID () + { + if (m_C_BankAccount_ID == 0) + return 0; + // + MBankAccount ba = MBankAccount.get(getCtx(), m_C_BankAccount_ID); + return ba.getAD_Org_ID(); + } // getBank_Org_ID + +} // Doc_Bank diff --git a/serverRoot/src/main/server/org/compiere/acct/Doc_Cash.java b/serverRoot/src/main/server/org/compiere/acct/Doc_Cash.java new file mode 100644 index 0000000000..568031b9e7 --- /dev/null +++ b/serverRoot/src/main/server/org/compiere/acct/Doc_Cash.java @@ -0,0 +1,254 @@ +/****************************************************************************** + * 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.acct; + +import java.math.*; +import java.sql.*; +import java.util.*; + +import org.compiere.model.*; +import java.util.logging.*; +import org.compiere.util.*; + +/** + * Post Invoice Documents. + *
+ *  Table:              C_Cash (407)
+ *  Document Types:     CMC
+ *  
+ * @author Jorg Janke + * @version $Id: Doc_Cash.java,v 1.3 2006/07/30 00:53:33 jjanke Exp $ + */ +public class Doc_Cash extends Doc +{ + /** + * Constructor + * @param ass accounting schemata + * @param rs record + * @param trxName trx + */ + protected Doc_Cash (MAcctSchema[] ass, ResultSet rs, String trxName) + { + super(ass, MCash.class, rs, DOCTYPE_CashJournal, trxName); + } // Doc_Cash + + /** + * Load Specific Document Details + * @return error message or null + */ + protected String loadDocumentDetails () + { + MCash cash = (MCash)getPO(); + setDateDoc(cash.getStatementDate()); + + // Amounts + setAmount(Doc.AMTTYPE_Gross, cash.getStatementDifference()); + + // Set CashBook Org & Currency + MCashBook cb = MCashBook.get(getCtx(), cash.getC_CashBook_ID()); + setC_CashBook_ID(cb.getC_CashBook_ID()); + setC_Currency_ID(cb.getC_Currency_ID()); + + // Contained Objects + p_lines = loadLines(cash, cb); + log.fine("Lines=" + p_lines.length); + return null; + } // loadDocumentDetails + + + /** + * Load Cash Line + * @param cash journal + * @param cb cash book + * @return DocLine Array + */ + private DocLine[] loadLines(MCash cash, MCashBook cb) + { + ArrayList list = new ArrayList(); + MCashLine[] lines = cash.getLines(false); + for (int i = 0; i < lines.length; i++) + { + MCashLine line = lines[i]; + DocLine_Cash docLine = new DocLine_Cash (line, this); + // + list.add(docLine); + } + + // Return Array + DocLine[] dls = new DocLine[list.size()]; + list.toArray(dls); + return dls; + } // loadLines + + + /************************************************************************** + * Get Source Currency Balance - subtracts line amounts from total - no rounding + * @return positive amount, if total invoice is bigger than lines + */ + public BigDecimal getBalance() + { + BigDecimal retValue = Env.ZERO; + StringBuffer sb = new StringBuffer (" ["); + // Total + retValue = retValue.add(getAmount(Doc.AMTTYPE_Gross)); + sb.append(getAmount(Doc.AMTTYPE_Gross)); + // - Lines + for (int i = 0; i < p_lines.length; i++) + { + retValue = retValue.subtract(p_lines[i].getAmtSource()); + sb.append("-").append(p_lines[i].getAmtSource()); + } + sb.append("]"); + // + log.fine(toString() + " Balance=" + retValue + sb.toString()); + // return retValue; + return Env.ZERO; // Lines are balanced + } // getBalance + + /** + * Create Facts (the accounting logic) for + * CMC. + *
+	 *  Expense
+	 *          CashExpense     DR
+	 *          CashAsset               CR
+	 *  Receipt
+	 *          CashAsset       DR
+	 *          CashReceipt             CR
+	 *  Charge
+	 *          Charge          DR
+	 *          CashAsset               CR
+	 *  Difference
+	 *          CashDifference  DR
+	 *          CashAsset               CR
+	 *  Invoice
+	 *          CashAsset       DR
+	 *          CashTransfer            CR
+	 *  Transfer
+	 *          BankInTransit   DR
+	 *          CashAsset               CR
+	 *  
+ * @param as account schema + * @return Fact + */ + public ArrayList createFacts (MAcctSchema as) + { + // Need to have CashBook + if (getC_CashBook_ID() == 0) + { + p_Error = "C_CashBook_ID not set"; + log.log(Level.SEVERE, p_Error); + return null; + } + + // create Fact Header + Fact fact = new Fact(this, as, Fact.POST_Actual); + + // Header posting amt as Invoices and Transfer could be differenet currency + // CashAsset Total + BigDecimal assetAmt = Env.ZERO; + + // Lines + for (int i = 0; i < p_lines.length; i++) + { + DocLine_Cash line = (DocLine_Cash)p_lines[i]; + String CashType = line.getCashType(); + + if (CashType.equals(DocLine_Cash.CASHTYPE_EXPENSE)) + { // amount is negative + // CashExpense DR + // CashAsset CR + fact.createLine(line, getAccount(Doc.ACCTTYPE_CashExpense, as), + getC_Currency_ID(), line.getAmount().negate(), null); + // fact.createLine(line, getAccount(Doc.ACCTTYPE_CashAsset, as), + // p_vo.C_Currency_ID, null, line.getAmount().negate()); + assetAmt = assetAmt.subtract(line.getAmount().negate()); + } + else if (CashType.equals(DocLine_Cash.CASHTYPE_RECEIPT)) + { // amount is positive + // CashAsset DR + // CashReceipt CR + // fact.createLine(line, getAccount(Doc.ACCTTYPE_CashAsset, as), + // p_vo.C_Currency_ID, line.getAmount(), null); + assetAmt = assetAmt.add(line.getAmount()); + fact.createLine(line, getAccount(Doc.ACCTTYPE_CashReceipt, as), + getC_Currency_ID(), null, line.getAmount()); + } + else if (CashType.equals(DocLine_Cash.CASHTYPE_CHARGE)) + { // amount is negative + // Charge DR + // CashAsset CR + fact.createLine(line, line.getChargeAccount(as, getAmount()), + getC_Currency_ID(), line.getAmount().negate(), null); + // fact.createLine(line, getAccount(Doc.ACCTTYPE_CashAsset, as), + // p_vo.C_Currency_ID, null, line.getAmount().negate()); + assetAmt = assetAmt.subtract(line.getAmount().negate()); + } + else if (CashType.equals(DocLine_Cash.CASHTYPE_DIFFERENCE)) + { // amount is pos/neg + // CashDifference DR + // CashAsset CR + fact.createLine(line, getAccount(Doc.ACCTTYPE_CashDifference, as), + getC_Currency_ID(), line.getAmount().negate()); + // fact.createLine(line, getAccount(Doc.ACCTTYPE_CashAsset, as), + // p_vo.C_Currency_ID, line.getAmount()); + assetAmt = assetAmt.add(line.getAmount()); + } + else if (CashType.equals(DocLine_Cash.CASHTYPE_INVOICE)) + { // amount is pos/neg + // CashAsset DR dr -- Invoice is in Invoice Currency ! + // CashTransfer cr CR + if (line.getC_Currency_ID() == getC_Currency_ID()) + assetAmt = assetAmt.add (line.getAmount()); + else + fact.createLine(line, + getAccount(Doc.ACCTTYPE_CashAsset, as), + line.getC_Currency_ID(), line.getAmount()); + fact.createLine(line, + getAccount(Doc.ACCTTYPE_CashTransfer, as), + line.getC_Currency_ID(), line.getAmount().negate()); + } + else if (CashType.equals(DocLine_Cash.CASHTYPE_TRANSFER)) + { // amount is pos/neg + // BankInTransit DR dr -- Transfer is in Bank Account Currency + // CashAsset dr CR + int temp = getC_BankAccount_ID(); + setC_BankAccount_ID (line.getC_BankAccount_ID()); + fact.createLine(line, + getAccount(Doc.ACCTTYPE_BankInTransit, as), + line.getC_Currency_ID(), line.getAmount().negate()); + setC_BankAccount_ID(temp); + if (line.getC_Currency_ID() == getC_Currency_ID()) + assetAmt = assetAmt.add (line.getAmount()); + else + fact.createLine(line, + getAccount(Doc.ACCTTYPE_CashAsset, as), + line.getC_Currency_ID(), line.getAmount()); + } + } // lines + + // Cash Asset + fact.createLine(null, getAccount(Doc.ACCTTYPE_CashAsset, as), + getC_Currency_ID(), assetAmt); + + // + ArrayList facts = new ArrayList(); + facts.add(fact); + return facts; + } // createFact + +} // Doc_Cash diff --git a/serverRoot/src/main/server/org/compiere/acct/Doc_GLJournal.java b/serverRoot/src/main/server/org/compiere/acct/Doc_GLJournal.java new file mode 100644 index 0000000000..d7fa2da090 --- /dev/null +++ b/serverRoot/src/main/server/org/compiere/acct/Doc_GLJournal.java @@ -0,0 +1,168 @@ +/****************************************************************************** + * 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.acct; + +import java.math.*; +import java.sql.*; +import java.util.*; + +import org.compiere.model.*; +import java.util.logging.*; +import org.compiere.util.*; + +/** + * Post Invoice Documents. + *
+ *  Table:              GL_Journal (224)
+ *  Document Types:     GLJ
+ *  
+ * @author Jorg Janke + * @version $Id: Doc_GLJournal.java,v 1.3 2006/07/30 00:53:33 jjanke Exp $ + */ +public class Doc_GLJournal extends Doc +{ + /** + * Constructor + * @param ass accounting schemata + * @param rs record + * @param trxName trx + */ + protected Doc_GLJournal (MAcctSchema[] ass, ResultSet rs, String trxName) + { + super(ass, MJournal.class, rs, null, trxName); + } // Foc_GL_Journal + + /** Posting Type */ + private String m_PostingType = null; + private int m_C_AcctSchema_ID = 0; + + /** + * Load Specific Document Details + * @return error message or null + */ + protected String loadDocumentDetails () + { + MJournal journal = (MJournal)getPO(); + m_PostingType = journal.getPostingType(); + m_C_AcctSchema_ID = journal.getC_AcctSchema_ID(); + + // Contained Objects + p_lines = loadLines(journal); + log.fine("Lines=" + p_lines.length); + return null; + } // loadDocumentDetails + + + /** + * Load Invoice Line + * @param journal journal + * @return DocLine Array + */ + private DocLine[] loadLines(MJournal journal) + { + ArrayList list = new ArrayList(); + MJournalLine[] lines = journal.getLines(false); + for (int i = 0; i < lines.length; i++) + { + MJournalLine line = lines[i]; + DocLine docLine = new DocLine (line, this); + // -- Source Amounts + docLine.setAmount (line.getAmtSourceDr(), line.getAmtSourceCr()); + // -- Converted Amounts + docLine.setConvertedAmt (m_C_AcctSchema_ID, line.getAmtAcctDr(), line.getAmtAcctCr()); + // -- Account + MAccount account = line.getAccount(); + docLine.setAccount (account); + // -- Organization of Line was set to Org of Account + list.add(docLine); + } + // Return Array + int size = list.size(); + DocLine[] dls = new DocLine[size]; + list.toArray(dls); + return dls; + } // loadLines + + + /************************************************************************** + * Get Source Currency Balance - subtracts line and tax amounts from total - no rounding + * @return positive amount, if total invoice is bigger than lines + */ + public BigDecimal getBalance() + { + BigDecimal retValue = Env.ZERO; + StringBuffer sb = new StringBuffer (" ["); + // Lines + for (int i = 0; i < p_lines.length; i++) + { + retValue = retValue.add(p_lines[i].getAmtSource()); + sb.append("+").append(p_lines[i].getAmtSource()); + } + sb.append("]"); + // + log.fine(toString() + " Balance=" + retValue + sb.toString()); + return retValue; + } // getBalance + + /** + * Create Facts (the accounting logic) for + * GLJ. + * (only for the accounting scheme, it was created) + *
+	 *      account     DR          CR
+	 *  
+ * @param as acct schema + * @return Fact + */ + public ArrayList createFacts (MAcctSchema as) + { + ArrayList facts = new ArrayList(); + // Other Acct Schema + if (as.getC_AcctSchema_ID() != m_C_AcctSchema_ID) + return facts; + + // create Fact Header + Fact fact = new Fact (this, as, m_PostingType); + + // GLJ + if (getDocumentType().equals(DOCTYPE_GLJournal)) + { + // account DR CR + for (int i = 0; i < p_lines.length; i++) + { + if (p_lines[i].getC_AcctSchema_ID () == as.getC_AcctSchema_ID ()) + { + FactLine line = fact.createLine (p_lines[i], + p_lines[i].getAccount (), + getC_Currency_ID(), + p_lines[i].getAmtSourceDr (), + p_lines[i].getAmtSourceCr ()); + } + } // for all lines + } + else + { + p_Error = "DocumentType unknown: " + getDocumentType(); + log.log(Level.SEVERE, p_Error); + fact = null; + } + // + facts.add(fact); + return facts; + } // createFact + +} // Doc_GLJournal diff --git a/serverRoot/src/main/server/org/compiere/acct/Doc_InOut.java b/serverRoot/src/main/server/org/compiere/acct/Doc_InOut.java new file mode 100644 index 0000000000..4ca4a7b57a --- /dev/null +++ b/serverRoot/src/main/server/org/compiere/acct/Doc_InOut.java @@ -0,0 +1,283 @@ +/****************************************************************************** + * 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.acct; + +import java.math.*; +import java.sql.*; +import java.util.*; + +import org.compiere.model.*; +import java.util.logging.*; +import org.compiere.util.*; + +/** + * Post Shipment/Receipt Documents. + *
+ *  Table:              M_InOut (319)
+ *  Document Types:     MMS, MMR
+ *  
+ * @author Jorg Janke + * @version $Id: Doc_InOut.java,v 1.3 2006/07/30 00:53:33 jjanke Exp $ + */ +public class Doc_InOut extends Doc +{ + /** + * Constructor + * @param ass accounting schemata + * @param rs record + * @param trxName trx + */ + public Doc_InOut (MAcctSchema[] ass, ResultSet rs, String trxName) + { + super (ass, MInOut.class, rs, null, trxName); + } // DocInOut + + /** + * Load Document Details + * @return error message or null + */ + protected String loadDocumentDetails() + { + setC_Currency_ID(NO_CURRENCY); + MInOut inout = (MInOut)getPO(); + setDateDoc (inout.getMovementDate()); + // Contained Objects + p_lines = loadLines(inout); + log.fine("Lines=" + p_lines.length); + return null; + } // loadDocumentDetails + + /** + * Load Invoice Line + * @param inout shipment/receipt + * @return DocLine Array + */ + private DocLine[] loadLines(MInOut inout) + { + ArrayList list = new ArrayList(); + MInOutLine[] lines = inout.getLines(false); + for (int i = 0; i < lines.length; i++) + { + MInOutLine line = lines[i]; + if (line.isDescription() + || line.getM_Product_ID() == 0 + || line.getMovementQty().signum() == 0) + { + log.finer("Ignored: " + line); + continue; + } + + DocLine docLine = new DocLine (line, this); + BigDecimal Qty = line.getMovementQty(); + docLine.setQty (Qty, getDocumentType().equals(DOCTYPE_MatShipment)); // sets Trx and Storage Qty + // + log.fine(docLine.toString()); + list.add (docLine); + } + + // Return Array + DocLine[] dls = new DocLine[list.size()]; + list.toArray(dls); + return dls; + } // loadLines + + /** + * Get Balance + * @return Zero (always balanced) + */ + public BigDecimal getBalance() + { + BigDecimal retValue = Env.ZERO; + return retValue; + } // getBalance + + /** + * Create Facts (the accounting logic) for + * MMS, MMR. + *
+	 *  Shipment
+	 *      CoGS (RevOrg)   DR
+	 *      Inventory               CR
+	 *  Shipment of Project Issue
+	 *      CoGS            DR
+	 *      Project                 CR
+	 *  Receipt
+	 *      Inventory       DR
+	 *      NotInvoicedReceipt      CR
+	 *  
+ * @param as accounting schema + * @return Fact + */ + public ArrayList createFacts (MAcctSchema as) + { + // create Fact Header + Fact fact = new Fact(this, as, Fact.POST_Actual); + setC_Currency_ID (as.getC_Currency_ID()); + + // Line pointers + FactLine dr = null; + FactLine cr = null; + + // *** Sales - Shipment + if (getDocumentType().equals(DOCTYPE_MatShipment)) + { + for (int i = 0; i < p_lines.length; i++) + { + DocLine line = p_lines[i]; + BigDecimal costs = line.getProductCosts(as, line.getAD_Org_ID(), true); + if (costs == null || costs.signum() == 0) // zero costs OK + { + MProduct product = line.getProduct(); + if (product.isStocked()) + { + p_Error = "No Costs for " + line.getProduct().getName(); + log.log(Level.WARNING, p_Error); + return null; + } + else // ignore service + continue; + } + // CoGS DR + dr = fact.createLine(line, + line.getAccount(ProductCost.ACCTTYPE_P_Cogs, as), + as.getC_Currency_ID(), costs, null); + if (dr == null) + { + p_Error = "FactLine DR not created: " + line; + log.log(Level.WARNING, p_Error); + return null; + } + dr.setM_Locator_ID(line.getM_Locator_ID()); + dr.setLocationFromLocator(line.getM_Locator_ID(), true); // from Loc + dr.setLocationFromBPartner(getC_BPartner_Location_ID(), false); // to Loc + dr.setAD_Org_ID(line.getOrder_Org_ID()); // Revenue X-Org + dr.setQty(line.getQty().negate()); + + // Inventory CR + cr = fact.createLine(line, + line.getAccount(ProductCost.ACCTTYPE_P_Asset, as), + as.getC_Currency_ID(), null, costs); + if (cr == null) + { + p_Error = "FactLine CR not created: " + line; + log.log(Level.WARNING, p_Error); + return null; + } + cr.setM_Locator_ID(line.getM_Locator_ID()); + cr.setLocationFromLocator(line.getM_Locator_ID(), true); // from Loc + cr.setLocationFromBPartner(getC_BPartner_Location_ID(), false); // to Loc + // + if (line.getM_Product_ID() != 0) + { + MCostDetail.createShipment(as, line.getAD_Org_ID(), + line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), + line.get_ID(), 0, + costs, line.getQty(), + line.getDescription(), true, getTrxName()); + } + } // for all lines + updateProductInfo(as.getC_AcctSchema_ID()); // only for SO! + } // Shipment + + // *** Purchasing - Receipt + else if (getDocumentType().equals(DOCTYPE_MatReceipt)) + { + for (int i = 0; i < p_lines.length; i++) + { + DocLine line = p_lines[i]; + BigDecimal costs = line.getProductCosts(as, line.getAD_Org_ID(), false); // non-zero costs + MProduct product = line.getProduct(); + if (costs == null || costs.signum() == 0) + { + p_Error = "Resubmit - No Costs for " + product.getName(); + log.log(Level.WARNING, p_Error); + return null; + } + // Inventory/Asset DR + MAccount assets = line.getAccount(ProductCost.ACCTTYPE_P_Asset, as); + if (product.isService()) + assets = line.getAccount(ProductCost.ACCTTYPE_P_Expense, as); + dr = fact.createLine(line, assets, + as.getC_Currency_ID(), costs, null); + if (dr == null) + { + p_Error = "DR not created: " + line; + log.log(Level.WARNING, p_Error); + return null; + } + dr.setM_Locator_ID(line.getM_Locator_ID()); + dr.setLocationFromBPartner(getC_BPartner_Location_ID(), true); // from Loc + dr.setLocationFromLocator(line.getM_Locator_ID(), false); // to Loc + // NotInvoicedReceipt CR + cr = fact.createLine(line, + getAccount(Doc.ACCTTYPE_NotInvoicedReceipts, as), + as.getC_Currency_ID(), null, costs); + if (cr == null) + { + p_Error = "CR not created: " + line; + log.log(Level.WARNING, p_Error); + return null; + } + cr.setM_Locator_ID(line.getM_Locator_ID()); + cr.setLocationFromBPartner(getC_BPartner_Location_ID(), true); // from Loc + cr.setLocationFromLocator(line.getM_Locator_ID(), false); // to Loc + cr.setQty(line.getQty().negate()); + } + } // Receipt + else + { + p_Error = "DocumentType unknown: " + getDocumentType(); + log.log(Level.SEVERE, p_Error); + return null; + } + // + ArrayList facts = new ArrayList(); + facts.add(fact); + return facts; + } // createFact + + + /** + * Update Sales Order Costing Product Info (old). + * Purchase side handeled in Invoice Matching. + *
+ * decrease average cumulatives + * @param C_AcctSchema_ID accounting schema + * @deprecated old costing + */ + private void updateProductInfo (int C_AcctSchema_ID) + { + log.fine("M_InOut_ID=" + get_ID()); + // Old Model + StringBuffer sql = new StringBuffer( + "UPDATE M_Product_Costing pc " + + "SET (CostAverageCumQty, CostAverageCumAmt)=" + + "(SELECT CostAverageCumQty - SUM(il.MovementQty)," + + " CostAverageCumAmt - SUM(il.MovementQty*CurrentCostPrice) " + + "FROM M_InOutLine il " + + "WHERE pc.M_Product_ID=il.M_Product_ID" + + " AND il.M_InOut_ID=").append(get_ID()).append(") ") + .append("WHERE EXISTS (SELECT * " + + "FROM M_InOutLine il " + + "WHERE pc.M_Product_ID=il.M_Product_ID" + + " AND il.M_InOut_ID=").append(get_ID()).append(")"); + int no = DB.executeUpdate(sql.toString(), getTrxName()); + log.fine("M_Product_Costing - Updated=" + no); + // + } // updateProductInfo + +} // Doc_InOut diff --git a/serverRoot/src/main/server/org/compiere/acct/Doc_Inventory.java b/serverRoot/src/main/server/org/compiere/acct/Doc_Inventory.java new file mode 100644 index 0000000000..fc8e902f51 --- /dev/null +++ b/serverRoot/src/main/server/org/compiere/acct/Doc_Inventory.java @@ -0,0 +1,179 @@ +/****************************************************************************** + * 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.acct; + +import java.math.*; +import java.sql.*; +import java.util.*; + +import org.compiere.model.*; +import org.compiere.util.*; + +/** + * Post Inventory Documents. + *
+ *  Table:              M_Inventory (321)
+ *  Document Types:     MMI
+ *  
+ * @author Jorg Janke + * @version $Id: Doc_Inventory.java,v 1.3 2006/07/30 00:53:33 jjanke Exp $ + */ +public class Doc_Inventory extends Doc +{ + /** + * Constructor + * @param ass accounting schemata + * @param rs record + * @param trxName trx + */ + public Doc_Inventory (MAcctSchema[] ass, ResultSet rs, String trxName) + { + super (ass, MInventory.class, rs, DOCTYPE_MatInventory, trxName); + } // Doc_Inventory + + /** + * Load Document Details + * @return error message or null + */ + protected String loadDocumentDetails() + { + setC_Currency_ID (NO_CURRENCY); + MInventory inventory = (MInventory)getPO(); + setDateDoc (inventory.getMovementDate()); + setDateAcct(inventory.getMovementDate()); + // Contained Objects + p_lines = loadLines(inventory); + log.fine("Lines=" + p_lines.length); + return null; + } // loadDocumentDetails + + /** + * Load Invoice Line + * @param inventory inventory + * @return DocLine Array + */ + private DocLine[] loadLines(MInventory inventory) + { + ArrayList list = new ArrayList(); + MInventoryLine[] lines = inventory.getLines(false); + for (int i = 0; i < lines.length; i++) + { + MInventoryLine line = lines[i]; + // nothing to post + if (line.getQtyBook().compareTo(line.getQtyCount()) == 0 + && line.getQtyInternalUse().signum() == 0) + continue; + // + DocLine docLine = new DocLine (line, this); + BigDecimal Qty = line.getQtyInternalUse(); + if (Qty.signum() != 0) + Qty = Qty.negate(); // Internal Use entered positive + else + { + BigDecimal QtyBook = line.getQtyBook(); + BigDecimal QtyCount = line.getQtyCount(); + Qty = QtyCount.subtract(QtyBook); + } + docLine.setQty (Qty, false); // -5 => -5 + // + log.fine(docLine.toString()); + list.add (docLine); + } + + // Return Array + DocLine[] dls = new DocLine[list.size()]; + list.toArray(dls); + return dls; + } // loadLines + + /** + * Get Balance + * @return Zero (always balanced) + */ + public BigDecimal getBalance() + { + BigDecimal retValue = Env.ZERO; + return retValue; + } // getBalance + + /** + * Create Facts (the accounting logic) for + * MMI. + *
+	 *  Inventory
+	 *      Inventory       DR      CR
+	 *      InventoryDiff   DR      CR   (or Charge)
+	 *  
+ * @param as account schema + * @return Fact + */ + public ArrayList createFacts (MAcctSchema as) + { + // create Fact Header + Fact fact = new Fact(this, as, Fact.POST_Actual); + setC_Currency_ID(as.getC_Currency_ID()); + + // Line pointers + FactLine dr = null; + FactLine cr = null; + + for (int i = 0; i < p_lines.length; i++) + { + DocLine line = p_lines[i]; + BigDecimal costs = line.getProductCosts(as, line.getAD_Org_ID(), false); + if (costs == null || costs.signum() == 0) + { + p_Error = "No Costs for " + line.getProduct().getName(); + return null; + } + // Inventory DR CR + dr = fact.createLine(line, + line.getAccount(ProductCost.ACCTTYPE_P_Asset, as), + as.getC_Currency_ID(), costs); + // may be zero difference - no line created. + if (dr == null) + continue; + dr.setM_Locator_ID(line.getM_Locator_ID()); + + // InventoryDiff DR CR + // or Charge + MAccount invDiff = line.getChargeAccount(as, costs.negate()); + if (invDiff == null) + invDiff = getAccount(Doc.ACCTTYPE_InvDifferences, as); + cr = fact.createLine(line, invDiff, + as.getC_Currency_ID(), costs.negate()); + if (cr == null) + continue; + cr.setM_Locator_ID(line.getM_Locator_ID()); + cr.setQty(line.getQty().negate()); + if (line.getC_Charge_ID() != 0) // explicit overwrite for charge + cr.setAD_Org_ID(line.getAD_Org_ID()); + + // Cost Detail + MCostDetail.createInventory(as, line.getAD_Org_ID(), + line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), + line.get_ID(), 0, + costs, line.getQty(), + line.getDescription(), getTrxName()); + } + // + ArrayList facts = new ArrayList(); + facts.add(fact); + return facts; + } // createFact + +} // Doc_Inventory diff --git a/serverRoot/src/main/server/org/compiere/acct/Doc_Invoice.java b/serverRoot/src/main/server/org/compiere/acct/Doc_Invoice.java new file mode 100644 index 0000000000..d61444dd37 --- /dev/null +++ b/serverRoot/src/main/server/org/compiere/acct/Doc_Invoice.java @@ -0,0 +1,953 @@ +/****************************************************************************** + * 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.acct; + +import java.math.*; +import java.sql.*; +import java.util.*; +import java.util.logging.*; +import org.compiere.model.*; +import org.compiere.util.*; + +/** + * Post Invoice Documents. + *
+ *  Table:              C_Invoice (318)
+ *  Document Types:     ARI, ARC, ARF, API, APC
+ *  
+ * @author Jorg Janke + * @version $Id: Doc_Invoice.java,v 1.2 2006/07/30 00:53:33 jjanke Exp $ + */ +public class Doc_Invoice extends Doc +{ + /** + * Constructor + * @param ass accounting schemata + * @param rs record + * @param trxName trx + */ + protected Doc_Invoice(MAcctSchema[] ass, ResultSet rs, String trxName) + { + super (ass, MInvoice.class, rs, null, trxName); + } // Doc_Invoice + + /** Contained Optional Tax Lines */ + private DocTax[] m_taxes = null; + /** Currency Precision */ + private int m_precision = -1; + /** All lines are Service */ + private boolean m_allLinesService = true; + /** All lines are product item */ + private boolean m_allLinesItem = true; + + /** + * Load Specific Document Details + * @return error message or null + */ + protected String loadDocumentDetails () + { + MInvoice invoice = (MInvoice)getPO(); + setDateDoc(invoice.getDateInvoiced()); + setIsTaxIncluded(invoice.isTaxIncluded()); + // Amounts + setAmount(Doc.AMTTYPE_Gross, invoice.getGrandTotal()); + setAmount(Doc.AMTTYPE_Net, invoice.getTotalLines()); + setAmount(Doc.AMTTYPE_Charge, invoice.getChargeAmt()); + + // Contained Objects + m_taxes = loadTaxes(); + p_lines = loadLines(invoice); + log.fine("Lines=" + p_lines.length + ", Taxes=" + m_taxes.length); + return null; + } // loadDocumentDetails + + /** + * Load Invoice Taxes + * @return DocTax Array + */ + private DocTax[] loadTaxes() + { + ArrayList list = new ArrayList(); + String sql = "SELECT it.C_Tax_ID, t.Name, t.Rate, it.TaxBaseAmt, it.TaxAmt, t.IsSalesTax " + + "FROM C_Tax t, C_InvoiceTax it " + + "WHERE t.C_Tax_ID=it.C_Tax_ID AND it.C_Invoice_ID=?"; + try + { + PreparedStatement pstmt = DB.prepareStatement(sql, getTrxName()); + pstmt.setInt(1, get_ID()); + ResultSet rs = pstmt.executeQuery(); + // + while (rs.next()) + { + int C_Tax_ID = rs.getInt(1); + String name = rs.getString(2); + BigDecimal rate = rs.getBigDecimal(3); + BigDecimal taxBaseAmt = rs.getBigDecimal(4); + BigDecimal amount = rs.getBigDecimal(5); + boolean salesTax = "Y".equals(rs.getString(6)); + // + DocTax taxLine = new DocTax(C_Tax_ID, name, rate, + taxBaseAmt, amount, salesTax); + log.fine(taxLine.toString()); + list.add(taxLine); + } + // + rs.close(); + pstmt.close(); + } + catch (SQLException e) + { + log.log(Level.SEVERE, sql, e); + return null; + } + + // Return Array + DocTax[] tl = new DocTax[list.size()]; + list.toArray(tl); + return tl; + } // loadTaxes + + /** + * Load Invoice Line + * @param invoice invoice + * @return DocLine Array + */ + private DocLine[] loadLines (MInvoice invoice) + { + ArrayList list = new ArrayList(); + // + MInvoiceLine[] lines = invoice.getLines(false); + for (int i = 0; i < lines.length; i++) + { + MInvoiceLine line = lines[i]; + if (line.isDescription()) + continue; + DocLine docLine = new DocLine(line, this); + // Qty + BigDecimal Qty = line.getQtyInvoiced(); + boolean cm = getDocumentType().equals(DOCTYPE_ARCredit) + || getDocumentType().equals(DOCTYPE_APCredit); + docLine.setQty(cm ? Qty.negate() : Qty, invoice.isSOTrx()); + // + BigDecimal LineNetAmt = line.getLineNetAmt(); + BigDecimal PriceList = line.getPriceList(); + int C_Tax_ID = docLine.getC_Tax_ID(); + // Correct included Tax + if (isTaxIncluded() && C_Tax_ID != 0) + { + MTax tax = MTax.get(getCtx(), C_Tax_ID); + if (!tax.isZeroTax()) + { + BigDecimal LineNetAmtTax = tax.calculateTax(LineNetAmt, true, getStdPercision()); + log.fine("LineNetAmt=" + LineNetAmt + " - Tax=" + LineNetAmtTax); + LineNetAmt = LineNetAmt.subtract(LineNetAmtTax); + for (int t = 0; t < m_taxes.length; t++) + { + if (m_taxes[t].getC_Tax_ID() == C_Tax_ID) + { + m_taxes[t].addIncludedTax(LineNetAmtTax); + break; + } + } + BigDecimal PriceListTax = tax.calculateTax(PriceList, true, getStdPercision()); + PriceList = PriceList.subtract(PriceListTax); + } + } // correct included Tax + + docLine.setAmount (LineNetAmt, PriceList, Qty); // qty for discount calc + if (docLine.isItem()) + m_allLinesService = false; + else + m_allLinesItem = false; + // + log.fine(docLine.toString()); + list.add(docLine); + } + + // Convert to Array + DocLine[] dls = new DocLine[list.size()]; + list.toArray(dls); + + // Included Tax - make sure that no difference + if (isTaxIncluded()) + { + for (int i = 0; i < m_taxes.length; i++) + { + if (m_taxes[i].isIncludedTaxDifference()) + { + BigDecimal diff = m_taxes[i].getIncludedTaxDifference(); + for (int j = 0; j < dls.length; j++) + { + if (dls[j].getC_Tax_ID() == m_taxes[i].getC_Tax_ID()) + { + dls[j].setLineNetAmtDifference(diff); + break; + } + } // for all lines + } // tax difference + } // for all taxes + } // Included Tax difference + + // Return Array + return dls; + } // loadLines + + /** + * Get Currency Percision + * @return precision + */ + private int getStdPercision() + { + if (m_precision == -1) + m_precision = MCurrency.getStdPrecision(getCtx(), getC_Currency_ID()); + return m_precision; + } // getPrecision + + + /************************************************************************** + * Get Source Currency Balance - subtracts line and tax amounts from total - no rounding + * @return positive amount, if total invoice is bigger than lines + */ + public BigDecimal getBalance() + { + BigDecimal retValue = Env.ZERO; + StringBuffer sb = new StringBuffer (" ["); + // Total + retValue = retValue.add(getAmount(Doc.AMTTYPE_Gross)); + sb.append(getAmount(Doc.AMTTYPE_Gross)); + // - Header Charge + retValue = retValue.subtract(getAmount(Doc.AMTTYPE_Charge)); + sb.append("-").append(getAmount(Doc.AMTTYPE_Charge)); + // - Tax + for (int i = 0; i < m_taxes.length; i++) + { + retValue = retValue.subtract(m_taxes[i].getAmount()); + sb.append("-").append(m_taxes[i].getAmount()); + } + // - Lines + for (int i = 0; i < p_lines.length; i++) + { + retValue = retValue.subtract(p_lines[i].getAmtSource()); + sb.append("-").append(p_lines[i].getAmtSource()); + } + sb.append("]"); + // + log.fine(toString() + " Balance=" + retValue + sb.toString()); + return retValue; + } // getBalance + + /** + * Create Facts (the accounting logic) for + * ARI, ARC, ARF, API, APC. + *
+	 *  ARI, ARF
+	 *      Receivables     DR
+	 *      Charge                  CR
+	 *      TaxDue                  CR
+	 *      Revenue                 CR
+	 *
+	 *  ARC
+	 *      Receivables             CR
+	 *      Charge          DR
+	 *      TaxDue          DR
+	 *      Revenue         RR
+	 *
+	 *  API
+	 *      Payables                CR
+	 *      Charge          DR
+	 *      TaxCredit       DR
+	 *      Expense         DR
+	 *
+	 *  APC
+	 *      Payables        DR
+	 *      Charge                  CR
+	 *      TaxCredit               CR
+	 *      Expense                 CR
+	 *  
+ * @param as accounting schema + * @return Fact + */ + public ArrayList createFacts (MAcctSchema as) + { + // + ArrayList facts = new ArrayList(); + // create Fact Header + Fact fact = new Fact(this, as, Fact.POST_Actual); + + // Cash based accounting + if (!as.isAccrual()) + return facts; + + // ** ARI, ARF + if (getDocumentType().equals(DOCTYPE_ARInvoice) + || getDocumentType().equals(DOCTYPE_ARProForma)) + { + BigDecimal grossAmt = getAmount(Doc.AMTTYPE_Gross); + BigDecimal serviceAmt = Env.ZERO; + + // Header Charge CR + BigDecimal amt = getAmount(Doc.AMTTYPE_Charge); + if (amt != null && amt.signum() != 0) + fact.createLine(null, getAccount(Doc.ACCTTYPE_Charge, as), + getC_Currency_ID(), null, amt); + // TaxDue CR + for (int i = 0; i < m_taxes.length; i++) + { + amt = m_taxes[i].getAmount(); + if (amt != null && amt.signum() != 0) + { + FactLine tl = fact.createLine(null, m_taxes[i].getAccount(DocTax.ACCTTYPE_TaxDue, as), + getC_Currency_ID(), null, amt); + if (tl != null) + tl.setC_Tax_ID(m_taxes[i].getC_Tax_ID()); + } + } + // Revenue CR + for (int i = 0; i < p_lines.length; i++) + { + amt = p_lines[i].getAmtSource(); + BigDecimal dAmt = null; + if (as.isTradeDiscountPosted()) + { + BigDecimal discount = p_lines[i].getDiscount(); + if (discount != null && discount.signum() != 0) + { + amt = amt.add(discount); + dAmt = discount; + } + } + fact.createLine (p_lines[i], + p_lines[i].getAccount(ProductCost.ACCTTYPE_P_Revenue, as), + getC_Currency_ID(), dAmt, amt); + if (!p_lines[i].isItem()) + { + grossAmt = grossAmt.subtract(amt); + serviceAmt = serviceAmt.add(amt); + } + } + // Set Locations + FactLine[] fLines = fact.getLines(); + for (int i = 0; i < fLines.length; i++) + { + if (fLines[i] != null) + { + fLines[i].setLocationFromOrg(fLines[i].getAD_Org_ID(), true); // from Loc + fLines[i].setLocationFromBPartner(getC_BPartner_Location_ID(), false); // to Loc + } + } + + // Receivables DR + int receivables_ID = getValidCombination_ID(Doc.ACCTTYPE_C_Receivable, as); + int receivablesServices_ID = getValidCombination_ID (Doc.ACCTTYPE_C_Receivable_Services, as); + if (m_allLinesItem || !as.isPostServices() + || receivables_ID == receivablesServices_ID) + { + grossAmt = getAmount(Doc.AMTTYPE_Gross); + serviceAmt = Env.ZERO; + } + else if (m_allLinesService) + { + serviceAmt = getAmount(Doc.AMTTYPE_Gross); + grossAmt = Env.ZERO; + } + if (grossAmt.signum() != 0) + fact.createLine(null, MAccount.get(getCtx(), receivables_ID), + getC_Currency_ID(), grossAmt, null); + if (serviceAmt.signum() != 0) + fact.createLine(null, MAccount.get(getCtx(), receivablesServices_ID), + getC_Currency_ID(), serviceAmt, null); + } + // ARC + else if (getDocumentType().equals(DOCTYPE_ARCredit)) + { + BigDecimal grossAmt = getAmount(Doc.AMTTYPE_Gross); + BigDecimal serviceAmt = Env.ZERO; + + // Header Charge DR + BigDecimal amt = getAmount(Doc.AMTTYPE_Charge); + if (amt != null && amt.signum() != 0) + fact.createLine(null, getAccount(Doc.ACCTTYPE_Charge, as), + getC_Currency_ID(), amt, null); + // TaxDue DR + for (int i = 0; i < m_taxes.length; i++) + { + amt = m_taxes[i].getAmount(); + if (amt != null && amt.signum() != 0) + { + FactLine tl = fact.createLine(null, m_taxes[i].getAccount(DocTax.ACCTTYPE_TaxDue, as), + getC_Currency_ID(), amt, null); + if (tl != null) + tl.setC_Tax_ID(m_taxes[i].getC_Tax_ID()); + } + } + // Revenue CR + for (int i = 0; i < p_lines.length; i++) + { + amt = p_lines[i].getAmtSource(); + BigDecimal dAmt = null; + if (as.isTradeDiscountPosted()) + { + BigDecimal discount = p_lines[i].getDiscount(); + if (discount != null && discount.signum() != 0) + { + amt = amt.add(discount); + dAmt = discount; + } + } + fact.createLine (p_lines[i], + p_lines[i].getAccount (ProductCost.ACCTTYPE_P_Revenue, as), + getC_Currency_ID(), amt, dAmt); + if (!p_lines[i].isItem()) + { + grossAmt = grossAmt.subtract(amt); + serviceAmt = serviceAmt.add(amt); + } + } + // Set Locations + FactLine[] fLines = fact.getLines(); + for (int i = 0; i < fLines.length; i++) + { + if (fLines[i] != null) + { + fLines[i].setLocationFromOrg(fLines[i].getAD_Org_ID(), true); // from Loc + fLines[i].setLocationFromBPartner(getC_BPartner_Location_ID(), false); // to Loc + } + } + // Receivables CR + int receivables_ID = getValidCombination_ID (Doc.ACCTTYPE_C_Receivable, as); + int receivablesServices_ID = getValidCombination_ID (Doc.ACCTTYPE_C_Receivable_Services, as); + if (m_allLinesItem || !as.isPostServices() + || receivables_ID == receivablesServices_ID) + { + grossAmt = getAmount(Doc.AMTTYPE_Gross); + serviceAmt = Env.ZERO; + } + else if (m_allLinesService) + { + serviceAmt = getAmount(Doc.AMTTYPE_Gross); + grossAmt = Env.ZERO; + } + if (grossAmt.signum() != 0) + fact.createLine(null, MAccount.get(getCtx(), receivables_ID), + getC_Currency_ID(), null, grossAmt); + if (serviceAmt.signum() != 0) + fact.createLine(null, MAccount.get(getCtx(), receivablesServices_ID), + getC_Currency_ID(), null, serviceAmt); + } + + // ** API + else if (getDocumentType().equals(DOCTYPE_APInvoice)) + { + BigDecimal grossAmt = getAmount(Doc.AMTTYPE_Gross); + BigDecimal serviceAmt = Env.ZERO; + + // Charge DR + fact.createLine(null, getAccount(Doc.ACCTTYPE_Charge, as), + getC_Currency_ID(), getAmount(Doc.AMTTYPE_Charge), null); + // TaxCredit DR + for (int i = 0; i < m_taxes.length; i++) + { + FactLine tl = fact.createLine(null, m_taxes[i].getAccount(m_taxes[i].getAPTaxType(), as), + getC_Currency_ID(), m_taxes[i].getAmount(), null); + if (tl != null) + tl.setC_Tax_ID(m_taxes[i].getC_Tax_ID()); + } + // Expense DR + for (int i = 0; i < p_lines.length; i++) + { + DocLine line = p_lines[i]; + boolean landedCost = landedCost(as, fact, line, true); + if (landedCost && as.isExplicitCostAdjustment()) + { + fact.createLine (line, line.getAccount(ProductCost.ACCTTYPE_P_Expense, as), + getC_Currency_ID(), line.getAmtSource(), null); + // + FactLine fl = fact.createLine (line, line.getAccount(ProductCost.ACCTTYPE_P_Expense, as), + getC_Currency_ID(), null, line.getAmtSource()); + String desc = line.getDescription(); + if (desc == null) + desc = "100%"; + else + desc += " 100%"; + fl.setDescription(desc); + } + if (!landedCost) + { + MAccount expense = line.getAccount(ProductCost.ACCTTYPE_P_Expense, as); + if (line.isItem()) + expense = line.getAccount (ProductCost.ACCTTYPE_P_InventoryClearing, as); + BigDecimal amt = line.getAmtSource(); + BigDecimal dAmt = null; + if (as.isTradeDiscountPosted() && !line.isItem()) + { + BigDecimal discount = line.getDiscount(); + if (discount != null && discount.signum() != 0) + { + amt = amt.add(discount); + dAmt = discount; + } + } + fact.createLine (line, expense, + getC_Currency_ID(), amt, dAmt); + if (!line.isItem()) + { + grossAmt = grossAmt.subtract(amt); + serviceAmt = serviceAmt.add(amt); + } + // + if (line.getM_Product_ID() != 0 + && line.getProduct().isService()) // otherwise Inv Matching + MCostDetail.createInvoice(as, line.getAD_Org_ID(), + line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), + line.get_ID(), 0, // No Cost Element + line.getAmtSource(), line.getQty(), + line.getDescription(), getTrxName()); + } + } + // Set Locations + FactLine[] fLines = fact.getLines(); + for (int i = 0; i < fLines.length; i++) + { + if (fLines[i] != null) + { + fLines[i].setLocationFromBPartner(getC_BPartner_Location_ID(), true); // from Loc + fLines[i].setLocationFromOrg(fLines[i].getAD_Org_ID(), false); // to Loc + } + } + + // Liability CR + int payables_ID = getValidCombination_ID (Doc.ACCTTYPE_V_Liability, as); + int payablesServices_ID = getValidCombination_ID (Doc.ACCTTYPE_V_Liability_Services, as); + if (m_allLinesItem || !as.isPostServices() + || payables_ID == payablesServices_ID) + { + grossAmt = getAmount(Doc.AMTTYPE_Gross); + serviceAmt = Env.ZERO; + } + else if (m_allLinesService) + { + serviceAmt = getAmount(Doc.AMTTYPE_Gross); + grossAmt = Env.ZERO; + } + if (grossAmt.signum() != 0) + fact.createLine(null, MAccount.get(getCtx(), payables_ID), + getC_Currency_ID(), null, grossAmt); + if (serviceAmt.signum() != 0) + fact.createLine(null, MAccount.get(getCtx(), payablesServices_ID), + getC_Currency_ID(), null, serviceAmt); + // + updateProductPO(as); // Only API + updateProductInfo (as.getC_AcctSchema_ID()); // only API + } + // APC + else if (getDocumentType().equals(DOCTYPE_APCredit)) + { + BigDecimal grossAmt = getAmount(Doc.AMTTYPE_Gross); + BigDecimal serviceAmt = Env.ZERO; + // Charge CR + fact.createLine (null, getAccount(Doc.ACCTTYPE_Charge, as), + getC_Currency_ID(), null, getAmount(Doc.AMTTYPE_Charge)); + // TaxCredit CR + for (int i = 0; i < m_taxes.length; i++) + { + FactLine tl = fact.createLine (null, m_taxes[i].getAccount(m_taxes[i].getAPTaxType(), as), + getC_Currency_ID(), null, m_taxes[i].getAmount()); + if (tl != null) + tl.setC_Tax_ID(m_taxes[i].getC_Tax_ID()); + } + // Expense CR + for (int i = 0; i < p_lines.length; i++) + { + DocLine line = p_lines[i]; + boolean landedCost = landedCost(as, fact, line, false); + if (landedCost && as.isExplicitCostAdjustment()) + { + fact.createLine (line, line.getAccount(ProductCost.ACCTTYPE_P_Expense, as), + getC_Currency_ID(), null, line.getAmtSource()); + // + FactLine fl = fact.createLine (line, line.getAccount(ProductCost.ACCTTYPE_P_Expense, as), + getC_Currency_ID(), line.getAmtSource(), null); + String desc = line.getDescription(); + if (desc == null) + desc = "100%"; + else + desc += " 100%"; + fl.setDescription(desc); + } + if (!landedCost) + { + MAccount expense = line.getAccount(ProductCost.ACCTTYPE_P_Expense, as); + if (line.isItem()) + expense = line.getAccount (ProductCost.ACCTTYPE_P_InventoryClearing, as); + BigDecimal amt = line.getAmtSource(); + BigDecimal dAmt = null; + if (as.isTradeDiscountPosted() && !line.isItem()) + { + BigDecimal discount = line.getDiscount(); + if (discount != null && discount.signum() != 0) + { + amt = amt.add(discount); + dAmt = discount; + } + } + fact.createLine (line, expense, + getC_Currency_ID(), dAmt, amt); + if (!line.isItem()) + { + grossAmt = grossAmt.subtract(amt); + serviceAmt = serviceAmt.add(amt); + } + // + if (line.getM_Product_ID() != 0 + && line.getProduct().isService()) // otherwise Inv Matching + MCostDetail.createInvoice(as, line.getAD_Org_ID(), + line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), + line.get_ID(), 0, // No Cost Element + line.getAmtSource().negate(), line.getQty(), + line.getDescription(), getTrxName()); + } + } + // Set Locations + FactLine[] fLines = fact.getLines(); + for (int i = 0; i < fLines.length; i++) + { + if (fLines[i] != null) + { + fLines[i].setLocationFromBPartner(getC_BPartner_Location_ID(), true); // from Loc + fLines[i].setLocationFromOrg(fLines[i].getAD_Org_ID(), false); // to Loc + } + } + // Liability DR + int payables_ID = getValidCombination_ID (Doc.ACCTTYPE_V_Liability, as); + int payablesServices_ID = getValidCombination_ID (Doc.ACCTTYPE_V_Liability_Services, as); + if (m_allLinesItem || !as.isPostServices() + || payables_ID == payablesServices_ID) + { + grossAmt = getAmount(Doc.AMTTYPE_Gross); + serviceAmt = Env.ZERO; + } + else if (m_allLinesService) + { + serviceAmt = getAmount(Doc.AMTTYPE_Gross); + grossAmt = Env.ZERO; + } + if (grossAmt.signum() != 0) + fact.createLine(null, MAccount.get(getCtx(), payables_ID), + getC_Currency_ID(), grossAmt, null); + if (serviceAmt.signum() != 0) + fact.createLine(null, MAccount.get(getCtx(), payablesServices_ID), + getC_Currency_ID(), serviceAmt, null); + } + else + { + p_Error = "DocumentType unknown: " + getDocumentType(); + log.log(Level.SEVERE, p_Error); + fact = null; + } + // + facts.add(fact); + return facts; + } // createFact + + /** + * Create Fact Cash Based (i.e. only revenue/expense) + * @param as accounting schema + * @param fact fact to add lines to + * @param multiplier source amount multiplier + * @return accounted amount + */ + public BigDecimal createFactCash (MAcctSchema as, Fact fact, BigDecimal multiplier) + { + boolean creditMemo = getDocumentType().equals(DOCTYPE_ARCredit) + || getDocumentType().equals(DOCTYPE_APCredit); + boolean payables = getDocumentType().equals(DOCTYPE_APInvoice) + || getDocumentType().equals(DOCTYPE_APCredit); + BigDecimal acctAmt = Env.ZERO; + FactLine fl = null; + // Revenue/Cost + for (int i = 0; i < p_lines.length; i++) + { + DocLine line = p_lines[i]; + boolean landedCost = false; + if (payables) + landedCost = landedCost(as, fact, line, false); + if (landedCost && as.isExplicitCostAdjustment()) + { + fact.createLine (line, line.getAccount(ProductCost.ACCTTYPE_P_Expense, as), + getC_Currency_ID(), null, line.getAmtSource()); + // + fl = fact.createLine (line, line.getAccount(ProductCost.ACCTTYPE_P_Expense, as), + getC_Currency_ID(), line.getAmtSource(), null); + String desc = line.getDescription(); + if (desc == null) + desc = "100%"; + else + desc += " 100%"; + fl.setDescription(desc); + } + if (!landedCost) + { + MAccount acct = line.getAccount( + payables ? ProductCost.ACCTTYPE_P_Expense : ProductCost.ACCTTYPE_P_Revenue, as); + if (payables) + { + // if Fixed Asset + if (line.isItem()) + acct = line.getAccount (ProductCost.ACCTTYPE_P_InventoryClearing, as); + } + BigDecimal amt = line.getAmtSource().multiply(multiplier); + BigDecimal amt2 = null; + if (creditMemo) + { + amt2 = amt; + amt = null; + } + if (payables) // Vendor = DR + fl = fact.createLine (line, acct, + getC_Currency_ID(), amt, amt2); + else // Customer = CR + fl = fact.createLine (line, acct, + getC_Currency_ID(), amt2, amt); + if (fl != null) + acctAmt = acctAmt.add(fl.getAcctBalance()); + } + } + // Tax + for (int i = 0; i < m_taxes.length; i++) + { + BigDecimal amt = m_taxes[i].getAmount(); + BigDecimal amt2 = null; + if (creditMemo) + { + amt2 = amt; + amt = null; + } + FactLine tl = null; + if (payables) + tl = fact.createLine (null, m_taxes[i].getAccount(m_taxes[i].getAPTaxType(), as), + getC_Currency_ID(), amt, amt2); + else + tl = fact.createLine (null, m_taxes[i].getAccount(DocTax.ACCTTYPE_TaxDue, as), + getC_Currency_ID(), amt2, amt); + if (tl != null) + tl.setC_Tax_ID(m_taxes[i].getC_Tax_ID()); + } + // Set Locations + FactLine[] fLines = fact.getLines(); + for (int i = 0; i < fLines.length; i++) + { + if (fLines[i] != null) + { + if (payables) + { + fLines[i].setLocationFromBPartner(getC_BPartner_Location_ID(), true); // from Loc + fLines[i].setLocationFromOrg(fLines[i].getAD_Org_ID(), false); // to Loc + } + else + { + fLines[i].setLocationFromOrg(fLines[i].getAD_Org_ID(), true); // from Loc + fLines[i].setLocationFromBPartner(getC_BPartner_Location_ID(), false); // to Loc + } + } + } + return acctAmt; + } // createFactCash + + + /** + * Create Landed Cost accounting & Cost lines + * @param as accounting schema + * @param fact fact + * @param line document line + * @param dr DR entry (normal api) + * @return true if landed costs were created + */ + private boolean landedCost (MAcctSchema as, Fact fact, DocLine line, boolean dr) + { + int C_InvoiceLine_ID = line.get_ID(); + MLandedCostAllocation[] lcas = MLandedCostAllocation.getOfInvoiceLine( + getCtx(), C_InvoiceLine_ID, getTrxName()); + if (lcas.length == 0) + return false; + + // Delete Old + String sql = "DELETE M_CostDetail WHERE C_InvoiceLine_ID=" + C_InvoiceLine_ID; + int no = DB.executeUpdate(sql, getTrxName()); + if (no != 0) + log.config("CostDetail Deleted #" + no); + + // Calculate Total Base + double totalBase = 0; + for (int i = 0; i < lcas.length; i++) + totalBase += lcas[i].getBase().doubleValue(); + + // Create New + MInvoiceLine il = new MInvoiceLine (getCtx(), C_InvoiceLine_ID, getTrxName()); + for (int i = 0; i < lcas.length; i++) + { + MLandedCostAllocation lca = lcas[i]; + if (lca.getBase().signum() == 0) + continue; + double percent = totalBase / lca.getBase().doubleValue(); + String desc = il.getDescription(); + if (desc == null) + desc = percent + "%"; + else + desc += " - " + percent + "%"; + if (line.getDescription() != null) + desc += " - " + line.getDescription(); + + // Accounting + ProductCost pc = new ProductCost (Env.getCtx(), + lca.getM_Product_ID(), lca.getM_AttributeSetInstance_ID(), getTrxName()); + BigDecimal drAmt = null; + BigDecimal crAmt = null; + if (dr) + drAmt = lca.getAmt(); + else + crAmt = lca.getAmt(); + FactLine fl = fact.createLine (line, pc.getAccount(ProductCost.ACCTTYPE_P_CostAdjustment, as), + getC_Currency_ID(), drAmt, crAmt); + fl.setDescription(desc); + + // Cost Detail - Convert to AcctCurrency + BigDecimal allocationAmt = lca.getAmt(); + if (getC_Currency_ID() != as.getC_Currency_ID()) + allocationAmt = MConversionRate.convert(getCtx(), allocationAmt, + getC_Currency_ID(), as.getC_Currency_ID(), + getDateAcct(), getC_ConversionType_ID(), + getAD_Client_ID(), getAD_Org_ID()); + if (allocationAmt.scale() > as.getCostingPrecision()) + allocationAmt = allocationAmt.setScale(as.getCostingPrecision(), BigDecimal.ROUND_HALF_UP); + if (!dr) + allocationAmt = allocationAmt.negate(); + MCostDetail cd = new MCostDetail (as, lca.getAD_Org_ID(), + lca.getM_Product_ID(), lca.getM_AttributeSetInstance_ID(), + lca.getM_CostElement_ID(), + allocationAmt, Env.ZERO, // Qty + desc, getTrxName()); + cd.setC_InvoiceLine_ID(C_InvoiceLine_ID); + boolean ok = cd.save(); + if (ok && !cd.isProcessed()) + { + MClient client = MClient.get(as.getCtx(), as.getAD_Client_ID()); + if (client.isCostImmediate()) + cd.process(); + } + } + + log.config("Created #" + lcas.length); + return true; + } // landedCosts + + /** + * Update ProductPO PriceLastInv + * @param as accounting schema + */ + private void updateProductPO (MAcctSchema as) + { + MClientInfo ci = MClientInfo.get(getCtx(), as.getAD_Client_ID()); + if (ci.getC_AcctSchema1_ID() != as.getC_AcctSchema_ID()) + return; + + StringBuffer sql = new StringBuffer ( + "UPDATE M_Product_PO po " + + "SET PriceLastInv = " + // select + + "(SELECT currencyConvert(il.PriceActual,i.C_Currency_ID,po.C_Currency_ID,i.DateInvoiced,i.C_ConversionType_ID,i.AD_Client_ID,i.AD_Org_ID) " + + "FROM C_Invoice i, C_InvoiceLine il " + + "WHERE i.C_Invoice_ID=il.C_Invoice_ID" + + " AND po.M_Product_ID=il.M_Product_ID AND po.C_BPartner_ID=i.C_BPartner_ID" + + " AND ROWNUM=1 AND i.C_Invoice_ID=").append(get_ID()).append(") ") + // update + .append("WHERE EXISTS (SELECT * " + + "FROM C_Invoice i, C_InvoiceLine il " + + "WHERE i.C_Invoice_ID=il.C_Invoice_ID" + + " AND po.M_Product_ID=il.M_Product_ID AND po.C_BPartner_ID=i.C_BPartner_ID" + + " AND i.C_Invoice_ID=").append(get_ID()).append(")"); + int no = DB.executeUpdate(sql.toString(), getTrxName()); + log.fine("Updated=" + no); + } // updateProductPO + + /** + * Update Product Info (old). + * - Costing (PriceLastInv) + * - PO (PriceLastInv) + * @param C_AcctSchema_ID accounting schema + * @deprecated old costing + */ + private void updateProductInfo (int C_AcctSchema_ID) + { + log.fine("C_Invoice_ID=" + get_ID()); + + /** @todo Last.. would need to compare document/last updated date + * would need to maintain LastPriceUpdateDate on _PO and _Costing */ + + // update Product Costing + // requires existence of currency conversion !! + // if there are multiple lines of the same product last price uses first + // -> TotalInvAmt is sometimes NULL !! -> error + // begin globalqss 2005-10-19 + // postgresql doesn't support LIMIT on UPDATE or DELETE statements + /* + StringBuffer sql = new StringBuffer ( + "UPDATE M_Product_Costing pc " + + "SET (PriceLastInv, TotalInvAmt,TotalInvQty) = " + // select + + "(SELECT currencyConvert(il.PriceActual,i.C_Currency_ID,a.C_Currency_ID,i.DateInvoiced,i.C_ConversionType_ID,i.AD_Client_ID,i.AD_Org_ID)," + + " currencyConvert(il.LineNetAmt,i.C_Currency_ID,a.C_Currency_ID,i.DateInvoiced,i.C_ConversionType_ID,i.AD_Client_ID,i.AD_Org_ID),il.QtyInvoiced " + + "FROM C_Invoice i, C_InvoiceLine il, C_AcctSchema a " + + "WHERE i.C_Invoice_ID=il.C_Invoice_ID" + + " AND pc.M_Product_ID=il.M_Product_ID AND pc.C_AcctSchema_ID=a.C_AcctSchema_ID" + + " AND ROWNUM=1" + + " AND pc.C_AcctSchema_ID=").append(C_AcctSchema_ID).append(" AND i.C_Invoice_ID=") + .append(get_ID()).append(") ") + // update + .append("WHERE EXISTS (SELECT * " + + "FROM C_Invoice i, C_InvoiceLine il, C_AcctSchema a " + + "WHERE i.C_Invoice_ID=il.C_Invoice_ID" + + " AND pc.M_Product_ID=il.M_Product_ID AND pc.C_AcctSchema_ID=a.C_AcctSchema_ID" + + " AND pc.C_AcctSchema_ID=").append(C_AcctSchema_ID).append(" AND i.C_Invoice_ID=") + .append(get_ID()).append(")"); + */ + // the next command is equivalent and works in postgresql and oracle + StringBuffer sql = new StringBuffer ( + "UPDATE M_Product_Costing pc " + + "SET (PriceLastInv, TotalInvAmt,TotalInvQty) = " + // select + + "(SELECT currencyConvert(il.PriceActual,i.C_Currency_ID,a.C_Currency_ID,i.DateInvoiced,i.C_ConversionType_ID,i.AD_Client_ID,i.AD_Org_ID)," + + " currencyConvert(il.LineNetAmt,i.C_Currency_ID,a.C_Currency_ID,i.DateInvoiced,i.C_ConversionType_ID,i.AD_Client_ID,i.AD_Org_ID),il.QtyInvoiced " + + "FROM C_Invoice i, C_InvoiceLine il, C_AcctSchema a " + + "WHERE i.C_Invoice_ID=il.C_Invoice_ID" + + " AND il.c_invoiceline_id = (SELECT MIN(C_InvoiceLine_ID) FROM C_InvoiceLine WHERE C_Invoice_ID=") + .append(get_ID()).append(")" + + " AND pc.M_Product_ID=il.M_Product_ID AND pc.C_AcctSchema_ID=a.C_AcctSchema_ID" + + " AND pc.C_AcctSchema_ID=").append(C_AcctSchema_ID).append(" AND i.C_Invoice_ID=") + .append(get_ID()).append(") ") + // update + .append("WHERE EXISTS (SELECT * " + + "FROM C_Invoice i, C_InvoiceLine il, C_AcctSchema a " + + "WHERE i.C_Invoice_ID=il.C_Invoice_ID" + + " AND pc.M_Product_ID=il.M_Product_ID AND pc.C_AcctSchema_ID=a.C_AcctSchema_ID" + + " AND pc.C_AcctSchema_ID=").append(C_AcctSchema_ID).append(" AND i.C_Invoice_ID=") + .append(get_ID()).append(")"); + // end globalqss 2005-10-19 + int no = DB.executeUpdate(sql.toString(), getTrxName()); + log.fine("M_Product_Costing - Updated=" + no); + } // updateProductInfo + +} // Doc_Invoice diff --git a/serverRoot/src/main/server/org/compiere/acct/Doc_MatchInv.java b/serverRoot/src/main/server/org/compiere/acct/Doc_MatchInv.java new file mode 100644 index 0000000000..f6e253ce71 --- /dev/null +++ b/serverRoot/src/main/server/org/compiere/acct/Doc_MatchInv.java @@ -0,0 +1,327 @@ +/****************************************************************************** + * 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.acct; + +import java.math.*; +import java.sql.*; +import java.util.*; +import org.compiere.model.*; +import org.compiere.util.*; + +/** + * Post MatchInv Documents. + *
+ *  Table:              M_MatchInv (472)
+ *  Document Types:     MXI
+ *  
+ * Update Costing Records + * @author Jorg Janke + * @version $Id: Doc_MatchInv.java,v 1.3 2006/07/30 00:53:33 jjanke Exp $ + */ +public class Doc_MatchInv extends Doc +{ + /** + * Constructor + * @param ass accounting schemata + * @param rs record + * @param trxName trx + */ + protected Doc_MatchInv (MAcctSchema[] ass, ResultSet rs, String trxName) + { + super(ass, MMatchInv.class, rs, DOCTYPE_MatMatchInv, trxName); + } // Doc_MatchInv + + /** Invoice Line */ + private MInvoiceLine m_invoiceLine = null; + /** Material Receipt */ + private MInOutLine m_receiptLine = null; + + private ProductCost m_pc = null; + + /** Commitments */ + private DocLine[] m_commitments = null; + + /** + * Load Specific Document Details + * @return error message or null + */ + protected String loadDocumentDetails () + { + setC_Currency_ID (Doc.NO_CURRENCY); + MMatchInv matchInv = (MMatchInv)getPO(); + setDateDoc(matchInv.getDateTrx()); + setQty (matchInv.getQty()); + // Invoice Info + int C_InvoiceLine_ID = matchInv.getC_InvoiceLine_ID(); + m_invoiceLine = new MInvoiceLine (getCtx(), C_InvoiceLine_ID, null); + // BP for NotInvoicedReceipts + int C_BPartner_ID = m_invoiceLine.getParent().getC_BPartner_ID(); + setC_BPartner_ID(C_BPartner_ID); + // + int M_InOutLine_ID = matchInv.getM_InOutLine_ID(); + m_receiptLine = new MInOutLine (getCtx(), M_InOutLine_ID, null); + // + m_pc = new ProductCost (Env.getCtx(), + getM_Product_ID(), matchInv.getM_AttributeSetInstance_ID(), null); + m_pc.setQty(getQty()); + + return null; + } // loadDocumentDetails + + + /************************************************************************** + * Get Source Currency Balance - subtracts line and tax amounts from total - no rounding + * @return Zero (always balanced) + */ + public BigDecimal getBalance() + { + return Env.ZERO; + } // getBalance + + + /** + * Create Facts (the accounting logic) for + * MXI. + * (single line) + *
+	 *      NotInvoicedReceipts     DR			(Receipt Org)
+	 *      InventoryClearing               CR
+	 *      InvoicePV               DR      CR  (difference)
+	 *  Commitment
+	 * 		Expense							CR
+	 * 		Offset					DR
+	 *  
+ * @param as accounting schema + * @return Fact + */ + public ArrayList createFacts (MAcctSchema as) + { + ArrayList facts = new ArrayList(); + // Nothing to do + if (getM_Product_ID() == 0 // no Product + || getQty().signum() == 0 + || m_receiptLine.getMovementQty().signum() == 0) // Qty = 0 + { + log.fine("No Product/Qty - M_Product_ID=" + getM_Product_ID() + + ",Qty=" + getQty() + ",InOutQty=" + m_receiptLine.getMovementQty()); + return facts; + } + MMatchInv matchInv = (MMatchInv)getPO(); + + // create Fact Header + Fact fact = new Fact(this, as, Fact.POST_Actual); + setC_Currency_ID (as.getC_Currency_ID()); + + /** Needs to be handeled in PO Matching as no Receipt info + if (m_pc.isService()) + { + log.fine("Service - skipped"); + return fact; + } + **/ + + + // NotInvoicedReceipt DR + // From Receipt + BigDecimal multiplier = getQty() + .divide(m_receiptLine.getMovementQty(), 12, BigDecimal.ROUND_HALF_UP) + .abs(); + FactLine dr = fact.createLine (null, + getAccount(Doc.ACCTTYPE_NotInvoicedReceipts, as), + as.getC_Currency_ID(), Env.ONE, null); // updated below + if (dr == null) + { + p_Error = "No Product Costs"; + return null; + } + dr.setQty(getQty()); + // dr.setM_Locator_ID(m_receiptLine.getM_Locator_ID()); + // MInOut receipt = m_receiptLine.getParent(); + // dr.setLocationFromBPartner(receipt.getC_BPartner_Location_ID(), true); // from Loc + // dr.setLocationFromLocator(m_receiptLine.getM_Locator_ID(), false); // to Loc + BigDecimal temp = dr.getAcctBalance(); + // Set AmtAcctCr/Dr from Receipt (sets also Project) + if (!dr.updateReverseLine (MInOut.Table_ID, // Amt updated + m_receiptLine.getM_InOut_ID(), m_receiptLine.getM_InOutLine_ID(), + multiplier)) + { + p_Error = "Mat.Receipt not posted yet"; + return null; + } + log.fine("CR - Amt(" + temp + "->" + dr.getAcctBalance() + + ") - " + dr.toString()); + + // InventoryClearing CR + // From Invoice + MAccount expense = m_pc.getAccount(ProductCost.ACCTTYPE_P_InventoryClearing, as); + if (m_pc.isService()) + expense = m_pc.getAccount(ProductCost.ACCTTYPE_P_Expense, as); + BigDecimal LineNetAmt = m_invoiceLine.getLineNetAmt(); + multiplier = getQty() + .divide(m_invoiceLine.getQtyInvoiced(), 12, BigDecimal.ROUND_HALF_UP) + .abs(); + if (multiplier.compareTo(Env.ONE) != 0) + LineNetAmt = LineNetAmt.multiply(multiplier); + if (m_pc.isService()) + LineNetAmt = dr.getAcctBalance(); // book out exact receipt amt + FactLine cr = null; + if (as.isAccrual()) + { + cr = fact.createLine (null, expense, + as.getC_Currency_ID(), null, LineNetAmt); // updated below + if (cr == null) + { + log.fine("Line Net Amt=0 - M_Product_ID=" + getM_Product_ID() + + ",Qty=" + getQty() + ",InOutQty=" + m_receiptLine.getMovementQty()); + facts.add(fact); + return facts; + } + cr.setQty(getQty().negate()); + temp = cr.getAcctBalance(); + // Set AmtAcctCr/Dr from Invoice (sets also Project) + if (as.isAccrual() && !cr.updateReverseLine (MInvoice.Table_ID, // Amt updated + m_invoiceLine.getC_Invoice_ID(), m_invoiceLine.getC_InvoiceLine_ID(), multiplier)) + { + p_Error = "Invoice not posted yet"; + return null; + } + log.fine("DR - Amt(" + temp + "->" + cr.getAcctBalance() + + ") - " + cr.toString()); + } + else // Cash Acct + { + MInvoice invoice = m_invoiceLine.getParent(); + if (as.getC_Currency_ID() == invoice.getC_Currency_ID()) + LineNetAmt = MConversionRate.convert(getCtx(), LineNetAmt, + invoice.getC_Currency_ID(), as.getC_Currency_ID(), + invoice.getDateAcct(), invoice.getC_ConversionType_ID(), + invoice.getAD_Client_ID(), invoice.getAD_Org_ID()); + cr = fact.createLine (null, expense, + as.getC_Currency_ID(), null, LineNetAmt); + cr.setQty(getQty().multiply(multiplier).negate()); + } + cr.setC_Activity_ID(m_invoiceLine.getC_Activity_ID()); + cr.setC_Campaign_ID(m_invoiceLine.getC_Campaign_ID()); + cr.setC_Project_ID(m_invoiceLine.getC_Project_ID()); + cr.setC_UOM_ID(m_invoiceLine.getC_UOM_ID()); + cr.setUser1_ID(m_invoiceLine.getUser1_ID()); + cr.setUser2_ID(m_invoiceLine.getUser2_ID()); + + + // Invoice Price Variance difference + BigDecimal ipv = cr.getAcctBalance().add(dr.getAcctBalance()).negate(); + if (ipv.signum() != 0) + { + FactLine pv = fact.createLine(null, + m_pc.getAccount(ProductCost.ACCTTYPE_P_IPV, as), + as.getC_Currency_ID(), ipv); + pv.setC_Activity_ID(m_invoiceLine.getC_Activity_ID()); + pv.setC_Campaign_ID(m_invoiceLine.getC_Campaign_ID()); + pv.setC_Project_ID(m_invoiceLine.getC_Project_ID()); + pv.setC_UOM_ID(m_invoiceLine.getC_UOM_ID()); + pv.setUser1_ID(m_invoiceLine.getUser1_ID()); + pv.setUser2_ID(m_invoiceLine.getUser2_ID()); + } + log.fine("IPV=" + ipv + "; Balance=" + fact.getSourceBalance()); + + // Cost Detail Record - data from Expense/IncClearing (CR) record + MCostDetail.createInvoice(as, getAD_Org_ID(), + getM_Product_ID(), matchInv.getM_AttributeSetInstance_ID(), + m_invoiceLine.getC_InvoiceLine_ID(), 0, // No cost element + cr.getAcctBalance().negate(), getQty(), // correcting + getDescription(), getTrxName()); + + // Update Costing + updateProductInfo(as.getC_AcctSchema_ID(), + MAcctSchema.COSTINGMETHOD_StandardCosting.equals(as.getCostingMethod())); + // + facts.add(fact); + + /** Commitment release ****/ + if (as.isAccrual() && as.isCreateCommitment()) + { + fact = Doc_Order.getCommitmentRelease(as, this, + getQty(), m_invoiceLine.getC_InvoiceLine_ID(), Env.ONE); + if (fact == null) + return null; + facts.add(fact); + } // Commitment + + return facts; + } // createFact + + /** + * Update Product Info (old). + * - Costing (CostStandardCumQty, CostStandardCumAmt, CostAverageCumQty, CostAverageCumAmt) + * @param C_AcctSchema_ID accounting schema + * @param standardCosting true if std costing + * @return true if updated + * @deprecated old costing + */ + private boolean updateProductInfo (int C_AcctSchema_ID, boolean standardCosting) + { + log.fine("M_MatchInv_ID=" + get_ID()); + + // update Product Costing Qty/Amt + // requires existence of currency conversion !! + StringBuffer sql = new StringBuffer ( + "UPDATE M_Product_Costing pc " + + "SET (CostStandardCumQty,CostStandardCumAmt, CostAverageCumQty,CostAverageCumAmt) = " + + "(SELECT pc.CostStandardCumQty + m.Qty," + + "pc.CostStandardCumAmt + currencyConvert(il.PriceActual,i.C_Currency_ID,a.C_Currency_ID,i.DateInvoiced,i.C_ConversionType_ID,i.AD_Client_ID,i.AD_Org_ID)*m.Qty, " + + "pc.CostAverageCumQty + m.Qty," + + "pc.CostAverageCumAmt + currencyConvert(il.PriceActual,i.C_Currency_ID,a.C_Currency_ID,i.DateInvoiced,i.C_ConversionType_ID,i.AD_Client_ID,i.AD_Org_ID)*m.Qty " + + "FROM M_MatchInv m" + + " INNER JOIN C_InvoiceLine il ON (m.C_InvoiceLine_ID=il.C_InvoiceLine_ID)" + + " INNER JOIN C_Invoice i ON (il.C_Invoice_ID=i.C_Invoice_ID)," + + " C_AcctSchema a " + + "WHERE pc.C_AcctSchema_ID=a.C_AcctSchema_ID" + + " AND pc.M_Product_ID=m.M_Product_ID" + + " AND m.M_MatchInv_ID=").append(get_ID()).append(")" + // + + "WHERE pc.C_AcctSchema_ID=").append(C_AcctSchema_ID).append( + " AND EXISTS (SELECT * FROM M_MatchInv m " + + "WHERE pc.M_Product_ID=m.M_Product_ID" + + " AND m.M_MatchInv_ID=").append(get_ID()).append(")"); + int no = DB.executeUpdate(sql.toString(), getTrxName()); + log.fine("M_Product_Costing - Qty/Amt Updated #=" + no); + + // Update Average Cost + sql = new StringBuffer ( + "UPDATE M_Product_Costing " + + "SET CostAverage = CostAverageCumAmt/DECODE(CostAverageCumQty, 0,1, CostAverageCumQty) " + + "WHERE C_AcctSchema_ID=").append(C_AcctSchema_ID) + .append(" AND M_Product_ID=").append(getM_Product_ID()); + no = DB.executeUpdate(sql.toString(), getTrxName()); + log.fine("M_Product_Costing - AvgCost Updated #=" + no); + + + // Update Current Cost + if (!standardCosting) + { + sql = new StringBuffer ( + "UPDATE M_Product_Costing " + + "SET CurrentCostPrice = CostAverage " + + "WHERE C_AcctSchema_ID=").append(C_AcctSchema_ID) + .append(" AND M_Product_ID=").append(getM_Product_ID()); + no = DB.executeUpdate(sql.toString(), getTrxName()); + log.fine("M_Product_Costing - CurrentCost Updated=" + no); + } + return true; + } // updateProductInfo + +} // Doc_MatchInv diff --git a/serverRoot/src/main/server/org/compiere/acct/Doc_MatchPO.java b/serverRoot/src/main/server/org/compiere/acct/Doc_MatchPO.java new file mode 100644 index 0000000000..cb2ca28575 --- /dev/null +++ b/serverRoot/src/main/server/org/compiere/acct/Doc_MatchPO.java @@ -0,0 +1,243 @@ +/****************************************************************************** + * 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.acct; + +import java.math.*; +import java.sql.*; +import java.util.*; +import java.util.logging.*; + +import org.compiere.model.*; +import org.compiere.util.*; + +/** + * Post MatchPO Documents. + *
+ *  Table:              C_MatchPO (473)
+ *  Document Types:     MXP
+ *  
+ * @author Jorg Janke + * @version $Id: Doc_MatchPO.java,v 1.3 2006/07/30 00:53:33 jjanke Exp $ + */ +public class Doc_MatchPO extends Doc +{ + /** + * Constructor + * @param ass accounting schemata + * @param rs record + * @param trxName trx + */ + protected Doc_MatchPO (MAcctSchema[] ass, ResultSet rs, String trxName) + { + super(ass, MMatchPO.class, rs, DOCTYPE_MatMatchPO, trxName); + } // Doc_MatchPO + + private int m_C_OrderLine_ID = 0; + private MOrderLine m_oLine = null; + // + private int m_M_InOutLine_ID = 0; + private int m_C_InvoiceLine_ID = 0; + private ProductCost m_pc; + private int m_M_AttributeSetInstance_ID = 0; + + /** + * Load Specific Document Details + * @return error message or null + */ + protected String loadDocumentDetails () + { + setC_Currency_ID (Doc.NO_CURRENCY); + MMatchPO matchPO = (MMatchPO)getPO(); + setDateDoc(matchPO.getDateTrx()); + // + m_M_AttributeSetInstance_ID = matchPO.getM_AttributeSetInstance_ID(); + setQty (matchPO.getQty()); + // + m_C_OrderLine_ID = matchPO.getC_OrderLine_ID(); + m_oLine = new MOrderLine (getCtx(), m_C_OrderLine_ID, getTrxName()); + // + m_M_InOutLine_ID = matchPO.getM_InOutLine_ID(); + m_C_InvoiceLine_ID = matchPO.getC_InvoiceLine_ID(); + // + m_pc = new ProductCost (Env.getCtx(), + getM_Product_ID(), m_M_AttributeSetInstance_ID, getTrxName()); + m_pc.setQty(getQty()); + return null; + } // loadDocumentDetails + + + /************************************************************************** + * Get Source Currency Balance - subtracts line and tax amounts from total - no rounding + * @return Zero - always balanced + */ + public BigDecimal getBalance() + { + return Env.ZERO; + } // getBalance + + + /** + * Create Facts (the accounting logic) for + * MXP. + *
+	 *      Product PPV     
+	 *      PPV_Offset                  
+	 *  
+ * @param as accounting schema + * @return Fact + */ + public ArrayList createFacts (MAcctSchema as) + { + ArrayList facts = new ArrayList(); + // + if (getM_Product_ID() == 0 // Nothing to do if no Product + || getQty().signum() == 0 + || m_M_InOutLine_ID == 0) // No posting if not matched to Shipment + { + log.fine("No Product/Qty - M_Product_ID=" + getM_Product_ID() + + ",Qty=" + getQty()); + return facts; + } + + // create Fact Header + Fact fact = new Fact(this, as, Fact.POST_Actual); + setC_Currency_ID(as.getC_Currency_ID()); + + // Purchase Order Line + BigDecimal poCost = m_oLine.getPriceCost(); + if (poCost == null || poCost.signum() == 0) + poCost = m_oLine.getPriceActual(); + poCost = poCost.multiply(getQty()); // Delivered so far + // Different currency + if (m_oLine.getC_Currency_ID() != as.getC_Currency_ID()) + { + MOrder order = m_oLine.getParent(); + BigDecimal rate = MConversionRate.getRate( + order.getC_Currency_ID(), as.getC_Currency_ID(), + order.getDateAcct(), order.getC_ConversionType_ID(), + m_oLine.getAD_Client_ID(), m_oLine.getAD_Org_ID()); + if (rate == null) + { + p_Error = "Purchase Order not convertible - " + as.getName(); + return null; + } + poCost = poCost.multiply(rate); + if (poCost.scale() > as.getCostingPrecision()) + poCost = poCost.setScale(as.getCostingPrecision(), BigDecimal.ROUND_HALF_UP); + } + + // Create PO Cost Detail Record firs + MCostDetail.createOrder(as, m_oLine.getAD_Org_ID(), + getM_Product_ID(), m_M_AttributeSetInstance_ID, + m_C_OrderLine_ID, 0, // no cost element + poCost, getQty(), // Delivered + m_oLine.getDescription(), getTrxName()); + + // Current Costs + String costingMethod = as.getCostingMethod(); + MProduct product = MProduct.get(getCtx(), getM_Product_ID()); + MProductCategoryAcct pca = MProductCategoryAcct.get(getCtx(), + product.getM_Product_Category_ID(), as.getC_AcctSchema_ID(), getTrxName()); + if (pca.getCostingMethod() != null) + costingMethod = pca.getCostingMethod(); + BigDecimal costs = m_pc.getProductCosts(as, getAD_Org_ID(), + costingMethod, m_C_OrderLine_ID, false); // non-zero costs + + // No Costs yet - no PPV + if (costs == null || costs.signum() == 0) + { + p_Error = "Resubmit - No Costs for " + product.getName(); + log.log(Level.SEVERE, p_Error); + return null; + } + + // Difference + BigDecimal difference = poCost.subtract(costs); + // Nothing to post + if (difference.signum() == 0) + { + log.log(Level.FINE, "No Cost Difference for M_Product_ID=" + getM_Product_ID()); + facts.add(fact); + return facts; + } + + // Product PPV + FactLine cr = fact.createLine(null, + m_pc.getAccount(ProductCost.ACCTTYPE_P_PPV, as), + as.getC_Currency_ID(), difference); + if (cr != null) + { + cr.setQty(getQty()); + cr.setC_BPartner_ID(m_oLine.getC_BPartner_ID()); + cr.setC_Activity_ID(m_oLine.getC_Activity_ID()); + cr.setC_Campaign_ID(m_oLine.getC_Campaign_ID()); + cr.setC_Project_ID(m_oLine.getC_Project_ID()); + cr.setC_UOM_ID(m_oLine.getC_UOM_ID()); + cr.setUser1_ID(m_oLine.getUser1_ID()); + cr.setUser2_ID(m_oLine.getUser2_ID()); + } + + // PPV Offset + FactLine dr = fact.createLine(null, + getAccount(Doc.ACCTTYPE_PPVOffset, as), + as.getC_Currency_ID(), difference.negate()); + if (dr != null) + { + dr.setQty(getQty().negate()); + dr.setC_BPartner_ID(m_oLine.getC_BPartner_ID()); + dr.setC_Activity_ID(m_oLine.getC_Activity_ID()); + dr.setC_Campaign_ID(m_oLine.getC_Campaign_ID()); + dr.setC_Project_ID(m_oLine.getC_Project_ID()); + dr.setC_UOM_ID(m_oLine.getC_UOM_ID()); + dr.setUser1_ID(m_oLine.getUser1_ID()); + dr.setUser2_ID(m_oLine.getUser2_ID()); + } + + // + facts.add(fact); + return facts; + } // createFact + + /** + * Update Product Info (old). + * - Costing (CostStandardPOQty, CostStandardPOAmt) + * @param C_AcctSchema_ID accounting schema + * @deprecated old costing + */ + private void updateProductInfo (int C_AcctSchema_ID) + { + log.fine("M_MatchPO_ID=" + get_ID()); + + // update Product Costing + // requires existence of currency conversion !! + StringBuffer sql = new StringBuffer ( + "UPDATE M_Product_Costing pc " + + "SET (CostStandardPOQty,CostStandardPOAmt) = " + + "(SELECT CostStandardPOQty + m.Qty," + + " CostStandardPOAmt + currencyConvert(ol.PriceActual,ol.C_Currency_ID,a.C_Currency_ID,ol.DateOrdered,null,ol.AD_Client_ID,ol.AD_Org_ID)*m.Qty " + + "FROM M_MatchPO m, C_OrderLine ol, C_AcctSchema a " + + "WHERE m.C_OrderLine_ID=ol.C_OrderLine_ID" + + " AND pc.M_Product_ID=ol.M_Product_ID" + + " AND pc.C_AcctSchema_ID=a.C_AcctSchema_ID" + + "AND m.M_MatchPO_ID=").append(get_ID()).append(") ") + .append("WHERE pc.C_AcctSchema_ID=").append(C_AcctSchema_ID) + .append(" AND pc.M_Product_ID=").append(getM_Product_ID()); + int no = DB.executeUpdate(sql.toString(), getTrxName()); + log.fine("M_Product_Costing - Updated=" + no); + } // updateProductInfo + +} // Doc_MatchPO diff --git a/serverRoot/src/main/server/org/compiere/acct/Doc_Movement.java b/serverRoot/src/main/server/org/compiere/acct/Doc_Movement.java new file mode 100644 index 0000000000..5a2ab5b025 --- /dev/null +++ b/serverRoot/src/main/server/org/compiere/acct/Doc_Movement.java @@ -0,0 +1,179 @@ +/****************************************************************************** + * 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.acct; + +import java.math.*; +import java.sql.*; +import java.util.*; + +import org.compiere.model.*; +import org.compiere.util.*; + +/** + * Post Invoice Documents. + *
+ *  Table:              M_Movement (323)
+ *  Document Types:     MMM
+ *  
+ * @author Jorg Janke + * @version $Id: Doc_Movement.java,v 1.3 2006/07/30 00:53:33 jjanke Exp $ + */ +public class Doc_Movement extends Doc +{ + /** + * Constructor + * @param ass accounting schemata + * @param rs record + * @param trxName trx + */ + public Doc_Movement (MAcctSchema[] ass, ResultSet rs, String trxName) + { + super (ass, MMovement.class, rs, DOCTYPE_MatMovement, trxName); + } // Doc_Movement + + /** + * Load Document Details + * @return error message or null + */ + protected String loadDocumentDetails() + { + setC_Currency_ID(NO_CURRENCY); + MMovement move = (MMovement)getPO(); + setDateDoc (move.getMovementDate()); + setDateAcct(move.getMovementDate()); + // Contained Objects + p_lines = loadLines(move); + log.fine("Lines=" + p_lines.length); + return null; + } // loadDocumentDetails + + /** + * Load Invoice Line + * @param move move + * @return document lines (DocLine_Material) + */ + private DocLine[] loadLines(MMovement move) + { + ArrayList list = new ArrayList(); + MMovementLine[] lines = move.getLines(false); + for (int i = 0; i < lines.length; i++) + { + MMovementLine line = lines[i]; + DocLine docLine = new DocLine (line, this); + docLine.setQty(line.getMovementQty(), false); + // + log.fine(docLine.toString()); + list.add (docLine); + } + + // Return Array + DocLine[] dls = new DocLine[list.size()]; + list.toArray(dls); + return dls; + } // loadLines + + /** + * Get Balance + * @return balance (ZERO) - always balanced + */ + public BigDecimal getBalance() + { + BigDecimal retValue = Env.ZERO; + return retValue; + } // getBalance + + /** + * Create Facts (the accounting logic) for + * MMM. + *
+	 *  Movement
+	 *      Inventory       DR      CR
+	 *      InventoryTo     DR      CR
+	 *  
+ * @param as account schema + * @return Fact + */ + public ArrayList createFacts (MAcctSchema as) + { + // create Fact Header + Fact fact = new Fact(this, as, Fact.POST_Actual); + setC_Currency_ID(as.getC_Currency_ID()); + + // Line pointers + FactLine dr = null; + FactLine cr = null; + + for (int i = 0; i < p_lines.length; i++) + { + DocLine line = p_lines[i]; + BigDecimal costs = line.getProductCosts(as, line.getAD_Org_ID(), false); + + // ** Inventory DR CR + dr = fact.createLine(line, + line.getAccount(ProductCost.ACCTTYPE_P_Asset, as), + as.getC_Currency_ID(), costs.negate()); // from (-) CR + if (dr == null) + continue; + dr.setM_Locator_ID(line.getM_Locator_ID()); + dr.setQty(line.getQty().negate()); // outgoing + + // ** InventoryTo DR CR + cr = fact.createLine(line, + line.getAccount(ProductCost.ACCTTYPE_P_Asset, as), + as.getC_Currency_ID(), costs); // to (+) DR + if (cr == null) + continue; + cr.setM_Locator_ID(line.getM_LocatorTo_ID()); + cr.setQty(line.getQty()); + + // Only for between-org movements + if (dr.getAD_Org_ID() != cr.getAD_Org_ID()) + { + String costingLevel = as.getCostingLevel(); + MProductCategoryAcct pca = MProductCategoryAcct.get(getCtx(), + line.getProduct().getM_Product_Category_ID(), + as.getC_AcctSchema_ID(), getTrxName()); + if (pca.getCostingLevel() != null) + costingLevel = pca.getCostingLevel(); + if (!MAcctSchema.COSTINGLEVEL_Organization.equals(costingLevel)) + continue; + // + String description = line.getDescription(); + if (description == null) + description = ""; + // Cost Detail From + MCostDetail.createMovement(as, dr.getAD_Org_ID(), // locator org + line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), + line.get_ID(), 0, + costs.negate(), line.getQty().negate(), true, + description + "(|->)", getTrxName()); + // Cost Detail To + MCostDetail.createMovement(as, cr.getAD_Org_ID(), // locator org + line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), + line.get_ID(), 0, + costs, line.getQty(), false, + description + "(|<-)", getTrxName()); + } + } + + // + ArrayList facts = new ArrayList(); + facts.add(fact); + return facts; + } // createFact + +} // Doc_Movement diff --git a/serverRoot/src/main/server/org/compiere/acct/Doc_Order.java b/serverRoot/src/main/server/org/compiere/acct/Doc_Order.java new file mode 100644 index 0000000000..05f5ec643e --- /dev/null +++ b/serverRoot/src/main/server/org/compiere/acct/Doc_Order.java @@ -0,0 +1,624 @@ +/****************************************************************************** + * 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.acct; + +import java.math.*; +import java.sql.*; +import java.util.*; +import java.util.logging.*; +import org.compiere.model.*; +import org.compiere.util.*; + +/** + * Post Order Documents. + *
+ *  Table:              C_Order (259)
+ *  Document Types:     SOO, POO
+ *  
+ * @author Jorg Janke + * @version $Id: Doc_Order.java,v 1.3 2006/07/30 00:53:33 jjanke Exp $ + */ +public class Doc_Order extends Doc +{ + /** + * Constructor + * @param ass accounting schemata + * @param rs record + * @param trxName trx + */ + protected Doc_Order (MAcctSchema[] ass, ResultSet rs, String trxName) + { + super (ass, MOrder.class, rs, null, trxName); + } // Doc_Order + + /** Contained Optional Tax Lines */ + private DocTax[] m_taxes = null; + /** Requisitions */ + private DocLine[] m_requisitions = null; + /** Order Currency Precision */ + private int m_precision = -1; + + /** + * Load Specific Document Details + * @return error message or null + */ + protected String loadDocumentDetails () + { + MOrder order = (MOrder)getPO(); + setDateDoc(order.getDateOrdered()); + setIsTaxIncluded(order.isTaxIncluded()); + // Amounts + setAmount(AMTTYPE_Gross, order.getGrandTotal()); + setAmount(AMTTYPE_Net, order.getTotalLines()); + setAmount(AMTTYPE_Charge, order.getChargeAmt()); + + // Contained Objects + m_taxes = loadTaxes(); + p_lines = loadLines(order); + // log.fine( "Lines=" + p_lines.length + ", Taxes=" + m_taxes.length); + return null; + } // loadDocumentDetails + + + /** + * Load Invoice Line + * @param order order + * @return DocLine Array + */ + private DocLine[] loadLines(MOrder order) + { + ArrayList list = new ArrayList(); + MOrderLine[] lines = order.getLines(); + for (int i = 0; i < lines.length; i++) + { + MOrderLine line = lines[i]; + DocLine docLine = new DocLine (line, this); + BigDecimal Qty = line.getQtyOrdered(); + docLine.setQty(Qty, order.isSOTrx()); + // + BigDecimal PriceActual = line.getPriceActual(); + BigDecimal PriceCost = null; + if (getDocumentType().equals(DOCTYPE_POrder)) // PO + PriceCost = line.getPriceCost(); + BigDecimal LineNetAmt = null; + if (PriceCost != null && PriceCost.signum() != 0) + LineNetAmt = Qty.multiply(PriceCost); + else + LineNetAmt = line.getLineNetAmt(); + docLine.setAmount (LineNetAmt); // DR + BigDecimal PriceList = line.getPriceList(); + int C_Tax_ID = docLine.getC_Tax_ID(); + // Correct included Tax + if (isTaxIncluded() && C_Tax_ID != 0) + { + MTax tax = MTax.get(getCtx(), C_Tax_ID); + if (!tax.isZeroTax()) + { + BigDecimal LineNetAmtTax = tax.calculateTax(LineNetAmt, true, getStdPrecision()); + log.fine("LineNetAmt=" + LineNetAmt + " - Tax=" + LineNetAmtTax); + LineNetAmt = LineNetAmt.subtract(LineNetAmtTax); + for (int t = 0; t < m_taxes.length; t++) + { + if (m_taxes[t].getC_Tax_ID() == C_Tax_ID) + { + m_taxes[t].addIncludedTax(LineNetAmtTax); + break; + } + } + BigDecimal PriceListTax = tax.calculateTax(PriceList, true, getStdPrecision()); + PriceList = PriceList.subtract(PriceListTax); + } + } // correct included Tax + + docLine.setAmount (LineNetAmt, PriceList, Qty); + list.add(docLine); + } + + // Return Array + DocLine[] dl = new DocLine[list.size()]; + list.toArray(dl); + return dl; + } // loadLines + + + /** + * Load Requisitions + * @return requisition lines of Order + */ + private DocLine[] loadRequisitions () + { + MOrder order = (MOrder)getPO(); + MOrderLine[] oLines = order.getLines(); + HashMap qtys = new HashMap(); + for (int i = 0; i < oLines.length; i++) + { + MOrderLine line = oLines[i]; + qtys.put(new Integer(line.getC_OrderLine_ID()), line.getQtyOrdered()); + } + // + ArrayList list = new ArrayList(); + String sql = "SELECT * FROM M_RequisitionLine rl " + + "WHERE EXISTS (SELECT * FROM C_Order o " + + " INNER JOIN C_OrderLine ol ON (o.C_Order_ID=ol.C_Order_ID) " + + "WHERE ol.C_OrderLine_ID=rl.C_OrderLine_ID" + + " AND o.C_Order_ID=?) " + + "ORDER BY rl.C_OrderLine_ID"; + PreparedStatement pstmt = null; + try + { + pstmt = DB.prepareStatement (sql, null); + pstmt.setInt (1, order.getC_Order_ID()); + ResultSet rs = pstmt.executeQuery (); + while (rs.next ()) + { + MRequisitionLine line = new MRequisitionLine (getCtx(), rs, null); + DocLine docLine = new DocLine (line, this); + // Quantity - not more then OrderLine + // Issue: Split of Requisition to multiple POs & different price + Integer key = new Integer(line.getC_OrderLine_ID()); + BigDecimal maxQty = qtys.get(key); + BigDecimal Qty = line.getQty().max(maxQty); + if (Qty.signum() == 0) + continue; + docLine.setQty (Qty, false); + qtys.put(key, maxQty.subtract(Qty)); + // + BigDecimal PriceActual = line.getPriceActual(); + BigDecimal LineNetAmt = line.getLineNetAmt(); + if (line.getQty().compareTo(Qty) != 0) + LineNetAmt = PriceActual.multiply(Qty); + docLine.setAmount (LineNetAmt); // DR + list.add (docLine); + } + rs.close (); + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + log.log (Level.SEVERE, sql, e); + } + try + { + if (pstmt != null) + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + pstmt = null; + } + + // Return Array + DocLine[] dls = new DocLine[list.size ()]; + list.toArray (dls); + return dls; + } // loadRequisitions + + + /** + * Get Currency Precision + * @return precision + */ + private int getStdPrecision() + { + if (m_precision == -1) + m_precision = MCurrency.getStdPrecision(getCtx(), getC_Currency_ID()); + return m_precision; + } // getPrecision + + /** + * Load Invoice Taxes + * @return DocTax Array + */ + private DocTax[] loadTaxes() + { + ArrayList list = new ArrayList(); + String sql = "SELECT it.C_Tax_ID, t.Name, t.Rate, it.TaxBaseAmt, it.TaxAmt, t.IsSalesTax " + + "FROM C_Tax t, C_OrderTax it " + + "WHERE t.C_Tax_ID=it.C_Tax_ID AND it.C_Order_ID=?"; + try + { + PreparedStatement pstmt = DB.prepareStatement(sql, getTrxName()); + pstmt.setInt(1, get_ID()); + ResultSet rs = pstmt.executeQuery(); + // + while (rs.next()) + { + int C_Tax_ID = rs.getInt(1); + String name = rs.getString(2); + BigDecimal rate = rs.getBigDecimal(3); + BigDecimal taxBaseAmt = rs.getBigDecimal(4); + BigDecimal amount = rs.getBigDecimal(5); + boolean salesTax = "Y".equals(rs.getString(6)); + // + DocTax taxLine = new DocTax(C_Tax_ID, name, rate, + taxBaseAmt, amount, salesTax); + list.add(taxLine); + } + // + rs.close(); + pstmt.close(); + } + catch (SQLException e) + { + log.log(Level.SEVERE, sql, e); + } + + // Return Array + DocTax[] tl = new DocTax[list.size()]; + list.toArray(tl); + return tl; + } // loadTaxes + + + /************************************************************************** + * Get Source Currency Balance - subtracts line and tax amounts from total - no rounding + * @return positive amount, if total invoice is bigger than lines + */ + public BigDecimal getBalance() + { + BigDecimal retValue = new BigDecimal(0.0); + StringBuffer sb = new StringBuffer (" ["); + // Total + retValue = retValue.add(getAmount(Doc.AMTTYPE_Gross)); + sb.append(getAmount(Doc.AMTTYPE_Gross)); + // - Header Charge + retValue = retValue.subtract(getAmount(Doc.AMTTYPE_Charge)); + sb.append("-").append(getAmount(Doc.AMTTYPE_Charge)); + // - Tax + if (m_taxes != null) + { + for (int i = 0; i < m_taxes.length; i++) + { + retValue = retValue.subtract(m_taxes[i].getAmount()); + sb.append("-").append(m_taxes[i].getAmount()); + } + } + // - Lines + if (p_lines != null) + { + for (int i = 0; i < p_lines.length; i++) + { + retValue = retValue.subtract(p_lines[i].getAmtSource()); + sb.append("-").append(p_lines[i].getAmtSource()); + } + sb.append("]"); + } + // + if (retValue.signum() != 0 // Sum of Cost(vs. Price) in lines may not add up + && getDocumentType().equals(DOCTYPE_POrder)) // PO + { + log.fine(toString() + " Balance=" + retValue + sb.toString() + " (ignored)"); + retValue = Env.ZERO; + } + else + log.fine(toString() + " Balance=" + retValue + sb.toString()); + return retValue; + } // getBalance + + + /************************************************************************* + * Create Facts (the accounting logic) for + * SOO, POO. + *
+	 *  Reservation (release)
+	 * 		Expense			DR
+	 * 		Offset					CR
+	 *  Commitment
+	 *  (to be released by Invoice Matching)
+	 * 		Expense					CR
+	 * 		Offset			DR
+	 *  
+ * @param as accounting schema + * @return Fact + */ + public ArrayList createFacts (MAcctSchema as) + { + ArrayList facts = new ArrayList(); + // Purchase Order + if (getDocumentType().equals(DOCTYPE_POrder)) + { + updateProductPO(as); + updateProductInfo(as.getC_AcctSchema_ID()); + + BigDecimal grossAmt = getAmount(Doc.AMTTYPE_Gross); + + // Commitment + if (as.isCreateCommitment()) + { + Fact fact = new Fact(this, as, Fact.POST_Commitment); + BigDecimal total = Env.ZERO; + for (int i = 0; i < p_lines.length; i++) + { + DocLine line = p_lines[i]; + BigDecimal cost = line.getAmtSource(); + total = total.add(cost); + + // Account + MAccount expense = line.getAccount(ProductCost.ACCTTYPE_P_Expense, as); + FactLine fl = fact.createLine (line, expense, + getC_Currency_ID(), cost, null); + } + // Offset + MAccount offset = getAccount(ACCTTYPE_CommitmentOffset, as); + if (offset == null) + { + p_Error = "@NotFound@ @CommitmentOffset_Acct@"; + log.log(Level.SEVERE, p_Error); + return null; + } + fact.createLine (null, offset, + getC_Currency_ID(), null, total); + // + facts.add(fact); + } + + // Reverse Reservation + if (as.isCreateReservation()) + { + Fact fact = new Fact(this, as, Fact.POST_Reservation); + BigDecimal total = Env.ZERO; + if (m_requisitions == null) + m_requisitions = loadRequisitions(); + for (int i = 0; i < m_requisitions.length; i++) + { + DocLine line = m_requisitions[i]; + BigDecimal cost = line.getAmtSource(); + total = total.add(cost); + + // Account + MAccount expense = line.getAccount(ProductCost.ACCTTYPE_P_Expense, as); + FactLine fl = fact.createLine (line, expense, + getC_Currency_ID(), null, cost); + } + // Offset + MAccount offset = getAccount(ACCTTYPE_CommitmentOffset, as); + if (offset == null) + { + p_Error = "@NotFound@ @CommitmentOffset_Acct@"; + log.log(Level.SEVERE, p_Error); + return null; + } + fact.createLine (null, offset, + getC_Currency_ID(), total, null); + // + facts.add(fact); + } // reservations + } + // SO + return facts; + } // createFact + + + /** + * Update ProductPO PriceLastPO + * @param as accounting schema + */ + private void updateProductPO(MAcctSchema as) + { + MClientInfo ci = MClientInfo.get(getCtx(), as.getAD_Client_ID()); + if (ci.getC_AcctSchema1_ID() != as.getC_AcctSchema_ID()) + return; + + StringBuffer sql = new StringBuffer ( + "UPDATE M_Product_PO po " + + "SET PriceLastPO = (SELECT currencyConvert(ol.PriceActual,ol.C_Currency_ID,po.C_Currency_ID,o.DateOrdered,o.C_ConversionType_ID,o.AD_Client_ID,o.AD_Org_ID) " + + "FROM C_Order o, C_OrderLine ol " + + "WHERE o.C_Order_ID=ol.C_Order_ID" + + " AND po.M_Product_ID=ol.M_Product_ID AND po.C_BPartner_ID=o.C_BPartner_ID" + + " AND ROWNUM=1 AND o.C_Order_ID=").append(get_ID()).append(") ") + .append("WHERE EXISTS (SELECT * " + + "FROM C_Order o, C_OrderLine ol " + + "WHERE o.C_Order_ID=ol.C_Order_ID" + + " AND po.M_Product_ID=ol.M_Product_ID AND po.C_BPartner_ID=o.C_BPartner_ID" + + " AND o.C_Order_ID=").append(get_ID()).append(")"); + int no = DB.executeUpdate(sql.toString(), getTrxName()); + log.fine("Updated=" + no); + } // updateProductPO + + + /** + * Get Commitments + * @param doc document + * @param maxQty Qty invoiced/matched + * @param C_InvoiceLine_ID invoice line + * @return commitments (order lines) + */ + protected static DocLine[] getCommitments(Doc doc, BigDecimal maxQty, int C_InvoiceLine_ID) + { + int precision = -1; + // + ArrayList list = new ArrayList(); + String sql = "SELECT * FROM C_OrderLine ol " + + "WHERE EXISTS " + + "(SELECT * FROM C_InvoiceLine il " + + "WHERE il.C_OrderLine_ID=ol.C_OrderLine_ID" + + " AND il.C_InvoiceLine_ID=?)" + + " OR EXISTS " + + "(SELECT * FROM M_MatchPO po " + + "WHERE po.C_OrderLine_ID=ol.C_OrderLine_ID" + + " AND po.C_InvoiceLine_ID=?)"; + PreparedStatement pstmt = null; + try + { + pstmt = DB.prepareStatement (sql, null); + pstmt.setInt (1, C_InvoiceLine_ID); + pstmt.setInt (2, C_InvoiceLine_ID); + ResultSet rs = pstmt.executeQuery (); + while (rs.next ()) + { + if (maxQty.signum() == 0) + continue; + MOrderLine line = new MOrderLine (doc.getCtx(), rs, null); + DocLine docLine = new DocLine (line, doc); + // Currency + if (precision == -1) + { + doc.setC_Currency_ID(docLine.getC_Currency_ID()); + precision = MCurrency.getStdPrecision(doc.getCtx(), docLine.getC_Currency_ID()); + } + // Qty + BigDecimal Qty = line.getQtyOrdered().max(maxQty); + docLine.setQty(Qty, false); + // + BigDecimal PriceActual = line.getPriceActual(); + BigDecimal PriceCost = line.getPriceCost(); + BigDecimal LineNetAmt = null; + if (PriceCost != null && PriceCost.signum() != 0) + LineNetAmt = Qty.multiply(PriceCost); + else if (Qty.equals(maxQty)) + LineNetAmt = line.getLineNetAmt(); + else + LineNetAmt = Qty.multiply(PriceActual); + maxQty = maxQty.subtract(Qty); + + docLine.setAmount (LineNetAmt); // DR + BigDecimal PriceList = line.getPriceList(); + int C_Tax_ID = docLine.getC_Tax_ID(); + // Correct included Tax + if (C_Tax_ID != 0 && line.getParent().isTaxIncluded()) + { + MTax tax = MTax.get(doc.getCtx(), C_Tax_ID); + if (!tax.isZeroTax()) + { + BigDecimal LineNetAmtTax = tax.calculateTax(LineNetAmt, true, precision); + s_log.fine("LineNetAmt=" + LineNetAmt + " - Tax=" + LineNetAmtTax); + LineNetAmt = LineNetAmt.subtract(LineNetAmtTax); + BigDecimal PriceListTax = tax.calculateTax(PriceList, true, precision); + PriceList = PriceList.subtract(PriceListTax); + } + } // correct included Tax + + docLine.setAmount (LineNetAmt, PriceList, Qty); + list.add(docLine); + } + rs.close (); + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + s_log.log (Level.SEVERE, sql, e); + } + try + { + if (pstmt != null) + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + pstmt = null; + } + + // Return Array + DocLine[] dl = new DocLine[list.size()]; + list.toArray(dl); + return dl; + } // getCommitments + + /** + * Get Commitment Release. + * Called from MatchInv for accrual and Allocation for Cash Based + * @param as accounting schema + * @param doc doc + * @param Qty qty invoiced/matched + * @param C_InvoiceLine_ID line + * @param multiplier 1 for accrual + * @return Fact + */ + protected static Fact getCommitmentRelease(MAcctSchema as, Doc doc, + BigDecimal Qty, int C_InvoiceLine_ID, BigDecimal multiplier) + { + Fact fact = new Fact(doc, as, Fact.POST_Commitment); + DocLine[] commitments = Doc_Order.getCommitments(doc, Qty, + C_InvoiceLine_ID); + + BigDecimal total = Env.ZERO; + int C_Currency_ID = -1; + for (int i = 0; i < commitments.length; i++) + { + DocLine line = commitments[i]; + if (C_Currency_ID == -1) + C_Currency_ID = line.getC_Currency_ID(); + else if (C_Currency_ID != line.getC_Currency_ID()) + { + doc.p_Error = "Different Currencies of Order Lines"; + s_log.log(Level.SEVERE, doc.p_Error); + return null; + } + BigDecimal cost = line.getAmtSource().multiply(multiplier); + total = total.add(cost); + + // Account + MAccount expense = line.getAccount(ProductCost.ACCTTYPE_P_Expense, as); + FactLine fl = fact.createLine (line, expense, + C_Currency_ID, null, cost); + } + // Offset + MAccount offset = doc.getAccount(ACCTTYPE_CommitmentOffset, as); + if (offset == null) + { + doc.p_Error = "@NotFound@ @CommitmentOffset_Acct@"; + s_log.log(Level.SEVERE, doc.p_Error); + return null; + } + fact.createLine (null, offset, + C_Currency_ID, total, null); + return fact; + } // getCommitmentRelease + + + /************************************************************************** + * Update Product Info (old) + * - Costing (PriceLastPO) + * - PO (PriceLastPO) + * @param C_AcctSchema_ID accounting schema + * @deprecated old costing + */ + private void updateProductInfo (int C_AcctSchema_ID) + { + log.fine("C_Order_ID=" + get_ID()); + + /** @todo Last.. would need to compare document/last updated date + * would need to maintain LastPriceUpdateDate on _PO and _Costing */ + + // update Product Costing + // requires existence of currency conversion !! + // if there are multiple lines of the same product last price uses first + StringBuffer sql = new StringBuffer ( + "UPDATE M_Product_Costing pc " + + "SET PriceLastPO = " + + "(SELECT currencyConvert(ol.PriceActual,ol.C_Currency_ID,a.C_Currency_ID,o.DateOrdered,o.C_ConversionType_ID,o.AD_Client_ID,o.AD_Org_ID) " + + "FROM C_Order o, C_OrderLine ol, C_AcctSchema a " + + "WHERE o.C_Order_ID=ol.C_Order_ID" + + " AND pc.M_Product_ID=ol.M_Product_ID AND pc.C_AcctSchema_ID=a.C_AcctSchema_ID" + + " AND ROWNUM=1" + + " AND pc.C_AcctSchema_ID=").append(C_AcctSchema_ID).append(" AND o.C_Order_ID=") + .append(get_ID()).append(") ") + .append("WHERE EXISTS (SELECT * " + + "FROM C_Order o, C_OrderLine ol, C_AcctSchema a " + + "WHERE o.C_Order_ID=ol.C_Order_ID" + + " AND pc.M_Product_ID=ol.M_Product_ID AND pc.C_AcctSchema_ID=a.C_AcctSchema_ID" + + " AND pc.C_AcctSchema_ID=").append(C_AcctSchema_ID).append(" AND o.C_Order_ID=") + .append(get_ID()).append(")"); + int no = DB.executeUpdate(sql.toString(), getTrxName()); + log.fine("M_Product_Costing - Updated=" + no); + } // updateProductInfo + +} // Doc_Order diff --git a/serverRoot/src/main/server/org/compiere/acct/Doc_Payment.java b/serverRoot/src/main/server/org/compiere/acct/Doc_Payment.java new file mode 100644 index 0000000000..766c357f74 --- /dev/null +++ b/serverRoot/src/main/server/org/compiere/acct/Doc_Payment.java @@ -0,0 +1,183 @@ +/****************************************************************************** + * 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.acct; + +import java.math.*; +import java.sql.*; +import java.util.*; +import java.util.logging.*; + +import org.compiere.model.*; +import org.compiere.util.*; + +/** + * Post Invoice Documents. + *
+ *  Table:              C_Payment (335)
+ *  Document Types      ARP, APP
+ *  
+ * @author Jorg Janke + * @version $Id: Doc_Payment.java,v 1.3 2006/07/30 00:53:33 jjanke Exp $ + */ +public class Doc_Payment extends Doc +{ + /** + * Constructor + * @param ass accounting schemata + * @param rs record + * @param trxName trx + */ + protected Doc_Payment (MAcctSchema[] ass, ResultSet rs, String trxName) + { + super (ass, MPayment.class, rs, null, trxName); + } // Doc_Payment + + /** Tender Type */ + private String m_TenderType = null; + /** Prepayment */ + private boolean m_Prepayment = false; + /** Bank Account */ + private int m_C_BankAccount_ID = 0; + + /** + * Load Specific Document Details + * @return error message or null + */ + protected String loadDocumentDetails () + { + MPayment pay = (MPayment)getPO(); + setDateDoc(pay.getDateTrx()); + m_TenderType = pay.getTenderType(); + m_Prepayment = pay.isPrepayment(); + m_C_BankAccount_ID = pay.getC_BankAccount_ID(); + // Amount + setAmount(Doc.AMTTYPE_Gross, pay.getPayAmt()); + return null; + } // loadDocumentDetails + + + /************************************************************************** + * Get Source Currency Balance - always zero + * @return Zero (always balanced) + */ + public BigDecimal getBalance() + { + BigDecimal retValue = Env.ZERO; + // log.config( toString() + " Balance=" + retValue); + return retValue; + } // getBalance + + /** + * Create Facts (the accounting logic) for + * ARP, APP. + *
+	 *  ARP
+	 *      BankInTransit   DR
+	 *      UnallocatedCash         CR
+	 *      or Charge/C_Prepayment
+	 *  APP
+	 *      PaymentSelect   DR
+	 *      or Charge/V_Prepayment
+	 *      BankInTransit           CR
+	 *  CashBankTransfer
+	 *      -
+	 *  
+ * @param as accounting schema + * @return Fact + */ + public ArrayList createFacts (MAcctSchema as) + { + // create Fact Header + Fact fact = new Fact(this, as, Fact.POST_Actual); + // Cash Transfer + if ("X".equals(m_TenderType)) + { + ArrayList facts = new ArrayList(); + facts.add(fact); + return facts; + } + + int AD_Org_ID = getBank_Org_ID(); // Bank Account Org + if (getDocumentType().equals(DOCTYPE_ARReceipt)) + { + // Asset + FactLine fl = fact.createLine(null, getAccount(Doc.ACCTTYPE_BankInTransit, as), + getC_Currency_ID(), getAmount(), null); + if (fl != null && AD_Org_ID != 0) + fl.setAD_Org_ID(AD_Org_ID); + // + MAccount acct = null; + if (getC_Charge_ID() != 0) + acct = MCharge.getAccount(getC_Charge_ID(), as, getAmount()); + else if (m_Prepayment) + acct = getAccount(Doc.ACCTTYPE_C_Prepayment, as); + else + acct = getAccount(Doc.ACCTTYPE_UnallocatedCash, as); + fl = fact.createLine(null, acct, + getC_Currency_ID(), null, getAmount()); + if (fl != null && AD_Org_ID != 0 + && getC_Charge_ID() == 0) // don't overwrite charge + fl.setAD_Org_ID(AD_Org_ID); + } + // APP + else if (getDocumentType().equals(DOCTYPE_APPayment)) + { + MAccount acct = null; + if (getC_Charge_ID() != 0) + acct = MCharge.getAccount(getC_Charge_ID(), as, getAmount()); + else if (m_Prepayment) + acct = getAccount(Doc.ACCTTYPE_V_Prepayment, as); + else + acct = getAccount(Doc.ACCTTYPE_PaymentSelect, as); + FactLine fl = fact.createLine(null, acct, + getC_Currency_ID(), getAmount(), null); + if (fl != null && AD_Org_ID != 0 + && getC_Charge_ID() == 0) // don't overwrite charge + fl.setAD_Org_ID(AD_Org_ID); + + // Asset + fl = fact.createLine(null, getAccount(Doc.ACCTTYPE_BankInTransit, as), + getC_Currency_ID(), null, getAmount()); + if (fl != null && AD_Org_ID != 0) + fl.setAD_Org_ID(AD_Org_ID); + } + else + { + p_Error = "DocumentType unknown: " + getDocumentType(); + log.log(Level.SEVERE, p_Error); + fact = null; + } + // + ArrayList facts = new ArrayList(); + facts.add(fact); + return facts; + } // createFact + + /** + * Get AD_Org_ID from Bank Account + * @return AD_Org_ID or 0 + */ + private int getBank_Org_ID () + { + if (m_C_BankAccount_ID == 0) + return 0; + // + MBankAccount ba = MBankAccount.get(getCtx(), m_C_BankAccount_ID); + return ba.getAD_Org_ID(); + } // getBank_Org_ID + +} // Doc_Payment diff --git a/serverRoot/src/main/server/org/compiere/acct/Doc_Production.java b/serverRoot/src/main/server/org/compiere/acct/Doc_Production.java new file mode 100644 index 0000000000..fb2615d4c0 --- /dev/null +++ b/serverRoot/src/main/server/org/compiere/acct/Doc_Production.java @@ -0,0 +1,216 @@ +/****************************************************************************** + * 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.acct; + +import java.math.*; +import java.sql.*; +import java.util.*; + +import org.compiere.model.*; +import java.util.logging.*; +import org.compiere.util.*; + +/** + * Post Invoice Documents. + *
+ *  Table:              M_Production (325)
+ *  Document Types:     MMP
+ *  
+ * @author Jorg Janke + * @version $Id: Doc_Production.java,v 1.3 2006/07/30 00:53:33 jjanke Exp $ + */ +public class Doc_Production extends Doc +{ + /** + * Constructor + * @param ass accounting schemata + * @param rs record + * @param trxName trx + */ + public Doc_Production (MAcctSchema[] ass, ResultSet rs, String trxName) + { + super (ass, X_M_Production.class, rs, DOCTYPE_MatProduction, trxName); + } // Doc_Production + + /** + * Load Document Details + * @return error message or null + */ + protected String loadDocumentDetails() + { + setC_Currency_ID (NO_CURRENCY); + X_M_Production prod = (X_M_Production)getPO(); + setDateDoc (prod.getMovementDate()); + setDateAcct(prod.getMovementDate()); + // Contained Objects + p_lines = loadLines(prod); + log.fine("Lines=" + p_lines.length); + return null; + } // loadDocumentDetails + + /** + * Load Invoice Line + * @param prod production + * @return DoaLine Array + */ + private DocLine[] loadLines(X_M_Production prod) + { + ArrayList list = new ArrayList(); + // Production + // -- ProductionPlan + // -- -- ProductionLine - the real level + String sqlPP = "SELECT * FROM M_ProductionPlan pp " + + "WHERE pp.M_Production_ID=? " + + "ORDER BY pp.Line"; + String sqlPL = "SELECT * FROM M_ProductionLine pl " + + "WHERE pl.M_ProductionPlan_ID=? " + + "ORDER BY pl.Line"; + + try + { + PreparedStatement pstmtPP = DB.prepareStatement(sqlPP, getTrxName()); + pstmtPP.setInt(1, get_ID()); + ResultSet rsPP = pstmtPP.executeQuery(); + // + while (rsPP.next()) + { + int M_Product_ID = rsPP.getInt("M_Product_ID"); + int M_ProductionPlan_ID = rsPP.getInt("M_ProductionPlan_ID"); + // + try + { + PreparedStatement pstmtPL = DB.prepareStatement(sqlPL, getTrxName()); + pstmtPL.setInt(1, M_ProductionPlan_ID); + ResultSet rsPL = pstmtPL.executeQuery(); + while (rsPL.next()) + { + X_M_ProductionLine line = new X_M_ProductionLine(getCtx(), rsPL, getTrxName()); + if (line.getMovementQty().signum() == 0) + { + log.info("LineQty=0 - " + line); + continue; + } + DocLine docLine = new DocLine (line, this); + docLine.setQty (line.getMovementQty(), false); + // Identify finished BOM Product + docLine.setProductionBOM(line.getM_Product_ID() == M_Product_ID); + // + log.fine(docLine.toString()); + list.add (docLine); + } + rsPL.close(); + pstmtPL.close(); + } + catch (Exception ee) + { + log.log(Level.SEVERE, sqlPL, ee); + } + } + rsPP.close(); + pstmtPP.close(); + } + catch (SQLException e) + { + log.log(Level.SEVERE, sqlPP, e); + } + // Return Array + DocLine[] dl = new DocLine[list.size()]; + list.toArray(dl); + return dl; + } // loadLines + + /** + * Get Balance + * @return Zero (always balanced) + */ + public BigDecimal getBalance() + { + BigDecimal retValue = Env.ZERO; + return retValue; + } // getBalance + + /** + * Create Facts (the accounting logic) for + * MMP. + *
+	 *  Production
+	 *      Inventory       DR      CR
+	 *  
+ * @param as account schema + * @return Fact + */ + public ArrayList createFacts (MAcctSchema as) + { + // create Fact Header + Fact fact = new Fact(this, as, Fact.POST_Actual); + setC_Currency_ID (as.getC_Currency_ID()); + + // Line pointer + FactLine fl = null; + for (int i = 0; i < p_lines.length; i++) + { + DocLine line = p_lines[i]; + // Calculate Costs + BigDecimal costs = null; + if (line.isProductionBOM()) + { + // Get BOM Cost - Sum of individual lines + BigDecimal bomCost = Env.ZERO; + for (int ii = 0; ii < p_lines.length; ii++) + { + DocLine line0 = p_lines[ii]; + if (line0.getM_ProductionPlan_ID() != line.getM_ProductionPlan_ID()) + continue; + if (!line0.isProductionBOM()) + bomCost = bomCost.add(line0.getProductCosts(as, line.getAD_Org_ID(), false)); + } + costs = bomCost.negate(); + } + else + costs = line.getProductCosts(as, line.getAD_Org_ID(), false); + + // Inventory DR CR + fl = fact.createLine(line, + line.getAccount(ProductCost.ACCTTYPE_P_Asset, as), + as.getC_Currency_ID(), costs); + if (fl == null) + { + p_Error = "No Costs for Line " + line.getLine() + " - " + line; + return null; + } + fl.setM_Locator_ID(line.getM_Locator_ID()); + fl.setQty(line.getQty()); + + // Cost Detail + String description = line.getDescription(); + if (description == null) + description = ""; + if (line.isProductionBOM()) + description += "(*)"; + MCostDetail.createProduction(as, line.getAD_Org_ID(), + line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), + line.get_ID(), 0, + costs, line.getQty(), + description, getTrxName()); + } + // + ArrayList facts = new ArrayList(); + facts.add(fact); + return facts; + } // createFact + +} // Doc_Production diff --git a/serverRoot/src/main/server/org/compiere/acct/Doc_ProjectIssue.java b/serverRoot/src/main/server/org/compiere/acct/Doc_ProjectIssue.java new file mode 100644 index 0000000000..f128dc07bc --- /dev/null +++ b/serverRoot/src/main/server/org/compiere/acct/Doc_ProjectIssue.java @@ -0,0 +1,221 @@ +/****************************************************************************** + * 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.acct; + +import java.math.*; +import java.sql.*; + +import org.compiere.model.*; +import java.util.*; +import java.util.logging.*; +import org.compiere.util.*; + +/** + * Project Issue. + * Note: + * Will load the default GL Category. + * Set up a document type to set the GL Category. + * + * @author Jorg Janke + * @version $Id: Doc_ProjectIssue.java,v 1.2 2006/07/30 00:53:33 jjanke Exp $ + */ +public class Doc_ProjectIssue extends Doc +{ + /** + * Constructor + * @param ass accounting schemata + * @param rs record + * @param trxName trx + */ + public Doc_ProjectIssue (MAcctSchema[] ass, ResultSet rs, String trxName) + { + super (ass, MProjectIssue.class, rs, DOCTYPE_ProjectIssue, trxName); + } // Doc_ProjectIssue + + /** Pseudo Line */ + private DocLine m_line = null; + /** Issue */ + private MProjectIssue m_issue = null; + + /** + * Load Document Details + * @return error message or null + */ + protected String loadDocumentDetails() + { + setC_Currency_ID(NO_CURRENCY); + m_issue = (MProjectIssue)getPO(); + setDateDoc (m_issue.getMovementDate()); + setDateAcct(m_issue.getMovementDate()); + + // Pseudo Line + m_line = new DocLine (m_issue, this); + m_line.setQty (m_issue.getMovementQty(), true); // sets Trx and Storage Qty + + // Pseudo Line Check + if (m_line.getM_Product_ID() == 0) + log.warning(m_line.toString() + " - No Product"); + log.fine(m_line.toString()); + return null; + } // loadDocumentDetails + + /** + * Get DocumentNo + * @return document no + */ + public String getDocumentNo () + { + MProject p = m_issue.getParent(); + if (p != null) + return p.getValue() + " #" + m_issue.getLine(); + return "(" + m_issue.get_ID() + ")"; + } // getDocumentNo + + /** + * Get Balance + * @return Zero (always balanced) + */ + public BigDecimal getBalance() + { + BigDecimal retValue = Env.ZERO; + return retValue; + } // getBalance + + /** + * Create Facts (the accounting logic) for + * PJI + *
+	 *  Issue
+	 *      ProjectWIP      DR
+	 *      Inventory               CR
+	 *  
+ * Project Account is either Asset or WIP depending on Project Type + * @param as accounting schema + * @return Fact + */ + public ArrayList createFacts (MAcctSchema as) + { + // create Fact Header + Fact fact = new Fact(this, as, Fact.POST_Actual); + setC_Currency_ID (as.getC_Currency_ID()); + + MProject project = new MProject (getCtx(), m_issue.getC_Project_ID(), null); + String ProjectCategory = project.getProjectCategory(); + MProduct product = MProduct.get(getCtx(), m_issue.getM_Product_ID()); + + // Line pointers + FactLine dr = null; + FactLine cr = null; + + // Issue Cost + BigDecimal cost = null; + if (m_issue.getM_InOutLine_ID() != 0) + cost = getPOCost(as); + else if (m_issue.getS_TimeExpenseLine_ID() != 0) + cost = getLaborCost(as); + if (cost == null) // standard Product Costs + cost = m_line.getProductCosts(as, getAD_Org_ID(), false); + + // Project DR + int acctType = ACCTTYPE_ProjectWIP; + if (MProject.PROJECTCATEGORY_AssetProject.equals(ProjectCategory)) + acctType = ACCTTYPE_ProjectAsset; + dr = fact.createLine(m_line, + getAccount(acctType, as), as.getC_Currency_ID(), cost, null); + dr.setQty(m_line.getQty().negate()); + + // Inventory CR + acctType = ProductCost.ACCTTYPE_P_Asset; + if (product.isService()) + acctType = ProductCost.ACCTTYPE_P_Expense; + cr = fact.createLine(m_line, + m_line.getAccount(acctType, as), + as.getC_Currency_ID(), null, cost); + cr.setM_Locator_ID(m_line.getM_Locator_ID()); + cr.setLocationFromLocator(m_line.getM_Locator_ID(), true); // from Loc + // + ArrayList facts = new ArrayList(); + facts.add(fact); + return facts; + } // createFact + + /** + * Get PO Costs in Currency of AcctSchema + * @param as Account Schema + * @return Unit PO Cost + */ + private BigDecimal getPOCost(MAcctSchema as) + { + BigDecimal retValue = null; + // Uses PO Date + String sql = "SELECT currencyConvert(ol.PriceActual, o.C_Currency_ID, ?, o.DateOrdered, o.C_ConversionType_ID, ?, ?) " + + "FROM C_OrderLine ol" + + " INNER JOIN M_InOutLine iol ON (iol.C_OrderLine_ID=ol.C_OrderLine_ID)" + + " INNER JOIN C_Order o ON (o.C_Order_ID=ol.C_Order_ID) " + + "WHERE iol.M_InOutLine_ID=?"; + PreparedStatement pstmt = null; + try + { + pstmt = DB.prepareStatement(sql, null); + pstmt.setInt(1, as.getC_Currency_ID()); + pstmt.setInt(2, getAD_Client_ID()); + pstmt.setInt(3, getAD_Org_ID()); + pstmt.setInt(4, m_issue.getM_InOutLine_ID()); + ResultSet rs = pstmt.executeQuery(); + if (rs.next()) + { + retValue = rs.getBigDecimal(1); + log.fine("POCost = " + retValue); + } + else + log.warning("Not found for M_InOutLine_ID=" + m_issue.getM_InOutLine_ID()); + rs.close(); + pstmt.close(); + pstmt = null; + } + catch (Exception e) + { + log.log(Level.SEVERE, sql, e); + } + try + { + if (pstmt != null) + pstmt.close(); + pstmt = null; + } + catch (Exception e) + { + pstmt = null; + } + return retValue; + } // getPOCost(); + + /** + * Get Labor Cost from Expense Report + * @param as Account Schema + * @return Unit Labor Cost + */ + private BigDecimal getLaborCost(MAcctSchema as) + { + BigDecimal retValue = null; + + /** TODO Labor Cost */ + return retValue; + } // getLaborCost + +} // DocProjectIssue + diff --git a/serverRoot/src/main/server/org/compiere/acct/Doc_Requisition.java b/serverRoot/src/main/server/org/compiere/acct/Doc_Requisition.java new file mode 100644 index 0000000000..75e6065ca2 --- /dev/null +++ b/serverRoot/src/main/server/org/compiere/acct/Doc_Requisition.java @@ -0,0 +1,153 @@ +/****************************************************************************** + * 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.acct; + +import java.math.*; +import java.util.*; +import java.util.logging.*; +import java.sql.*; + +import org.compiere.model.*; +import org.compiere.util.*; + +/** + * Post Order Documents. + * + *
+ *   Table:              M_Requisition
+ *   Document Types:     POR (Requisition)
+ * 
+ * + * @author Jorg Janke + * @version $Id: Doc_Requisition.java,v 1.3 2006/07/30 00:53:33 jjanke Exp $ + */ +public class Doc_Requisition extends Doc +{ + /** + * Constructor + * @param ass accounting schemata + * @param rs record + * @param trxName trx + */ + protected Doc_Requisition (MAcctSchema[] ass, ResultSet rs, String trxName) + { + super (ass, MRequisition.class, rs, DOCTYPE_PurchaseRequisition, trxName); + } // Doc_Requisition + + /** + * Load Specific Document Details + * @return error message or null + */ + protected String loadDocumentDetails () + { + setC_Currency_ID(NO_CURRENCY); + MRequisition req = (MRequisition)getPO(); + setDateDoc (req.getDateDoc()); + setDateAcct (req.getDateDoc()); + // Amounts + setAmount(AMTTYPE_Gross, req.getTotalLines()); + setAmount(AMTTYPE_Net, req.getTotalLines()); + // Contained Objects + p_lines = loadLines (req); + // log.fine( "Lines=" + p_lines.length + ", Taxes=" + m_taxes.length); + return null; + } // loadDocumentDetails + + /** + * Load Requisition Lines + * @param req requisition + * @return DocLine Array + */ + private DocLine[] loadLines (MRequisition req) + { + ArrayList list = new ArrayList (); + MRequisitionLine[] lines = req.getLines(); + for (int i = 0; i < lines.length; i++) + { + MRequisitionLine line = lines[i]; + DocLine docLine = new DocLine (line, this); + BigDecimal Qty = line.getQty(); + docLine.setQty (Qty, false); + BigDecimal PriceActual = line.getPriceActual(); + BigDecimal LineNetAmt = line.getLineNetAmt(); + docLine.setAmount (LineNetAmt); // DR + list.add (docLine); + } + // Return Array + DocLine[] dls = new DocLine[list.size ()]; + list.toArray (dls); + return dls; + } // loadLines + + /*************************************************************************** + * Get Source Currency Balance - subtracts line and tax amounts from total - + * no rounding + * + * @return positive amount, if total invoice is bigger than lines + */ + public BigDecimal getBalance () + { + BigDecimal retValue = new BigDecimal (0.0); + return retValue; + } // getBalance + + /*************************************************************************** + * Create Facts (the accounting logic) for POR. + *
+	 * Reservation
+	 * 	Expense		CR
+	 * 	Offset			DR
+	 * 
+ * @param as accounting schema + * @return Fact + */ + public ArrayList createFacts (MAcctSchema as) + { + ArrayList facts = new ArrayList(); + Fact fact = new Fact (this, as, Fact.POST_Reservation); + setC_Currency_ID(as.getC_Currency_ID()); + // + BigDecimal grossAmt = getAmount (Doc.AMTTYPE_Gross); + // Commitment + if (as.isCreateReservation ()) + { + BigDecimal total = Env.ZERO; + for (int i = 0; i < p_lines.length; i++) + { + DocLine line = p_lines[i]; + BigDecimal cost = line.getAmtSource(); + total = total.add (cost); + // Account + MAccount expense = line.getAccount(ProductCost.ACCTTYPE_P_Expense, as); + // + fact.createLine (line, expense, as.getC_Currency_ID(), cost, null); + } + // Offset + MAccount offset = getAccount (ACCTTYPE_CommitmentOffset, as); + if (offset == null) + { + p_Error = "@NotFound@ @CommitmentOffset_Acct@"; + log.log (Level.SEVERE, p_Error); + return null; + } + fact.createLine (null, offset, getC_Currency_ID(), null, total); + facts.add(fact); + } + + return facts; + } // createFact +} // Doc_Requisition diff --git a/serverRoot/src/main/server/org/compiere/acct/Fact.java b/serverRoot/src/main/server/org/compiere/acct/Fact.java new file mode 100644 index 0000000000..fbf8f092da --- /dev/null +++ b/serverRoot/src/main/server/org/compiere/acct/Fact.java @@ -0,0 +1,860 @@ +/****************************************************************************** + * 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.acct; + +import java.math.*; +import java.util.*; +import org.compiere.model.*; +import org.compiere.util.*; + +/** + * Accounting Fact + * + * @author Jorg Janke + * @version $Id: Fact.java,v 1.2 2006/07/30 00:53:33 jjanke Exp $ + */ +public final class Fact +{ + /** + * Constructor + * @param document pointer to document + * @param acctSchema Account Schema to create accounts + * @param defaultPostingType the default Posting type (actual,..) for this posting + */ + public Fact (Doc document, MAcctSchema acctSchema, String defaultPostingType) + { + m_doc = document; + m_acctSchema = acctSchema; + m_postingType = defaultPostingType; + // + log.config(toString()); + } // Fact + + + /** Log */ + private CLogger log = CLogger.getCLogger(getClass()); + + /** Document */ + private Doc m_doc = null; + /** Accounting Schema */ + private MAcctSchema m_acctSchema = null; + /** Transaction */ + private String m_trxName; + + /** Posting Type */ + private String m_postingType = null; + + /** Actual Balance Type */ + public static final String POST_Actual = MFactAcct.POSTINGTYPE_Actual; + /** Budget Balance Type */ + public static final String POST_Budget = MFactAcct.POSTINGTYPE_Budget;; + /** Encumbrance Posting */ + public static final String POST_Commitment = MFactAcct.POSTINGTYPE_Commitment; + /** Encumbrance Posting */ + public static final String POST_Reservation = MFactAcct.POSTINGTYPE_Reservation; + + + /** Is Converted */ + private boolean m_converted = false; + + /** Lines */ + private ArrayList m_lines = new ArrayList(); + + + /** + * Dispose + */ + public void dispose() + { + m_lines.clear(); + m_lines = null; + } // dispose + + /** + * Create and convert Fact Line. + * Used to create a DR and/or CR entry + * + * @param docLine the document line or null + * @param account if null, line is not created + * @param C_Currency_ID the currency + * @param debitAmt debit amount, can be null + * @param creditAmt credit amount, can be null + * @return Fact Line + */ + public FactLine createLine (DocLine docLine, MAccount account, + int C_Currency_ID, BigDecimal debitAmt, BigDecimal creditAmt) + { + // log.fine("createLine - " + account + " - Dr=" + debitAmt + ", Cr=" + creditAmt); + + // Data Check + if (account == null) + { + log.info("No account for " + docLine + + ": Amt=" + debitAmt + "/" + creditAmt + + " - " + toString()); + return null; + } + // + FactLine line = new FactLine (m_doc.getCtx(), m_doc.get_Table_ID(), + m_doc.get_ID(), + docLine == null ? 0 : docLine.get_ID(), m_trxName); + // Set Info & Account + line.setDocumentInfo(m_doc, docLine); + line.setPostingType(m_postingType); + line.setAccount(m_acctSchema, account); + + // Amounts - one needs to not zero + if (!line.setAmtSource(C_Currency_ID, debitAmt, creditAmt)) + { + if (docLine == null || docLine.getQty() == null || docLine.getQty().signum() == 0) + { + log.fine("Both amounts & qty = 0/Null - " + docLine + + " - " + toString()); + return null; + } + log.fine("Both amounts = 0/Null, Qty=" + docLine.getQty() + " - " + docLine + + " - " + toString()); + } + // Convert + line.convert(); + // Optionally overwrite Acct Amount + if (docLine != null + && (docLine.getAmtAcctDr() != null || docLine.getAmtAcctCr() != null)) + line.setAmtAcct(docLine.getAmtAcctDr(), docLine.getAmtAcctCr()); + // + log.fine(line.toString()); + add(line); + return line; + } // createLine + + /** + * Add Fact Line + * @param line fact line + */ + void add (FactLine line) + { + m_lines.add(line); + } // add + + /** + * Create and convert Fact Line. + * Used to create either a DR or CR entry + * + * @param docLine Document Line or null + * @param accountDr Account to be used if Amt is DR balance + * @param accountCr Account to be used if Amt is CR balance + * @param C_Currency_ID Currency + * @param Amt if negative Cr else Dr + * @return FactLine + */ + public FactLine createLine (DocLine docLine, MAccount accountDr, MAccount accountCr, + int C_Currency_ID, BigDecimal Amt) + { + if (Amt.signum() < 0) + return createLine (docLine, accountCr, C_Currency_ID, null, Amt.abs()); + else + return createLine (docLine, accountDr, C_Currency_ID, Amt, null); + } // createLine + + /** + * Create and convert Fact Line. + * Used to create either a DR or CR entry + * + * @param docLine Document line or null + * @param account Account to be used + * @param C_Currency_ID Currency + * @param Amt if negative Cr else Dr + * @return FactLine + */ + public FactLine createLine (DocLine docLine, MAccount account, + int C_Currency_ID, BigDecimal Amt) + { + if (Amt.signum() < 0) + return createLine (docLine, account, C_Currency_ID, null, Amt.abs()); + else + return createLine (docLine, account, C_Currency_ID, Amt, null); + } // createLine + + /** + * Is Posting Type + * @param PostingType - see POST_* + * @return true if document is posting type + */ + public boolean isPostingType (String PostingType) + { + return m_postingType.equals(PostingType); + } // isPostingType + + /** + * Is converted + * @return true if converted + */ + public boolean isConverted() + { + return m_converted; + } // isConverted + + /** + * Get AcctSchema + * @return AcctSchema + */ + public MAcctSchema getAcctSchema() + { + return m_acctSchema; + } // getAcctSchema + + + /************************************************************************** + * Are the lines Source Balanced + * @return true if source lines balanced + */ + public boolean isSourceBalanced() + { + // No lines -> balanded + if (m_lines.size() == 0) + return true; + BigDecimal balance = getSourceBalance(); + boolean retValue = balance.signum() == 0; + if (retValue) + log.finer(toString()); + else + log.warning ("NO - Diff=" + balance + " - " + toString()); + return retValue; + } // isSourceBalanced + + /** + * Return Source Balance + * @return source balance + */ + protected BigDecimal getSourceBalance() + { + BigDecimal result = Env.ZERO; + for (int i = 0; i < m_lines.size(); i++) + { + FactLine line = (FactLine)m_lines.get(i); + result = result.add (line.getSourceBalance()); + } + // log.fine("getSourceBalance - " + result.toString()); + return result; + } // getSourceBalance + + /** + * Create Source Line for Suspense Balancing. + * Only if Suspense Balancing is enabled and not a multi-currency document + * (double check as otherwise the rule should not have fired) + * If not balanced create balancing entry in currency of the document + * @return FactLine + */ + public FactLine balanceSource() + { + if (!m_acctSchema.isSuspenseBalancing() || m_doc.isMultiCurrency()) + return null; + BigDecimal diff = getSourceBalance(); + log.finer("Diff=" + diff); + + // new line + FactLine line = new FactLine (m_doc.getCtx(), m_doc.get_Table_ID(), + m_doc.get_ID(), 0, m_trxName); + line.setDocumentInfo(m_doc, null); + line.setPostingType(m_postingType); + + // Amount + if (diff.signum() < 0) // negative balance => DR + line.setAmtSource(m_doc.getC_Currency_ID(), diff.abs(), Env.ZERO); + else // positive balance => CR + line.setAmtSource(m_doc.getC_Currency_ID(), Env.ZERO, diff); + + // Account + line.setAccount(m_acctSchema, m_acctSchema.getSuspenseBalancing_Acct()); + + // Convert + line.convert(); + // + log.fine(line.toString()); + m_lines.add(line); + return line; + } // balancingSource + + + /************************************************************************** + * Are all segments balanced + * @return true if segments are balanced + */ + public boolean isSegmentBalanced() + { + if (m_lines.size() == 0) + return true; + + MAcctSchemaElement[] elements = m_acctSchema.getAcctSchemaElements(); + // check all balancing segments + for (int i = 0; i < elements.length; i++) + { + MAcctSchemaElement ase = elements[i]; + if (ase.isBalanced() && !isSegmentBalanced (ase.getElementType())) + return false; + } + return true; + } // isSegmentBalanced + + /** + * Is Source Segment balanced. + * @param segmentType - see AcctSchemaElement.SEGMENT_* + * Implemented only for Org + * Other sensible candidates are Project, User1/2 + * @return true if segments are balanced + */ + public boolean isSegmentBalanced (String segmentType) + { + if (segmentType.equals(MAcctSchemaElement.ELEMENTTYPE_Organization)) + { + HashMap map = new HashMap(); + // Add up values by key + for (int i = 0; i < m_lines.size(); i++) + { + FactLine line = (FactLine)m_lines.get(i); + Integer key = new Integer(line.getAD_Org_ID()); + BigDecimal bal = line.getSourceBalance(); + BigDecimal oldBal = (BigDecimal)map.get(key); + if (oldBal != null) + bal = bal.add(oldBal); + map.put(key, bal); + // System.out.println("Add Key=" + key + ", Bal=" + bal + " <- " + line); + } + // check if all keys are zero + Iterator values = map.values().iterator(); + while (values.hasNext()) + { + BigDecimal bal = (BigDecimal)values.next(); + if (bal.signum() != 0) + { + map.clear(); + log.warning ("(" + segmentType + ") NO - " + toString() + ", Balance=" + bal); + return false; + } + } + map.clear(); + log.finer("(" + segmentType + ") - " + toString()); + return true; + } + log.finer("(" + segmentType + ") (not checked) - " + toString()); + return true; + } // isSegmentBalanced + + /** + * Balance all segments. + * - For all balancing segments + * - For all segment values + * - If balance <> 0 create dueTo/dueFrom line + * overwriting the segment value + */ + public void balanceSegments() + { + MAcctSchemaElement[] elements = m_acctSchema.getAcctSchemaElements(); + // check all balancing segments + for (int i = 0; i < elements.length; i++) + { + MAcctSchemaElement ase = elements[i]; + if (ase.isBalanced()) + balanceSegment (ase.getElementType()); + } + } // balanceSegments + + /** + * Balance Source Segment + * @param elementType segment element type + */ + private void balanceSegment (String elementType) + { + // no lines -> balanced + if (m_lines.size() == 0) + return; + + log.fine ("(" + elementType + ") - " + toString()); + + // Org + if (elementType.equals(MAcctSchemaElement.ELEMENTTYPE_Organization)) + { + HashMap map = new HashMap(); + // Add up values by key + for (int i = 0; i < m_lines.size(); i++) + { + FactLine line = (FactLine)m_lines.get(i); + Integer key = new Integer(line.getAD_Org_ID()); + // BigDecimal balance = line.getSourceBalance(); + Balance oldBalance = (Balance)map.get(key); + if (oldBalance == null) + { + oldBalance = new Balance (line.getAmtSourceDr(), line.getAmtSourceCr()); + map.put(key, oldBalance); + } + else + oldBalance.add(line.getAmtSourceDr(), line.getAmtSourceCr()); + // log.info ("Key=" + key + ", Balance=" + balance + " - " + line); + } + + // Create entry for non-zero element + Iterator keys = map.keySet().iterator(); + while (keys.hasNext()) + { + Integer key = (Integer)keys.next(); + Balance difference = (Balance)map.get(key); + log.info (elementType + "=" + key + ", " + difference); + // + if (!difference.isZeroBalance()) + { + // Create Balancing Entry + FactLine line = new FactLine (m_doc.getCtx(), m_doc.get_Table_ID(), + m_doc.get_ID(), 0, m_trxName); + line.setDocumentInfo(m_doc, null); + line.setPostingType(m_postingType); + // Amount & Account + if (difference.getBalance().signum() < 0) + { + if (difference.isReversal()) + { + line.setAmtSource(m_doc.getC_Currency_ID(), Env.ZERO, difference.getPostBalance()); + line.setAccount(m_acctSchema, m_acctSchema.getDueTo_Acct(elementType)); + } + else + { + line.setAmtSource(m_doc.getC_Currency_ID(), difference.getPostBalance(), Env.ZERO); + line.setAccount(m_acctSchema, m_acctSchema.getDueFrom_Acct(elementType)); + } + } + else + { + if (difference.isReversal()) + { + line.setAmtSource(m_doc.getC_Currency_ID(), difference.getPostBalance(), Env.ZERO); + line.setAccount(m_acctSchema, m_acctSchema.getDueFrom_Acct(elementType)); + } + else + { + line.setAmtSource(m_doc.getC_Currency_ID(), Env.ZERO, difference.getPostBalance()); + line.setAccount(m_acctSchema, m_acctSchema.getDueTo_Acct(elementType)); + } + } + line.convert(); + line.setAD_Org_ID(key.intValue()); + // + m_lines.add(line); + log.fine("(" + elementType + ") - " + line); + } + } + map.clear(); + } + } // balanceSegment + + + /************************************************************************** + * Are the lines Accounting Balanced + * @return true if accounting lines are balanced + */ + public boolean isAcctBalanced() + { + // no lines -> balanced + if (m_lines.size() == 0) + return true; + BigDecimal balance = getAcctBalance(); + boolean retValue = balance.signum() == 0; + if (retValue) + log.finer(toString()); + else + log.warning("NO - Diff=" + balance + " - " + toString()); + return retValue; + } // isAcctBalanced + + /** + * Return Accounting Balance + * @return true if accounting lines are balanced + */ + protected BigDecimal getAcctBalance() + { + BigDecimal result = Env.ZERO; + for (int i = 0; i < m_lines.size(); i++) + { + FactLine line = (FactLine)m_lines.get(i); + result = result.add(line.getAcctBalance()); + } + // log.fine(result.toString()); + return result; + } // getAcctBalance + + /** + * Balance Accounting Currency. + * If the accounting currency is not balanced, + * if Currency balancing is enabled + * create a new line using the currency balancing account with zero source balance + * or + * adjust the line with the largest balance sheet account + * or if no balance sheet account exist, the line with the largest amount + * @return FactLine + */ + public FactLine balanceAccounting() + { + BigDecimal diff = getAcctBalance(); // DR-CR + log.fine("Balance=" + diff + + ", CurrBal=" + m_acctSchema.isCurrencyBalancing() + + " - " + toString()); + FactLine line = null; + + BigDecimal BSamount = Env.ZERO; + FactLine BSline = null; + BigDecimal PLamount = Env.ZERO; + FactLine PLline = null; + + // Find line biggest BalanceSheet or P&L line + for (int i = 0; i < m_lines.size(); i++) + { + FactLine l = (FactLine)m_lines.get(i); + BigDecimal amt = l.getAcctBalance().abs(); + if (l.isBalanceSheet() && amt.compareTo(BSamount) > 0) + { + BSamount = amt; + BSline = l; + } + else if (!l.isBalanceSheet() && amt.compareTo(PLamount) > 0) + { + PLamount = amt; + PLline = l; + } + } + + // Create Currency Balancing Entry + if (m_acctSchema.isCurrencyBalancing()) + { + line = new FactLine (m_doc.getCtx(), m_doc.get_Table_ID(), + m_doc.get_ID(), 0, m_trxName); + line.setDocumentInfo (m_doc, null); + line.setPostingType (m_postingType); + line.setAccount (m_acctSchema, m_acctSchema.getCurrencyBalancing_Acct()); + + // Amount + line.setAmtSource(m_doc.getC_Currency_ID(), Env.ZERO, Env.ZERO); + line.convert(); + // Accounted + BigDecimal drAmt = Env.ZERO; + BigDecimal crAmt = Env.ZERO; + boolean isDR = diff.signum() < 0; + BigDecimal difference = diff.abs(); + if (isDR) + drAmt = difference; + else + crAmt = difference; + // Switch sides + boolean switchIt = BSline != null + && ((BSline.isDrSourceBalance() && isDR) + || (!BSline.isDrSourceBalance() && !isDR)); + if (switchIt) + { + drAmt = Env.ZERO; + crAmt = Env.ZERO; + if (isDR) + crAmt = difference.negate(); + else + drAmt = difference.negate(); + } + line.setAmtAcct(drAmt, crAmt); + log.fine(line.toString()); + m_lines.add(line); + } + else // Adjust biggest (Balance Sheet) line amount + { + if (BSline != null) + line = BSline; + else + line = PLline; + if (line == null) + log.severe ("No Line found"); + else + { + log.fine("Adjusting Amt=" + diff + "; Line=" + line); + line.currencyCorrect(diff); + log.fine(line.toString()); + } + } // correct biggest amount + + return line; + } // balanceAccounting + + /** + * Check Accounts of Fact Lines + * @return true if success + */ + public boolean checkAccounts() + { + // no lines -> nothing to distribute + if (m_lines.size() == 0) + return true; + + // For all fact lines + for (int i = 0; i < m_lines.size(); i++) + { + FactLine line = (FactLine)m_lines.get(i); + MAccount account = line.getAccount(); + if (account == null) + { + log.warning("No Account for " + line); + return false; + } + MElementValue ev = account.getAccount(); + if (ev == null) + { + log.warning("No Element Value for " + account + + ": " + line); + return false; + } + if (ev.isSummary()) + { + log.warning("Cannot post to Summary Account " + ev + + ": " + line); + return false; + } + if (!ev.isActive()) + { + log.warning("Cannot post to Inactive Account " + ev + + ": " + line); + return false; + } + + } // for all lines + + return true; + } // checkAccounts + + /** + * GL Distribution of Fact Lines + * @return true if success + */ + public boolean distribute() + { + // no lines -> nothing to distribute + if (m_lines.size() == 0) + return true; + + ArrayList newLines = new ArrayList(); + // For all fact lines + for (int i = 0; i < m_lines.size(); i++) + { + FactLine dLine = (FactLine)m_lines.get(i); + MDistribution[] distributions = MDistribution.get (dLine.getAccount(), + m_postingType, m_doc.getC_DocType_ID()); + // No Distribution for this line + if (distributions == null || distributions.length == 0) + continue; + // Just the first + if (distributions.length > 1) + log.warning("More then one Distributiion for " + dLine.getAccount()); + MDistribution distribution = distributions[0]; + // Add Reversal + FactLine reversal = dLine.reverse(distribution.getName()); + log.info("Reversal=" + reversal); + newLines.add(reversal); // saved in postCommit + // Prepare + distribution.distribute(dLine.getAccount(), dLine.getSourceBalance(), dLine.getC_Currency_ID()); + MDistributionLine[] lines = distribution.getLines(false); + for (int j = 0; j < lines.length; j++) + { + MDistributionLine dl = lines[j]; + if (!dl.isActive() || dl.getAmt().signum() == 0) + continue; + FactLine factLine = new FactLine (m_doc.getCtx(), m_doc.get_Table_ID(), + m_doc.get_ID(), 0, m_trxName); + // Set Info & Account + factLine.setDocumentInfo(m_doc, dLine.getDocLine()); + factLine.setAccount(m_acctSchema, dl.getAccount()); + factLine.setPostingType(m_postingType); + if (dl.isOverwriteOrg()) // set Org explicitly + factLine.setAD_Org_ID(dl.getOrg_ID()); + // + if (dl.getAmt().signum() < 0) + factLine.setAmtSource(dLine.getC_Currency_ID(), null, dl.getAmt().abs()); + else + factLine.setAmtSource(dLine.getC_Currency_ID(), dl.getAmt(), null); + // Convert + factLine.convert(); + // + String description = distribution.getName() + " #" + dl.getLine(); + if (dl.getDescription() != null) + description += " - " + dl.getDescription(); + factLine.addDescription(description); + // + log.info(factLine.toString()); + newLines.add(factLine); + } + } // for all lines + + // Add Lines + for (int i = 0; i < newLines.size(); i++) + m_lines.add(newLines.get(i)); + + return true; + } // distribute + + + /************************************************************************** + * String representation + * @return String + */ + public String toString() + { + StringBuffer sb = new StringBuffer("Fact["); + sb.append(m_doc.toString()); + sb.append(",").append(m_acctSchema.toString()); + sb.append(",PostType=").append(m_postingType); + sb.append("]"); + return sb.toString(); + } // toString + + /** + * Get Lines + * @return FactLine Array + */ + public FactLine[] getLines() + { + FactLine[] temp = new FactLine[m_lines.size()]; + m_lines.toArray(temp); + return temp; + } // getLines + + /** + * Save Fact + * @param trxName transaction + * @return true if all lines were saved + */ + public boolean save (String trxName) + { + // save Lines + for (int i = 0; i < m_lines.size(); i++) + { + FactLine fl = (FactLine)m_lines.get(i); + // log.fine("save - " + fl); + if (!fl.save(trxName)) // abort on first error + return false; + } + return true; + } // commit + + /** + * Get Transaction + * @return trx + */ + public String get_TrxName() + { + return m_trxName; + } // getTrxName + + /** + * Set Transaction name + * @param trxName + */ + private void set_TrxName(String trxName) + { + m_trxName = trxName; + } // set_TrxName + + /** + * Fact Balance Utility + * + * @author Jorg Janke + * @version $Id: Fact.java,v 1.2 2006/07/30 00:53:33 jjanke Exp $ + */ + public class Balance + { + /** + * New Balance + * @param dr DR + * @param cr CR + */ + public Balance (BigDecimal dr, BigDecimal cr) + { + DR = dr; + CR = cr; + } + + /** DR Amount */ + public BigDecimal DR = Env.ZERO; + /** CR Amount */ + public BigDecimal CR = Env.ZERO; + + /** + * Add + * @param dr DR + * @param cr CR + */ + public void add (BigDecimal dr, BigDecimal cr) + { + DR = DR.add(dr); + CR = CR.add(cr); + } + + /** + * Get Balance + * @return balance + */ + public BigDecimal getBalance() + { + return DR.subtract(CR); + } // getBalance + + /** + * Get Post Balance + * @return absolute balance - negative if reversal + */ + public BigDecimal getPostBalance() + { + BigDecimal bd = getBalance().abs(); + if (isReversal()) + return bd.negate(); + return bd; + } // getPostBalance + + /** + * Zero Balance + * @return true if 0 + */ + public boolean isZeroBalance() + { + return getBalance().signum() == 0; + } // isZeroBalance + + /** + * Reversal + * @return true if both DR/CR are negative or zero + */ + public boolean isReversal() + { + return DR.signum() <= 0 && CR.signum() <= 0; + } // isReversal + + /** + * String Representation + * @return info + */ + public String toString () + { + StringBuffer sb = new StringBuffer ("Balance["); + sb.append ("DR=").append(DR) + .append ("-CR=").append(CR) + .append(" = ").append(getBalance()) + .append ("]"); + return sb.toString (); + } // toString + + } // Balance + +} // Fact diff --git a/serverRoot/src/main/server/org/compiere/acct/FactLine.java b/serverRoot/src/main/server/org/compiere/acct/FactLine.java new file mode 100644 index 0000000000..343364b561 --- /dev/null +++ b/serverRoot/src/main/server/org/compiere/acct/FactLine.java @@ -0,0 +1,949 @@ +/****************************************************************************** + * 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.acct; + +import java.math.*; +import java.sql.*; +import java.util.*; +import java.util.logging.*; +import org.compiere.model.*; +import org.compiere.util.*; + +/** + * Accounting Fact Entry. + * + * @author Jorg Janke + * @version $Id: FactLine.java,v 1.3 2006/07/30 00:53:33 jjanke Exp $ + */ +public final class FactLine extends X_Fact_Acct +{ + /** + * Constructor + * @param ctx context + * @param AD_Table_ID - Table of Document Source + * @param Record_ID - Record of document + * @param Line_ID - Optional line id + * @param trxName transaction + */ + public FactLine (Properties ctx, int AD_Table_ID, int Record_ID, int Line_ID, String trxName) + { + super(ctx, 0, trxName); + setAD_Client_ID(0); // do not derive + setAD_Org_ID(0); // do not derive + // + setAmtAcctCr (Env.ZERO); + setAmtAcctDr (Env.ZERO); + setAmtSourceCr (Env.ZERO); + setAmtSourceDr (Env.ZERO); + // Log.trace(this,Log.l1_User, "FactLine " + AD_Table_ID + ":" + Record_ID); + setAD_Table_ID (AD_Table_ID); + setRecord_ID (Record_ID); + setLine_ID (Line_ID); + } // FactLine + + /** Account */ + private MAccount m_acct = null; + /** Accounting Schema */ + private MAcctSchema m_acctSchema = null; + /** Document Header */ + private Doc m_doc = null; + /** Document Line */ + private DocLine m_docLine = null; + + /** + * Create Reversal (negate DR/CR) of the line + * @param description new description + * @return reversal line + */ + public FactLine reverse (String description) + { + FactLine reversal = new FactLine (getCtx(), getAD_Table_ID(), getRecord_ID(), getLine_ID(), get_TrxName()); + reversal.setClientOrg(this); // needs to be set explicitly + reversal.setDocumentInfo(m_doc, m_docLine); + reversal.setAccount(m_acctSchema, m_acct); + reversal.setPostingType(getPostingType()); + // + reversal.setAmtSource(getC_Currency_ID(), getAmtSourceDr().negate(), getAmtSourceCr().negate()); + reversal.convert(); + reversal.setDescription(description); + return reversal; + } // reverse + + /** + * Create Accrual (flip CR/DR) of the line + * @param description new description + * @return accrual line + */ + public FactLine accrue (String description) + { + FactLine accrual = new FactLine (getCtx(), getAD_Table_ID(), getRecord_ID(), getLine_ID(), get_TrxName()); + accrual.setClientOrg(this); // needs to be set explicitly + accrual.setDocumentInfo(m_doc, m_docLine); + accrual.setAccount(m_acctSchema, m_acct); + accrual.setPostingType(getPostingType()); + // + accrual.setAmtSource(getC_Currency_ID(), getAmtSourceCr(), getAmtSourceDr()); + accrual.convert(); + accrual.setDescription(description); + return accrual; + } // reverse + + /** + * Set Account Info + * @param acctSchema account schema + * @param acct account + */ + public void setAccount (MAcctSchema acctSchema, MAccount acct) + { + m_acctSchema = acctSchema; + setC_AcctSchema_ID (acctSchema.getC_AcctSchema_ID()); + // + m_acct = acct; + if (getAD_Client_ID() == 0) + setAD_Client_ID(m_acct.getAD_Client_ID()); + setAccount_ID (m_acct.getAccount_ID()); + setC_SubAcct_ID(m_acct.getC_SubAcct_ID()); + } // setAccount + + /** + * Set Source Amounts + * @param C_Currency_ID currency + * @param AmtSourceDr source amount dr + * @param AmtSourceCr source amount cr + * @return true, if any if the amount is not zero + */ + public boolean setAmtSource (int C_Currency_ID, BigDecimal AmtSourceDr, BigDecimal AmtSourceCr) + { + setC_Currency_ID (C_Currency_ID); + if (AmtSourceDr != null) + setAmtSourceDr (AmtSourceDr); + if (AmtSourceCr != null) + setAmtSourceCr (AmtSourceCr); + // one needs to be non zero + if (getAmtSourceDr().equals(Env.ZERO) && getAmtSourceCr().equals(Env.ZERO)) + return false; + // Currency Precision + int precision = MCurrency.getStdPrecision(getCtx(), C_Currency_ID); + if (AmtSourceDr != null && AmtSourceDr.scale() > precision) + { + BigDecimal AmtSourceDr1 = AmtSourceDr.setScale(precision, BigDecimal.ROUND_HALF_UP); + log.warning("Source DR Precision " + AmtSourceDr + " -> " + AmtSourceDr1); + setAmtSourceDr(AmtSourceDr1); + } + if (AmtSourceCr != null && AmtSourceCr.scale() > precision) + { + BigDecimal AmtSourceCr1 = AmtSourceCr.setScale(precision, BigDecimal.ROUND_HALF_UP); + log.warning("Source CR Precision " + AmtSourceCr + " -> " + AmtSourceCr1); + setAmtSourceCr(AmtSourceCr1); + } + return true; + } // setAmtSource + + /** + * Set Accounted Amounts (alternative: call convert) + * @param AmtAcctDr acct amount dr + * @param AmtAcctCr acct amount cr + */ + public void setAmtAcct(BigDecimal AmtAcctDr, BigDecimal AmtAcctCr) + { + setAmtAcctDr (AmtAcctDr); + setAmtAcctCr (AmtAcctCr); + } // setAmtAcct + + /** + * Set Document Info + * @param doc document + * @param docLine doc line + */ + public void setDocumentInfo(Doc doc, DocLine docLine) + { + m_doc = doc; + m_docLine = docLine; + // reset + setAD_Org_ID(0); + setC_SalesRegion_ID(0); + // Client + if (getAD_Client_ID() == 0) + setAD_Client_ID (m_doc.getAD_Client_ID()); + // Date Trx + setDateTrx (m_doc.getDateDoc()); + if (m_docLine != null && m_docLine.getDateDoc() != null) + setDateTrx (m_docLine.getDateDoc()); + // Date Acct + setDateAcct (m_doc.getDateAcct()); + if (m_docLine != null && m_docLine.getDateAcct() != null) + setDateAcct (m_docLine.getDateAcct()); + // Period, Tax + if (m_docLine != null && m_docLine.getC_Period_ID() != 0) + setC_Period_ID(m_docLine.getC_Period_ID()); + else + setC_Period_ID (m_doc.getC_Period_ID()); + if (m_docLine != null) + setC_Tax_ID (m_docLine.getC_Tax_ID()); + // Description + StringBuffer description = new StringBuffer(m_doc.getDocumentNo()); + if (m_docLine != null) + { + description.append(" #").append(m_docLine.getLine()); + if (m_docLine.getDescription() != null) + description.append(" (").append(m_docLine.getDescription()).append(")"); + else if (m_doc.getDescription() != null && m_doc.getDescription().length() > 0) + description.append(" (").append(m_doc.getDescription()).append(")"); + } + else if (m_doc.getDescription() != null && m_doc.getDescription().length() > 0) + description.append(" (").append(m_doc.getDescription()).append(")"); + setDescription(description.toString()); + // Journal Info + setGL_Budget_ID (m_doc.getGL_Budget_ID()); + setGL_Category_ID (m_doc.getGL_Category_ID()); + + // Product + if (m_docLine != null) + setM_Product_ID (m_docLine.getM_Product_ID()); + if (getM_Product_ID() == 0) + setM_Product_ID (m_doc.getM_Product_ID()); + // UOM + if (m_docLine != null) + setC_UOM_ID (m_docLine.getC_UOM_ID()); + // Qty + if (get_Value("Qty") == null) // not previously set + { + setQty (m_doc.getQty()); // neg = outgoing + if (m_docLine != null) + setQty (m_docLine.getQty()); + } + + // Loc From (maybe set earlier) + if (getC_LocFrom_ID() == 0 && m_docLine != null) + setC_LocFrom_ID (m_docLine.getC_LocFrom_ID()); + if (getC_LocFrom_ID() == 0) + setC_LocFrom_ID (m_doc.getC_LocFrom_ID()); + // Loc To (maybe set earlier) + if (getC_LocTo_ID() == 0 && m_docLine != null) + setC_LocTo_ID (m_docLine.getC_LocTo_ID()); + if (getC_LocTo_ID() == 0) + setC_LocTo_ID (m_doc.getC_LocTo_ID()); + // BPartner + if (m_docLine != null) + setC_BPartner_ID (m_docLine.getC_BPartner_ID()); + if (getC_BPartner_ID() == 0) + setC_BPartner_ID (m_doc.getC_BPartner_ID()); + // Sales Region from BPLocation/Sales Rep + // Trx Org + if (m_docLine != null) + setAD_OrgTrx_ID (m_docLine.getAD_OrgTrx_ID()); + if (getAD_OrgTrx_ID() == 0) + setAD_OrgTrx_ID (m_doc.getAD_OrgTrx_ID()); + // Project + if (m_docLine != null) + setC_Project_ID (m_docLine.getC_Project_ID()); + if (getC_Project_ID() == 0) + setC_Project_ID (m_doc.getC_Project_ID()); + // Campaign + if (m_docLine != null) + setC_Campaign_ID (m_docLine.getC_Campaign_ID()); + if (getC_Campaign_ID() == 0) + setC_Campaign_ID (m_doc.getC_Campaign_ID()); + // Activity + if (m_docLine != null) + setC_Activity_ID (m_docLine.getC_Activity_ID()); + if (getC_Activity_ID() == 0) + setC_Activity_ID (m_doc.getC_Activity_ID()); + // User List 1 + if (m_docLine != null) + setUser1_ID (m_docLine.getUser1_ID()); + if (getUser1_ID() == 0) + setUser1_ID (m_doc.getUser1_ID()); + // User List 2 + if (m_docLine != null) + setUser2_ID (m_docLine.getUser2_ID()); + if (getUser2_ID() == 0) + setUser2_ID (m_doc.getUser2_ID()); + // User Defined + + } // setDocumentInfo + + /** + * Get Document Line + * @return doc line + */ + protected DocLine getDocLine() + { + return m_docLine; + } // getDocLine + + /** + * Set Description + * @param description description + */ + public void addDescription (String description) + { + String original = getDescription(); + if (original == null || original.trim().length() == 0) + super.setDescription(description); + else + super.setDescription(original + " - " + description); + } // addDescription + + /** + * Set Warehouse Locator. + * - will overwrite Organization - + * @param M_Locator_ID locator + */ + public void setM_Locator_ID (int M_Locator_ID) + { + super.setM_Locator_ID (M_Locator_ID); + setAD_Org_ID(0); // reset + } // setM_Locator_ID + + + /************************************************************************** + * Set Location + * @param C_Location_ID location + * @param isFrom from + */ + public void setLocation (int C_Location_ID, boolean isFrom) + { + if (isFrom) + setC_LocFrom_ID (C_Location_ID); + else + setC_LocTo_ID (C_Location_ID); + } // setLocator + + /** + * Set Location from Locator + * @param M_Locator_ID locator + * @param isFrom from + */ + public void setLocationFromLocator (int M_Locator_ID, boolean isFrom) + { + if (M_Locator_ID == 0) + return; + int C_Location_ID = 0; + String sql = "SELECT w.C_Location_ID FROM M_Warehouse w, M_Locator l " + + "WHERE w.M_Warehouse_ID=l.M_Warehouse_ID AND l.M_Locator_ID=?"; + try + { + PreparedStatement pstmt = DB.prepareStatement(sql, get_TrxName()); + pstmt.setInt(1, M_Locator_ID); + ResultSet rs = pstmt.executeQuery(); + if (rs.next()) + C_Location_ID = rs.getInt(1); + rs.close(); + pstmt.close(); + } + catch (SQLException e) + { + log.log(Level.SEVERE, sql, e); + return; + } + if (C_Location_ID != 0) + setLocation (C_Location_ID, isFrom); + } // setLocationFromLocator + + /** + * Set Location from Busoness Partner Location + * @param C_BPartner_Location_ID bp location + * @param isFrom from + */ + public void setLocationFromBPartner (int C_BPartner_Location_ID, boolean isFrom) + { + if (C_BPartner_Location_ID == 0) + return; + int C_Location_ID = 0; + String sql = "SELECT C_Location_ID FROM C_BPartner_Location WHERE C_BPartner_Location_ID=?"; + try + { + PreparedStatement pstmt = DB.prepareStatement(sql, get_TrxName()); + pstmt.setInt(1, C_BPartner_Location_ID); + ResultSet rs = pstmt.executeQuery(); + if (rs.next()) + C_Location_ID = rs.getInt(1); + rs.close(); + pstmt.close(); + } + catch (SQLException e) + { + log.log(Level.SEVERE, sql, e); + return; + } + if (C_Location_ID != 0) + setLocation (C_Location_ID, isFrom); + } // setLocationFromBPartner + + /** + * Set Location from Organization + * @param AD_Org_ID org + * @param isFrom from + */ + public void setLocationFromOrg (int AD_Org_ID, boolean isFrom) + { + if (AD_Org_ID == 0) + return; + int C_Location_ID = 0; + String sql = "SELECT C_Location_ID FROM AD_OrgInfo WHERE AD_Org_ID=?"; + try + { + PreparedStatement pstmt = DB.prepareStatement(sql, get_TrxName()); + pstmt.setInt(1, AD_Org_ID); + ResultSet rs = pstmt.executeQuery(); + if (rs.next()) + C_Location_ID = rs.getInt(1); + rs.close(); + pstmt.close(); + } + catch (SQLException e) + { + log.log(Level.SEVERE, sql, e); + return; + } + if (C_Location_ID != 0) + setLocation (C_Location_ID, isFrom); + } // setLocationFromOrg + + + /************************************************************************** + * Returns Source Balance of line + * @return source balance + */ + public BigDecimal getSourceBalance() + { + if (getAmtSourceDr() == null) + setAmtSourceDr (Env.ZERO); + if (getAmtSourceCr() == null) + setAmtSourceCr (Env.ZERO); + // + return getAmtSourceDr().subtract(getAmtSourceCr()); + } // getSourceBalance + + /** + * Is Debit Source Balance + * @return true if DR source balance + */ + public boolean isDrSourceBalance() + { + return getSourceBalance().signum() != -1; + } // isDrSourceBalance + + /** + * Get Accounted Balance + * @return accounting balance + */ + public BigDecimal getAcctBalance() + { + if (getAmtAcctDr() == null) + setAmtAcctDr (Env.ZERO); + if (getAmtAcctCr() == null) + setAmtAcctCr (Env.ZERO); + return getAmtAcctDr().subtract(getAmtAcctCr()); + } // getAcctBalance + + /** + * Is Account on Balance Sheet + * @return true if account is a balance sheet account + */ + public boolean isBalanceSheet() + { + return m_acct.isBalanceSheet(); + } // isBalanceSheet + + /** + * Currect Accounting Amount. + *
+	 *  Example:    1       -1      1       -1
+	 *  Old         100/0   100/0   0/100   0/100
+	 *  New         99/0    101/0   0/99    0/101
+	 *  
+ * @param deltaAmount delta amount + */ + public void currencyCorrect (BigDecimal deltaAmount) + { + boolean negative = deltaAmount.compareTo(Env.ZERO) < 0; + boolean adjustDr = getAmtAcctDr().abs().compareTo(getAmtAcctCr().abs()) > 0; + + log.fine(deltaAmount.toString() + + "; Old-AcctDr=" + getAmtAcctDr() + ",AcctCr=" + getAmtAcctCr() + + "; Negative=" + negative + "; AdjustDr=" + adjustDr); + + if (adjustDr) + if (negative) + setAmtAcctDr (getAmtAcctDr().subtract(deltaAmount)); + else + setAmtAcctDr (getAmtAcctDr().subtract(deltaAmount)); + else + if (negative) + setAmtAcctCr (getAmtAcctCr().add(deltaAmount)); + else + setAmtAcctCr (getAmtAcctCr().add(deltaAmount)); + + log.fine("New-AcctDr=" + getAmtAcctDr() + ",AcctCr=" + getAmtAcctCr()); + } // currencyCorrect + + /** + * Convert to Accounted Currency + * @return true if converted + */ + public boolean convert () + { + // Document has no currency + if (getC_Currency_ID() == Doc.NO_CURRENCY) + setC_Currency_ID (m_acctSchema.getC_Currency_ID()); + + if (m_acctSchema.getC_Currency_ID() == getC_Currency_ID()) + { + setAmtAcctDr (getAmtSourceDr()); + setAmtAcctCr (getAmtSourceCr()); + return true; + } + // Get Conversion Type from Line or Header + int C_ConversionType_ID = 0; + int AD_Org_ID = 0; + if (m_docLine != null) // get from line + { + C_ConversionType_ID = m_docLine.getC_ConversionType_ID(); + AD_Org_ID = m_docLine.getAD_Org_ID(); + } + if (C_ConversionType_ID == 0) // get from header + { + if (m_doc == null) + { + log.severe ("No Document VO"); + return false; + } + C_ConversionType_ID = m_doc.getC_ConversionType_ID(); + if (AD_Org_ID == 0) + AD_Org_ID = m_doc.getAD_Org_ID(); + } + setAmtAcctDr (MConversionRate.convert (getCtx(), + getAmtSourceDr(), getC_Currency_ID(), m_acctSchema.getC_Currency_ID(), + getDateAcct(), C_ConversionType_ID, m_doc.getAD_Client_ID(), AD_Org_ID)); + if (getAmtAcctDr() == null) + return false; + setAmtAcctCr (MConversionRate.convert (getCtx(), + getAmtSourceCr(), getC_Currency_ID(), m_acctSchema.getC_Currency_ID(), + getDateAcct(), C_ConversionType_ID, m_doc.getAD_Client_ID(), AD_Org_ID)); + return true; + } // convert + + /** + * Get Account + * @return account + */ + public MAccount getAccount() + { + return m_acct; + } // getAccount + + /** + * To String + * @return String + */ + public String toString() + { + StringBuffer sb = new StringBuffer("FactLine=["); + sb.append(getAD_Table_ID()).append(":").append(getRecord_ID()) + .append(",").append(m_acct) + .append(",Cur=").append(getC_Currency_ID()) + .append(", DR=").append(getAmtSourceDr()).append("|").append(getAmtAcctDr()) + .append(", CR=").append(getAmtSourceCr()).append("|").append(getAmtAcctCr()) + .append("]"); + return sb.toString(); + } // toString + + + /** + * Get AD_Org_ID (balancing segment). + * (if not set directly - from document line, document, account, locator) + *

+ * Note that Locator needs to be set before - otherwise + * segment balancing might produce the wrong results + * @return AD_Org_ID + */ + public int getAD_Org_ID() + { + if (super.getAD_Org_ID() != 0) // set earlier + return super.getAD_Org_ID(); + // Prio 1 - get from locator - if exist + if (getM_Locator_ID() != 0) + { + String sql = "SELECT AD_Org_ID FROM M_Locator WHERE M_Locator_ID=? AND AD_Client_ID=?"; + try + { + PreparedStatement pstmt = DB.prepareStatement(sql, get_TrxName()); + pstmt.setInt(1, getM_Locator_ID()); + pstmt.setInt(2, getAD_Client_ID()); + ResultSet rs = pstmt.executeQuery(); + if (rs.next()) + { + setAD_Org_ID (rs.getInt(1)); + log.finer("AD_Org_ID=" + super.getAD_Org_ID() + " (1 from M_Locator_ID=" + getM_Locator_ID() + ")"); + } + else + log.log(Level.SEVERE, "AD_Org_ID - Did not find M_Locator_ID=" + getM_Locator_ID()); + rs.close(); + pstmt.close(); + } + catch (SQLException e) + { + log.log(Level.SEVERE, sql, e); + } + } // M_Locator_ID != 0 + + // Prio 2 - get from doc line - if exists (document context overwrites) + if (m_docLine != null && super.getAD_Org_ID() == 0) + { + setAD_Org_ID (m_docLine.getAD_Org_ID()); + log.finer("AD_Org_ID=" + super.getAD_Org_ID() + " (2 from DocumentLine)"); + } + // Prio 3 - get from doc - if not GL + if (m_doc != null && super.getAD_Org_ID() == 0) + { + if (Doc.DOCTYPE_GLJournal.equals (m_doc.getDocumentType())) + { + setAD_Org_ID (m_acct.getAD_Org_ID()); // inter-company GL + log.finer("AD_Org_ID=" + super.getAD_Org_ID() + " (3 from Acct)"); + } + else + { + setAD_Org_ID (m_doc.getAD_Org_ID()); + log.finer("AD_Org_ID=" + super.getAD_Org_ID() + " (3 from Document)"); + } + } + // Prio 4 - get from account - if not GL + if (m_doc != null && super.getAD_Org_ID() == 0) + { + if (Doc.DOCTYPE_GLJournal.equals (m_doc.getDocumentType())) + { + setAD_Org_ID (m_doc.getAD_Org_ID()); + log.finer("AD_Org_ID=" + super.getAD_Org_ID() + " (4 from Document)"); + } + else + { + setAD_Org_ID (m_acct.getAD_Org_ID()); + log.finer("AD_Org_ID=" + super.getAD_Org_ID() + " (4 from Acct)"); + } + } + return super.getAD_Org_ID(); + } // setAD_Org_ID + + + /** + * Get/derive Sales Region + * @return Sales Region + */ + public int getC_SalesRegion_ID () + { + if (super.getC_SalesRegion_ID() != 0) + return super.getC_SalesRegion_ID(); + // + if (m_docLine != null) + setC_SalesRegion_ID (m_docLine.getC_SalesRegion_ID()); + if (m_doc != null) + { + if (super.getC_SalesRegion_ID() == 0) + setC_SalesRegion_ID (m_doc.getC_SalesRegion_ID()); + if (super.getC_SalesRegion_ID() == 0 && m_doc.getBP_C_SalesRegion_ID() > 0) + setC_SalesRegion_ID (m_doc.getBP_C_SalesRegion_ID()); + // derive SalesRegion if AcctSegment + if (super.getC_SalesRegion_ID() == 0 + && m_doc.getC_BPartner_Location_ID() != 0 + && m_doc.getBP_C_SalesRegion_ID() == -1) // never tried + // && m_acctSchema.isAcctSchemaElement(MAcctSchemaElement.ELEMENTTYPE_SalesRegion)) + { + String sql = "SELECT COALESCE(C_SalesRegion_ID,0) FROM C_BPartner_Location WHERE C_BPartner_Location_ID=?"; + setC_SalesRegion_ID (DB.getSQLValue(null, + sql, m_doc.getC_BPartner_Location_ID())); + if (super.getC_SalesRegion_ID() != 0) // save in VO + { + m_doc.setBP_C_SalesRegion_ID(super.getC_SalesRegion_ID()); + log.fine("C_SalesRegion_ID=" + super.getC_SalesRegion_ID() + " (from BPL)" ); + } + else // From Sales Rep of Document -> Sales Region + { + sql = "SELECT COALESCE(MAX(C_SalesRegion_ID),0) FROM C_SalesRegion WHERE SalesRep_ID=?"; + setC_SalesRegion_ID (DB.getSQLValue(null, + sql, m_doc.getSalesRep_ID())); + if (super.getC_SalesRegion_ID() != 0) // save in VO + { + m_doc.setBP_C_SalesRegion_ID(super.getC_SalesRegion_ID()); + log.fine("C_SalesRegion_ID=" + super.getC_SalesRegion_ID() + " (from SR)" ); + } + else + m_doc.setBP_C_SalesRegion_ID(-2); // don't try again + } + } + if (m_acct != null && super.getC_SalesRegion_ID() == 0) + setC_SalesRegion_ID (m_acct.getC_SalesRegion_ID()); + } + // + // log.fine("C_SalesRegion_ID=" + super.getC_SalesRegion_ID() + // + ", C_BPartner_Location_ID=" + m_docVO.C_BPartner_Location_ID + // + ", BP_C_SalesRegion_ID=" + m_docVO.BP_C_SalesRegion_ID + // + ", SR=" + m_acctSchema.isAcctSchemaElement(MAcctSchemaElement.ELEMENTTYPE_SalesRegion)); + return super.getC_SalesRegion_ID(); + } // getC_SalesRegion_ID + + + /** + * Before Save + * @param newRecord new + * @return true + */ + protected boolean beforeSave (boolean newRecord) + { + if (newRecord) + { + log.fine(toString()); + // + getAD_Org_ID(); + getC_SalesRegion_ID(); + // Set Default Account Info + if (getM_Product_ID() == 0) + setM_Product_ID (m_acct.getM_Product_ID()); + if (getC_LocFrom_ID() == 0) + setC_LocFrom_ID (m_acct.getC_LocFrom_ID()); + if (getC_LocTo_ID() == 0) + setC_LocTo_ID (m_acct.getC_LocTo_ID()); + if (getC_BPartner_ID() == 0) + setC_BPartner_ID (m_acct.getC_BPartner_ID()); + if (getAD_OrgTrx_ID() == 0) + setAD_OrgTrx_ID (m_acct.getAD_OrgTrx_ID()); + if (getC_Project_ID() == 0) + setC_Project_ID (m_acct.getC_Project_ID()); + if (getC_Campaign_ID() == 0) + setC_Campaign_ID (m_acct.getC_Campaign_ID()); + if (getC_Activity_ID() == 0) + setC_Activity_ID (m_acct.getC_Activity_ID()); + if (getUser1_ID() == 0) + setUser1_ID (m_acct.getUser1_ID()); + if (getUser2_ID() == 0) + setUser2_ID (m_acct.getUser2_ID()); + + // Revenue Recognition for AR Invoices + if (m_doc.getDocumentType().equals(Doc.DOCTYPE_ARInvoice) + && m_docLine != null + && m_docLine.getC_RevenueRecognition_ID() != 0) + { + int AD_User_ID = 0; + setAccount_ID ( + createRevenueRecognition ( + m_docLine.getC_RevenueRecognition_ID(), m_docLine.get_ID(), + getAD_Client_ID(), getAD_Org_ID(), AD_User_ID, + getAccount_ID(), getC_SubAcct_ID(), + getM_Product_ID(), getC_BPartner_ID(), getAD_OrgTrx_ID(), + getC_LocFrom_ID(), getC_LocTo_ID(), + getC_SalesRegion_ID(), getC_Project_ID(), + getC_Campaign_ID(), getC_Activity_ID(), + getUser1_ID(), getUser2_ID(), + getUserElement1_ID(), getUserElement2_ID()) + ); + } + } + return true; + } // beforeSave + + + /************************************************************************** + * Revenue Recognition. + * Called from FactLine.save + *

+ * Create Revenue recognition plan and return Unearned Revenue account + * to be used instead of Revenue Account. If not found, it returns + * the revenue account. + * + * @param C_RevenueRecognition_ID revenue recognition + * @param C_InvoiceLine_ID invoice line + * @param AD_Client_ID client + * @param AD_Org_ID org + * @param AD_User_ID user + * @param Account_ID of Revenue Account + * @param C_SubAcct_ID sub account + * @param M_Product_ID product + * @param C_BPartner_ID bpartner + * @param AD_OrgTrx_ID trx org + * @param C_LocFrom_ID loc from + * @param C_LocTo_ID loc to + * @param C_SRegion_ID sales region + * @param C_Project_ID project + * @param C_Campaign_ID campaign + * @param C_Activity_ID activity + * @param User1_ID user1 + * @param User2_ID user2 + * @param UserElement1_ID user element 1 + * @param UserElement2_ID user element 2 + * @return Account_ID for Unearned Revenue or Revenue Account if not found + */ + private int createRevenueRecognition ( + int C_RevenueRecognition_ID, int C_InvoiceLine_ID, + int AD_Client_ID, int AD_Org_ID, int AD_User_ID, + int Account_ID, int C_SubAcct_ID, + int M_Product_ID, int C_BPartner_ID, int AD_OrgTrx_ID, + int C_LocFrom_ID, int C_LocTo_ID, int C_SRegion_ID, int C_Project_ID, + int C_Campaign_ID, int C_Activity_ID, + int User1_ID, int User2_ID, int UserElement1_ID, int UserElement2_ID) + { + log.fine("From Accout_ID=" + Account_ID); + // get VC for P_Revenue (from Product) + MAccount revenue = MAccount.get(getCtx(), + AD_Client_ID, AD_Org_ID, getC_AcctSchema_ID(), Account_ID, C_SubAcct_ID, + M_Product_ID, C_BPartner_ID, AD_OrgTrx_ID, C_LocFrom_ID, C_LocTo_ID, C_SRegion_ID, + C_Project_ID, C_Campaign_ID, C_Activity_ID, + User1_ID, User2_ID, UserElement1_ID, UserElement2_ID); + if (revenue != null && revenue.get_ID() == 0) + revenue.save(); + if (revenue == null || revenue.get_ID() == 0) + { + log.severe ("Revenue_Acct not found"); + return Account_ID; + } + int P_Revenue_Acct = revenue.get_ID(); + + // get Unearned Revenue Acct from BPartner Group + int UnearnedRevenue_Acct = 0; + int new_Account_ID = 0; + String sql = "SELECT ga.UnearnedRevenue_Acct, vc.Account_ID " + + "FROM C_BP_Group_Acct ga, C_BPartner p, C_ValidCombination vc " + + "WHERE ga.C_BP_Group_ID=p.C_BP_Group_ID" + + " AND ga.UnearnedRevenue_Acct=vc.C_ValidCombination_ID" + + " AND ga.C_AcctSchema_ID=? AND p.C_BPartner_ID=?"; + try + { + PreparedStatement pstmt = DB.prepareStatement(sql, get_TrxName()); + pstmt.setInt(1, getC_AcctSchema_ID()); + pstmt.setInt(2, C_BPartner_ID); + ResultSet rs = pstmt.executeQuery(); + if (rs.next()) + { + UnearnedRevenue_Acct = rs.getInt(1); + new_Account_ID = rs.getInt(2); + } + rs.close(); + pstmt.close(); + } + catch (SQLException e) + { + log.log(Level.SEVERE, sql, e); + } + if (new_Account_ID == 0) + { + log.severe ("UnearnedRevenue_Acct not found"); + return Account_ID; + } + + MRevenueRecognitionPlan plan = new MRevenueRecognitionPlan(getCtx(), 0, null); + plan.setC_RevenueRecognition_ID (C_RevenueRecognition_ID); + plan.setC_AcctSchema_ID (getC_AcctSchema_ID()); + plan.setC_InvoiceLine_ID (C_InvoiceLine_ID); + plan.setUnEarnedRevenue_Acct (UnearnedRevenue_Acct); + plan.setP_Revenue_Acct (P_Revenue_Acct); + plan.setC_Currency_ID (getC_Currency_ID()); + plan.setTotalAmt (getAcctBalance()); + if (!plan.save(get_TrxName())) + { + log.severe ("Plan NOT created"); + return Account_ID; + } + log.fine("From Acctount_ID=" + Account_ID + " to " + new_Account_ID + + " - Plan from UnearnedRevenue_Acct=" + UnearnedRevenue_Acct + " to Revenue_Acct=" + P_Revenue_Acct); + return new_Account_ID; + } // createRevenueRecognition + + + /************************************************************************** + * Update Line with reversed Original Amount in Accounting Currency. + * Also copies original dimensions like Project, etc. + * Called from Doc_MatchInv + * @param AD_Table_ID table + * @param Record_ID record + * @param Line_ID line + * @param multiplier targetQty/documentQty + * @return true if success + */ + public boolean updateReverseLine (int AD_Table_ID, int Record_ID, int Line_ID, + BigDecimal multiplier) + { + boolean success = false; + + String sql = "SELECT * " + + "FROM Fact_Acct " + + "WHERE C_AcctSchema_ID=? AND AD_Table_ID=? AND Record_ID=?" + + " AND Line_ID=? AND Account_ID=?"; + try + { + PreparedStatement pstmt = DB.prepareStatement(sql, get_TrxName()); + pstmt.setInt(1, getC_AcctSchema_ID()); + pstmt.setInt(2, AD_Table_ID); + pstmt.setInt(3, Record_ID); + pstmt.setInt(4, Line_ID); + pstmt.setInt(5, m_acct.getAccount_ID()); + ResultSet rs = pstmt.executeQuery(); + if (rs.next()) + { + MFactAcct fact = new MFactAcct(getCtx(), rs, get_TrxName()); + // Accounted Amounts - reverse + BigDecimal dr = fact.getAmtAcctDr(); + BigDecimal cr = fact.getAmtAcctCr(); + setAmtAcctDr (cr.multiply(multiplier)); + setAmtAcctCr (dr.multiply(multiplier)); + // Source Amounts + setAmtSourceDr (getAmtAcctDr()); + setAmtSourceCr (getAmtAcctCr()); + // + success = true; + log.fine(new StringBuffer("(Table=").append(AD_Table_ID) + .append(",Record_ID=").append(Record_ID) + .append(",Line=").append(Record_ID) + .append(", Account=").append(m_acct) + .append(",dr=").append(dr).append(",cr=").append(cr) + .append(") - DR=").append(getAmtSourceDr()).append("|").append(getAmtAcctDr()) + .append(", CR=").append(getAmtSourceCr()).append("|").append(getAmtAcctCr()) + .toString()); + // Dimensions + setAD_OrgTrx_ID(fact.getAD_OrgTrx_ID()); + setC_Project_ID (fact.getC_Project_ID()); + setC_Activity_ID(fact.getC_Activity_ID()); + setC_Campaign_ID(fact.getC_Campaign_ID()); + setC_SalesRegion_ID(fact.getC_SalesRegion_ID()); + setC_LocFrom_ID(fact.getC_LocFrom_ID()); + setC_LocTo_ID(fact.getC_LocTo_ID()); + setM_Product_ID(fact.getM_Product_ID()); + setM_Locator_ID(fact.getM_Locator_ID()); + setUser1_ID(fact.getUser1_ID()); + setUser2_ID(fact.getUser2_ID()); + setC_UOM_ID(fact.getC_UOM_ID()); + setC_Tax_ID(fact.getC_Tax_ID()); + // Org for cross charge + setAD_Org_ID (fact.getAD_Org_ID()); + } + else + log.warning(new StringBuffer("Not Found (try later) ") + .append(",C_AcctSchema_ID=").append(getC_AcctSchema_ID()) + .append(", AD_Table_ID=").append(AD_Table_ID) + .append(",Record_ID=").append(Record_ID) + .append(",Line_ID=").append(Line_ID) + .append(", Account_ID=").append(m_acct.getAccount_ID()).toString()); + rs.close(); + pstmt.close(); + } + catch (SQLException e) + { + log.log(Level.SEVERE, sql, e); + } + return success; + } // updateReverseLine + +} // FactLine diff --git a/serverRoot/src/main/server/org/compiere/acct/Matcher.java b/serverRoot/src/main/server/org/compiere/acct/Matcher.java new file mode 100644 index 0000000000..0a7e8886b3 --- /dev/null +++ b/serverRoot/src/main/server/org/compiere/acct/Matcher.java @@ -0,0 +1,165 @@ +/****************************************************************************** + * 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.acct; + +import java.math.*; +import java.sql.*; +import java.util.logging.*; +import org.compiere.util.*; + +/** + * Automatic Matching. + * Inv + * + * @author Jorg Janke + * @version $Id: Matcher.java,v 1.2 2006/07/30 00:53:33 jjanke Exp $ + */ +public class Matcher +{ + /** + * Constructor + * @param AD_Client_ID Client + * @param trxName transaction + */ + public Matcher (int AD_Client_ID, String trxName) + { + m_AD_Client_ID = AD_Client_ID; + m_trxName = trxName; + } // Matcher + + /** Client */ + private int m_AD_Client_ID; + /** Transaction */ + private String m_trxName = null; + /** Logger */ + protected CLogger log = CLogger.getCLogger (getClass()); + + /** + * Matching + *

+	 *  Derive Invoice-Receipt Match from PO-Invoice and PO-Receipt
+	 * 	Purchase Order (20)
+	 *  - Invoice1 (10)
+	 *  - Invoice2 (10)
+	 *  - Receipt1 (5)
+	 *  - Receipt2 (15)
+	 *
+	 * 	(a) Creates Directs
+	 * 		- Invoice1 - Receipt1 (5)
+	 * 		- Invoice2 - Receipt2 (10)
+	 *
+	 *  (b) Creates Indirects
+	 * 		- Invoice1 - Receipt2 (5)
+	 *  (Not imlemented)
+	 *
+	 *
+	 *  
+ * @return number of records created + */ + public int match() + { + int counter = 0; + // (a) Direct Matches + String sql = "SELECT m1.AD_Client_ID,m2.AD_Org_ID, " // 1..2 + + "m1.C_InvoiceLine_ID,m2.M_InOutLine_ID,m1.M_Product_ID, " // 3..5 + + "m1.DateTrx,m2.DateTrx, m1.Qty, m2.Qty " // 6..9 + + "FROM M_MatchPO m1, M_MatchPO m2 " + + "WHERE m1.C_OrderLine_ID=m2.C_OrderLine_ID" + + " AND m1.M_InOutLine_ID IS NULL" + + " AND m2.C_InvoiceLine_ID IS NULL" + + " AND m1.M_Product_ID=m2.M_Product_ID" + + " AND m1.AD_Client_ID=?" // #1 + // Not existing Inv Matches + + " AND NOT EXISTS (SELECT * FROM M_MatchInv mi " + + "WHERE mi.C_InvoiceLine_ID=m1.C_InvoiceLine_ID AND mi.M_InOutLine_ID=m2.M_InOutLine_ID)"; + try + { + PreparedStatement pstmt = DB.prepareStatement(sql, null); + pstmt.setInt(1, m_AD_Client_ID); + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) + { + BigDecimal qty1 = rs.getBigDecimal(8); + BigDecimal qty2 = rs.getBigDecimal(9); + BigDecimal Qty = qty1.min(qty2); + if (Qty.equals(Env.ZERO)) + continue; + Timestamp dateTrx1 = rs.getTimestamp(6); + Timestamp dateTrx2 = rs.getTimestamp(7); + Timestamp DateTrx = dateTrx1; + if (dateTrx1.before(dateTrx2)) + DateTrx = dateTrx2; + // + int AD_Client_ID = rs.getInt(1); + int AD_Org_ID = rs.getInt(2); + int C_InvoiceLine_ID = rs.getInt(3); + int M_InOutLine_ID = rs.getInt(4); + int M_Product_ID = rs.getInt(5); + // + if (createMatchInv(AD_Client_ID, AD_Org_ID, + M_InOutLine_ID, C_InvoiceLine_ID, + M_Product_ID, DateTrx, Qty)) + counter++; + } + rs.close(); + pstmt.close(); + } + catch (SQLException e) + { + log.log(Level.SEVERE, "match", e); + } + log.fine("Matcher.match - Client_ID=" + m_AD_Client_ID + + ", Records created=" + counter); + return counter; + } // match + + /** + * Create MatchInv record + * @param AD_Client_ID Client + * @param AD_Org_ID Org + * @param M_InOutLine_ID Receipt + * @param C_InvoiceLine_ID Invoice + * @param M_Product_ID Product + * @param DateTrx Date + * @param Qty Qty + * @return true if record created + */ + private boolean createMatchInv (int AD_Client_ID, int AD_Org_ID, + int M_InOutLine_ID, int C_InvoiceLine_ID, + int M_Product_ID, Timestamp DateTrx, BigDecimal Qty) + { + log.fine("InvLine=" + C_InvoiceLine_ID + ",Rec=" + M_InOutLine_ID + ", Qty=" + Qty + ", " + DateTrx); + + // MMatchInv inv = new MMatchInv (); + int M_MatchInv_ID = DB.getNextID (AD_Client_ID, "M_MatchInv", m_trxName); + // + StringBuffer sql = new StringBuffer("INSERT INTO M_MatchInv (" + + "M_MatchInv_ID, " + + "AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy, " + + "M_InOutLine_ID,C_InvoiceLine_ID, " + + "M_Product_ID,DateTrx,Qty, " + + "Processing,Processed,Posted) VALUES (") + .append(M_MatchInv_ID).append(", ") + .append(AD_Client_ID).append(",").append(AD_Org_ID).append(",'Y',SysDate,0,SysDate,0, ") + .append(M_InOutLine_ID).append(",").append(C_InvoiceLine_ID).append(", ") + .append(M_Product_ID).append(",").append(DB.TO_DATE(DateTrx,true)).append(",").append(Qty) + .append(", 'N','Y','N')"); + int no = DB.executeUpdate(sql.toString(), m_trxName); + return no == 1; + } // createMatchInv + +} // Matcher diff --git a/serverRoot/src/main/server/org/compiere/acct/ProductInfo.java b/serverRoot/src/main/server/org/compiere/acct/ProductInfo.java new file mode 100644 index 0000000000..fd744a3644 --- /dev/null +++ b/serverRoot/src/main/server/org/compiere/acct/ProductInfo.java @@ -0,0 +1,390 @@ +/****************************************************************************** + * 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.acct; + +import java.math.*; +import java.sql.*; +import java.util.logging.*; +import org.compiere.model.*; +import org.compiere.util.*; + +/** + * Product Costing Information. + * + * @author Jorg Janke + * @version $Id: ProductInfo.java,v 1.2 2006/07/30 00:53:33 jjanke Exp $ + */ +public class ProductInfo +{ + /** + * Constructor + * @param M_Product_ID Product + * @param trxName transcation + */ + public ProductInfo (int M_Product_ID, String trxName) + { + m_trxName = trxName; + init (M_Product_ID); + } // ProductInfo + + /** The Product Key */ + private int m_M_Product_ID = 0; + /** Transaction */ + private String m_trxName = null; + + // Product Info + private int m_AD_Client_ID = 0; + private int m_AD_Org_ID = 0; + + private String m_productType = null; + private String m_ProductCategory = null; + + private boolean m_isBOM = false; + private boolean m_isStocked = true; + + private int m_C_RevenueRecognition_ID = 0; + + private int m_C_UOM_ID = 0; + private BigDecimal m_qty = Env.ZERO; + + /** Logger */ + protected CLogger log = CLogger.getCLogger (getClass()); + + /** + * Get Product Info (Service, Revenue Recognition). + * automatically called by constructor + * @param M_Product_ID Product + */ + private void init (int M_Product_ID) + { + m_M_Product_ID = M_Product_ID; + if (m_M_Product_ID == 0) + return; + + String sql = "SELECT p.ProductType, pc.Value, " // 1..2 + + "p.C_RevenueRecognition_ID,p.C_UOM_ID, " // 3..4 + + "p.AD_Client_ID,p.AD_Org_ID, " // 5..6 + + "p.IsBOM, p.IsStocked " // 7..8 + + "FROM M_Product_Category pc" + + " INNER JOIN M_Product p ON (pc.M_Product_Category_ID=p.M_Product_Category_ID) " + + "WHERE p.M_Product_ID=?"; // #1 + try + { + PreparedStatement pstmt = DB.prepareStatement(sql, null); + pstmt.setInt(1, m_M_Product_ID); + ResultSet rs = pstmt.executeQuery(); + if (rs.next()) + { + m_productType = rs.getString(1); + m_ProductCategory = rs.getString(2); + m_C_RevenueRecognition_ID = rs.getInt(3); + m_C_UOM_ID = rs.getInt(4); + // reference + m_AD_Client_ID = rs.getInt(5); + m_AD_Org_ID = rs.getInt(6); + // + m_isBOM = "Y".equals(rs.getString(7)); + m_isStocked = "Y".equals(rs.getString(8)); + } + rs.close(); + pstmt.close(); + } + catch (SQLException e) + { + log.log(Level.SEVERE, sql, e); + } + } // init + + /** + * Is Product/Item + * @return true if product + */ + public boolean isProduct() + { + return MProduct.PRODUCTTYPE_Item.equals(m_productType); + } // isProduct + + /** + * Is it a BOM + * @return true if BOM + */ + public boolean isBOM() + { + return m_isBOM; + } // isBOM + + /** + * Is it stocked + * @return true if stocked + */ + public boolean isStocked() + { + return m_isStocked; + } // isStocked + + /** + * Is Service + * @return true if service + */ + public boolean isService() + { + return MProduct.PRODUCTTYPE_Service.equals(m_productType); + } // isService + + /** + * Get Product Category (Value) + * @return M_Product_Category_ID + */ + public String getProductCategory() + { + return m_ProductCategory; + } // getProductCategory + + /** + * Has Revenue Recognition + * @return true if product/service has revenue recognition + */ + public boolean isRevenueRecognition() + { + return m_C_RevenueRecognition_ID != 0; + } // isRevenueRecognition + + /** + * Get Revenue Recognition + * @return C_RevenueRecognition_ID + */ + public int getC_RevenueRecognition_ID() + { + return m_C_RevenueRecognition_ID; + } // getC_RevenueRecognition_ID + + /** + * Quantity UOM + * @return C_UOM_ID + */ + public int getC_UOM_ID() + { + return m_C_UOM_ID; + } // getC_UOM_ID + + /*************************************************************************/ + + /** + * Set Quantity in Storage UOM + * @param qty quantity + */ + public void setQty (BigDecimal qty) + { + m_qty = qty; + } // setQty + + /** + * Set Quantity in UOM + * @param qty quantity + * @param C_UOM_ID UOM + */ + public void setQty (BigDecimal qty, int C_UOM_ID) + { + m_qty = MUOMConversion.convert (C_UOM_ID, m_C_UOM_ID, qty, true); // StdPrecision + if (qty != null && m_qty == null) // conversion error + { + log.severe ("Conversion error - set to " + qty); + m_qty = qty; + } + } // setQty + + /** + * Get Qty in Storage UOM + * @return qty + */ + public BigDecimal getQty() + { + return m_qty; + } // getQty + + + + /** + * Update/Create initial Cost Record. + * Check first for Purchase Price List, + * then Product Purchase Costs + * and then Price List + * @param as accounting schema + * @param create create record + * @return costs + */ + private BigDecimal updateCosts (MAcctSchema as, boolean create) + { + // Create Zero Record + if (create) + { + StringBuffer sql = new StringBuffer ("INSERT INTO M_Product_Costing " + + "(M_Product_ID,C_AcctSchema_ID," + + " AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy," + + " CurrentCostPrice,CostStandard,FutureCostPrice," + + " CostStandardPOQty,CostStandardPOAmt,CostStandardCumQty,CostStandardCumAmt," + + " CostAverage,CostAverageCumQty,CostAverageCumAmt," + + " PriceLastPO,PriceLastInv, TotalInvQty,TotalInvAmt) " + + "VALUES ("); + sql.append(m_M_Product_ID).append(",").append(as.getC_AcctSchema_ID()).append(",") + .append(m_AD_Client_ID).append(",").append(m_AD_Org_ID).append(",") + .append("'Y',SysDate,0,SysDate,0, 0,0,0, 0,0,0,0, 0,0,0, 0,0, 0,0)"); + int no = DB.executeUpdate(sql.toString(), m_trxName); + if (no == 1) + log.fine("CostingCreated"); + } + + // Try to find non ZERO Price + String costSource = "PriceList-PO"; + BigDecimal costs = getPriceList (as, true); + if (costs == null || costs.equals(Env.ZERO)) + { + costSource = "PO Cost"; + costs = getPOCost(as); + } + if (costs == null || costs.equals(Env.ZERO)) + { + costSource = "PriceList"; + costs = getPriceList (as, false); + } + + // if not found use $1 (to be able to do material transactions) + if (costs == null || costs.equals(Env.ZERO)) + { + costSource = "Not Found"; + costs = new BigDecimal("1"); + } + + // update current costs + StringBuffer sql = new StringBuffer ("UPDATE M_Product_Costing "); + sql.append("SET CurrentCostPrice=").append(costs) + .append(" WHERE M_Product_ID=").append(m_M_Product_ID) + .append(" AND C_AcctSchema_ID=").append(as.getC_AcctSchema_ID()); + int no = DB.executeUpdate(sql.toString(), m_trxName); + if (no == 1) + log.fine(costSource + " - " + costs); + return costs; + } // createCosts + + /** + * Get PO Price from PriceList - and convert it to AcctSchema Currency + * @param as accounting schema + * @param onlyPOPriceList use only PO price list + * @return po price + */ + private BigDecimal getPriceList (MAcctSchema as, boolean onlyPOPriceList) + { + StringBuffer sql = new StringBuffer ( + "SELECT pl.C_Currency_ID, pp.PriceList, pp.PriceStd, pp.PriceLimit " + + "FROM M_PriceList pl, M_PriceList_Version plv, M_ProductPrice pp " + + "WHERE pl.M_PriceList_ID = plv.M_PriceList_ID" + + " AND plv.M_PriceList_Version_ID = pp.M_PriceList_Version_ID" + + " AND pp.M_Product_ID=?"); + if (onlyPOPriceList) + sql.append(" AND pl.IsSOPriceList='N'"); + sql.append(" ORDER BY pl.IsSOPriceList ASC, plv.ValidFrom DESC"); + int C_Currency_ID = 0; + BigDecimal PriceList = null; + BigDecimal PriceStd = null; + BigDecimal PriceLimit = null; + try + { + PreparedStatement pstmt = DB.prepareStatement(sql.toString(), null); + pstmt.setInt(1, m_M_Product_ID); + ResultSet rs = pstmt.executeQuery(); + if (rs.next()) + { + C_Currency_ID = rs.getInt(1); + PriceList = rs.getBigDecimal(2); + PriceStd = rs.getBigDecimal(3); + PriceLimit = rs.getBigDecimal(4); + } + rs.close(); + pstmt.close(); + } + catch (SQLException e) + { + log.log(Level.SEVERE, sql.toString(), e); + } + // nothing found + if (C_Currency_ID == 0) + return null; + + BigDecimal price = PriceLimit; // best bet + if (price == null || price.equals(Env.ZERO)) + price = PriceStd; + if (price == null || price.equals(Env.ZERO)) + price = PriceList; + // Convert + if (price != null && !price.equals(Env.ZERO)) + price = MConversionRate.convert (as.getCtx(), + price, C_Currency_ID, as.getC_Currency_ID(), + as.getAD_Client_ID(), 0); + return price; + } // getPOPrice + + /** + * Get PO Cost from Purchase Info - and convert it to AcctSchema Currency + * @param as accounting schema + * @return po cost + */ + private BigDecimal getPOCost (MAcctSchema as) + { + String sql = "SELECT C_Currency_ID, PriceList,PricePO,PriceLastPO " + + "FROM M_Product_PO WHERE M_Product_ID=? " + + "ORDER BY IsCurrentVendor DESC"; + + int C_Currency_ID = 0; + BigDecimal PriceList = null; + BigDecimal PricePO = null; + BigDecimal PriceLastPO = null; + try + { + PreparedStatement pstmt = DB.prepareStatement(sql, null); + pstmt.setInt(1, m_M_Product_ID); + ResultSet rs = pstmt.executeQuery(); + if (rs.next()) + { + C_Currency_ID = rs.getInt(1); + PriceList = rs.getBigDecimal(2); + PricePO = rs.getBigDecimal(3); + PriceLastPO = rs.getBigDecimal(4); + } + rs.close(); + pstmt.close(); + } + catch (SQLException e) + { + log.log(Level.SEVERE, sql, e); + } + // nothing found + if (C_Currency_ID == 0) + return null; + + BigDecimal cost = PriceLastPO; // best bet + if (cost == null || cost.equals(Env.ZERO)) + cost = PricePO; + if (cost == null || cost.equals(Env.ZERO)) + cost = PriceList; + // Convert - standard precision!! - should be costing precision + if (cost != null && !cost.equals(Env.ZERO)) + cost = MConversionRate.convert (as.getCtx(), + cost, C_Currency_ID, as.getC_Currency_ID(), m_AD_Client_ID, m_AD_Org_ID); + return cost; + } // getPOCost + +} // ProductInfo diff --git a/serverRoot/src/main/server/org/compiere/ldap/LdapConnectionHandler.java b/serverRoot/src/main/server/org/compiere/ldap/LdapConnectionHandler.java new file mode 100644 index 0000000000..532c7062da --- /dev/null +++ b/serverRoot/src/main/server/org/compiere/ldap/LdapConnectionHandler.java @@ -0,0 +1,118 @@ +/****************************************************************************** + * 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. + * You may reach us at: ComPiere, Inc. - http://www.adempiere.org/license.html + * 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA or info@adempiere.org + *****************************************************************************/ +package org.compiere.ldap; + +import java.io.*; +import java.net.*; +import java.util.logging.*; +import org.compiere.ldap.*; +import org.compiere.util.*; +import com.sun.jndi.ldap.*; + +/** + * LDAP Connection Handler + * + * @author Jorg Janke + * @version $Id: LdapConnectionHandler.java,v 1.1 2006/10/09 00:23:16 jjanke Exp $ + */ +public class LdapConnectionHandler extends Thread +{ + /** + * Ldap Connection Handler + * @param socket server socket + */ + public LdapConnectionHandler(Socket socket) + { + try + { + m_socket = socket; + m_socket.setTcpNoDelay(true); // should not be required + } + catch (Exception e) + { + log.log(Level.SEVERE, "", e); + } // no timeout + } // LdapConnectionHandler + + /** Socket */ + private Socket m_socket = null; + /** Logger */ + private static CLogger log = CLogger.getCLogger (LdapConnectionHandler.class); + + + /** + * Do Work + */ + public void run() + { + try + { + if (m_socket == null || m_socket.isClosed()) + return; + + boolean activeSession = true; + while (activeSession) + { + InputStream in = m_socket.getInputStream(); + BufferedOutputStream out = new BufferedOutputStream(m_socket.getOutputStream()); + // Read + byte[] buffer = new byte[512]; + int length = in.read(buffer, 0, 512); + + LdapMessage msg = new LdapMessage (buffer, length); + if (msg.getOperation() == LdapMessage.UNBIND_REQUEST) + { + activeSession = false; + out.close(); + } + else + { + LdapResult result = new LdapResult (); + byte[] bytes = result.bindResponse(); + // + out.write(bytes); + out.flush(); + } + } + } + catch (IOException e) + { + log.log(Level.SEVERE, "", e); + } + + try + { + m_socket.close(); + } + catch (Exception e) + { + log.log(Level.WARNING, "Socket", e); + } + m_socket = null; + } // run + + /** + * String Representation + * @return info + */ + public String toString() + { + StringBuffer sb = new StringBuffer ("LdapConnectionHandler["); + sb.append (hashCode()).append ("]"); + return sb.toString (); + } // toString + +} // LdapConnectionHandler diff --git a/serverRoot/src/main/server/org/compiere/ldap/LdapMessage.java b/serverRoot/src/main/server/org/compiere/ldap/LdapMessage.java new file mode 100644 index 0000000000..e3e0c26e0f --- /dev/null +++ b/serverRoot/src/main/server/org/compiere/ldap/LdapMessage.java @@ -0,0 +1,172 @@ +/****************************************************************************** + * 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. + * You may reach us at: ComPiere, Inc. - http://www.adempiere.org/license.html + * 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA or info@adempiere.org + *****************************************************************************/ +package org.compiere.ldap; + +import java.util.logging.*; +import org.compiere.util.*; +import com.sun.jndi.ldap.*; + +/** + * Ldap Message + * + * @author Jorg Janke + * @version $Id: LdapMessage.java,v 1.1 2006/10/09 00:23:16 jjanke Exp $ + */ +public class LdapMessage +{ + /** + * Ldap Message + * @param data BER data + * @param length Ber data length + */ + public LdapMessage (byte[] data, int length) + { + try + { + decode(data, length); + } + catch (Exception e) + { + log.log(Level.SEVERE, data.toString(), e); + } + } // LdapMessage + + /** + LDAPMessage ::= SEQUENCE { + messageID MessageID, + protocolOp CHOICE { + bindRequest BindRequest, + bindResponse BindResponse, + unbindRequest UnbindRequest, + searchRequest SearchRequest, + searchResEntry SearchResultEntry, + searchResDone SearchResultDone, + searchResRef SearchResultReference, + modifyRequest ModifyRequest, + modifyResponse ModifyResponse, + addRequest AddRequest, + addResponse AddResponse, + delRequest DelRequest, + delResponse DelResponse, + modDNRequest ModifyDNRequest, + modDNResponse ModifyDNResponse, + compareRequest CompareRequest, + compareResponse CompareResponse, + abandonRequest AbandonRequest, + extendedReq ExtendedRequest, + extendedResp ExtendedResponse }, + controls [0] Controls OPTIONAL } + **/ + + static public final int BIND_REQUEST = 0; + static public final int BIND_RESPONSE = 1; + static public final int UNBIND_REQUEST = 2; + static public final int SEARCH_REQUEST = 3; + static public final int SEARCH_RESENTRY = 4; + static public final int SEARCH_RESDONE = 5; + static public final int MODIFY_REQUEST = 6; + static public final int MODIFY_RESPONSE = 7; + static public final int ADD_REQUEST = 8; + static public final int ADD_RESPONSE = 9; + static public final int DEL_REQUEST = 10; + static public final int DEL_RESPONSE = 11; + static public final int MODDN_REQUEST = 12; + static public final int MODDN_RESPONSE = 13; + static public final int COMPARE_REQUEST = 14; + static public final int COMPARE_RESPONSE = 15; + static public final int ABANDON_REQUEST = 16; + static public final int EXTENDED_REQUEST = 17; + static public final int EXTENDED_RESPONSE = 18; + + static public final int[] PROTOCOL_OP = { + BIND_REQUEST, BIND_RESPONSE, UNBIND_REQUEST, + SEARCH_REQUEST, SEARCH_RESENTRY, SEARCH_RESDONE, + MODIFY_REQUEST, MODIFY_RESPONSE, ADD_REQUEST, ADD_RESPONSE, + DEL_REQUEST, DEL_RESPONSE, MODDN_REQUEST, MODDN_RESPONSE, + COMPARE_REQUEST, COMPARE_RESPONSE, ABANDON_REQUEST, + EXTENDED_REQUEST, EXTENDED_RESPONSE}; + + + /** Logger */ + private static CLogger log = CLogger.getCLogger (LdapMessage.class); + /** Protocol Operation */ + private int m_protocolOp = -1; + + + /** + * Decode Message + * @param data data + * @param length length + * @throws Exception + */ + private void decode (byte[] data, int length) throws Exception + { + BerDecoder decoder = new BerDecoder(data, 0, length); + int left = decoder.bytesLeft(); + int pos = decoder.getParsePosition(); + // + int seq = decoder.parseSeq(null); + left = decoder.bytesLeft(); + pos = decoder.getParsePosition(); + // + int messageID = decoder.parseInt(); + left = decoder.bytesLeft(); + pos = decoder.getParsePosition(); + // + int peek = decoder.peekByte(); + m_protocolOp = decoder.parseSeq(PROTOCOL_OP); + m_protocolOp -= Ber.ASN_APPLICATION; + if (m_protocolOp - Ber.ASN_CONSTRUCTOR >= 0) + m_protocolOp -= Ber.ASN_CONSTRUCTOR; + left = decoder.bytesLeft(); + pos = decoder.getParsePosition(); + // + // Payload + if (m_protocolOp == BIND_REQUEST) + { + int version = decoder.parseInt(); + left = decoder.bytesLeft(); + pos = decoder.getParsePosition(); + // + byte[] dn = decoder.parseOctetString(Ber.ASN_OCTET_STR, null); + left = decoder.bytesLeft(); + pos = decoder.getParsePosition(); + // + byte[] authentification = decoder.parseOctetString(Ber.ASN_CONTEXT, null); + left = decoder.bytesLeft(); + pos = decoder.getParsePosition(); + // + log.info("#" + messageID + ": bind - version=" + version + ", dn=" + new String(dn) + + ", auth=" + new String (authentification)); + } + else if (m_protocolOp == UNBIND_REQUEST) + log.info("#" + messageID + ": unbind"); + else + { + log.warning("#" + messageID + ": Unknown Op + " + m_protocolOp); + } + } // decode + + /** + * Get Operation Code + * @return protocolOp + */ + public int getOperation() + { + return m_protocolOp; + } // getOperation + +} // LdapMessage diff --git a/serverRoot/src/main/server/org/compiere/ldap/LdapProcessor.java b/serverRoot/src/main/server/org/compiere/ldap/LdapProcessor.java new file mode 100644 index 0000000000..13a181595a --- /dev/null +++ b/serverRoot/src/main/server/org/compiere/ldap/LdapProcessor.java @@ -0,0 +1,141 @@ +/****************************************************************************** + * 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. + * You may reach us at: ComPiere, Inc. - http://www.adempiere.org/license.html + * 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA or info@adempiere.org + *****************************************************************************/ +package org.compiere.ldap; + +import java.net.*; +import java.sql.*; +import java.util.*; +import java.util.logging.*; +import javax.naming.ldap.*; +import org.compiere.*; +import org.compiere.ldap.*; +import org.compiere.model.*; +import org.compiere.server.*; +import org.compiere.util.*; + +/** + * LDAP Server + * + * @author Jorg Janke + * @version $Id: LdapProcessor.java,v 1.1 2006/10/09 00:23:16 jjanke Exp $ + */ +public class LdapProcessor extends AdempiereServer +{ + /** + * Ldap Processor (Server) + * @param model Ldap Model + */ + public LdapProcessor (LdapProcessorModel model) + { + super (model, 300); + m_model = model; + init(); + } // LdapProcessor + + /** The Concrete Model */ + private LdapProcessorModel m_model = null; + /** Last Summary */ + private StringBuffer m_summary = new StringBuffer(); + /** Client info */ + private MClient m_client = null; + /** Server Socket */ + private ServerSocket m_serverSocket = null; + /** Counter */ + private int m_counter = 0; + + + /** + * Do Work + */ + protected void doWork() + { + // Close Socket + if (m_serverSocket != null) + { + try + { + m_serverSocket.close(); + } + catch (Exception e) + { + } + } + m_counter = 0; + // + m_summary = new StringBuffer(m_model.toString()) + .append(" - "); + // + + try + { + m_serverSocket = new ServerSocket(m_model.getLdapPort()); + log.log(Level.INFO, "Opened Port=" + m_model.getLdapPort()); + while (!isInterrupted()) + { + Socket socket = m_serverSocket.accept(); // waits for connection + log.log(Level.FINE, "Connection on Port=" + m_model.getLdapPort()); + LdapConnectionHandler handler = new LdapConnectionHandler (socket); + handler.start(); + m_counter++; + } + } + catch (Exception e) + { + log.log(Level.WARNING, "Port=" + m_model.getLdapPort(), e); + m_summary.append(e.toString()); + } + + } // doWork + + /** + * Initialize + */ + private void init() + { + try + { + InitialLdapContext lctx = new InitialLdapContext(); + // lctx.setRequestControls(critModCtls); + // lctx.modifyAttributes(name, mods); + Control[] respCtls = lctx.getResponseControls(); + } + catch (Exception e) + { + } + } // + + /** + * Get Server Info + * @return info + */ + public String getServerInfo() + { + return "#" + p_runCount + " - Last=" + m_summary.toString() + + "; Counter=" + m_counter; + } // getServerInfo + + /** + * Test + * @param args + */ + public static void main(String[] args) + { + Adempiere.startup(true); + new LdapProcessor(new LdapProcessorModel(new Properties())).doWork(); + } // main + +} // LdapProcessor + diff --git a/serverRoot/src/main/server/org/compiere/ldap/LdapProcessorModel.java b/serverRoot/src/main/server/org/compiere/ldap/LdapProcessorModel.java new file mode 100644 index 0000000000..b2b04e7a4a --- /dev/null +++ b/serverRoot/src/main/server/org/compiere/ldap/LdapProcessorModel.java @@ -0,0 +1,158 @@ +/****************************************************************************** + * 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. + * You may reach us at: ComPiere, Inc. - http://www.adempiere.org/license.html + * 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA or info@adempiere.org + *****************************************************************************/ +package org.compiere.ldap; + +import java.sql.*; +import java.util.*; +import org.compiere.model.*; + +/** + * Interim LDAP Server Model + * + * @author Jorg Janke + * @version $Id: LdapProcessorModel.java,v 1.1 2006/10/09 00:23:16 jjanke Exp $ + */ +public class LdapProcessorModel implements AdempiereProcessor +{ + /** + * Ldap Processor Model + * @param ctx context + */ + public LdapProcessorModel (Properties ctx) + { + m_ctx = ctx; + } + // Properties + private Properties m_ctx = null; + + private Timestamp m_dateNextRun; + private Timestamp m_dateLastRun; + + + public int getLdapPort() + { + return 389; + } + + + + /** + * String Representation + * @return info + */ + public String toString() + { + StringBuffer sb = new StringBuffer (getName()); + sb.append (";Port=").append (getLdapPort()); + return sb.toString (); + } // toString + + + + + /************************************************************************** + * getAD_Client_ID + * @see org.compiere.model.AdempiereProcessor#getAD_Client_ID() + * @return 0 + */ + public int getAD_Client_ID() + { + return 0; + } + /** + * getName + * @see org.compiere.model.AdempiereProcessor#getName() + * @return name + */ + public String getName() + { + return "Adempiere LDAP Server"; + } + /** + * getDescription + * @see org.compiere.model.AdempiereProcessor#getDescription() + * @return - + */ + public String getDescription() + { + return "-"; + } + /** + * Get Ctx + * @return context + */ + public Properties getCtx() + { + return m_ctx; + } + /** + * GetFrequencyType + * @see org.compiere.model.AdempiereProcessor#getFrequencyType() + * @return min + */ + public String getFrequencyType() + { + return MRequestProcessor.FREQUENCYTYPE_Minute; + } + /** + * getFrequency + * @see org.compiere.model.AdempiereProcessor#getFrequency() + * @return 1 + */ + public int getFrequency() + { + return 1; + } + + /** + * Get Unique Server ID + * @return id + */ + public String getServerID() + { + return "Ldap"; + } + + public Timestamp getDateNextRun(boolean requery) + { + return m_dateNextRun; + } + + public void setDateNextRun(Timestamp dateNextWork) + { + m_dateNextRun = dateNextWork; + } + + public Timestamp getDateLastRun() + { + return m_dateLastRun; + } + + public void setDateLastRun(Timestamp dateLastRun) + { + m_dateLastRun = dateLastRun; + } + + public boolean save() + { + return true; + } + + public AdempiereProcessorLog[] getLogs() + { + return new AdempiereProcessorLog[0]; + } +} diff --git a/serverRoot/src/main/server/org/compiere/ldap/LdapResult.java b/serverRoot/src/main/server/org/compiere/ldap/LdapResult.java new file mode 100644 index 0000000000..7c459ed878 --- /dev/null +++ b/serverRoot/src/main/server/org/compiere/ldap/LdapResult.java @@ -0,0 +1,147 @@ +/****************************************************************************** + * 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. + * You may reach us at: ComPiere, Inc. - http://www.adempiere.org/license.html + * 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA or info@adempiere.org + *****************************************************************************/ +package org.compiere.ldap; + +import java.io.*; +import java.util.logging.*; +import org.compiere.util.*; +import com.sun.jndi.ldap.*; + +/** + * Ldap Wire Response + * + * @author Jorg Janke + * @version $Id: LdapResult.java,v 1.1 2006/10/09 00:23:16 jjanke Exp $ + */ +public class LdapResult +{ + + public LdapResult() + { + super (); + } // LdapResult + + /** + LDAPResult ::= SEQUENCE { + resultCode ENUMERATED { + success (0), + operationsError (1), + protocolError (2), + timeLimitExceeded (3), + sizeLimitExceeded (4), + compareFalse (5), + compareTrue (6), + + authMethodNotSupported (7), + strongAuthRequired (8), + -- 9 reserved -- + referral (10), -- new + adminLimitExceeded (11), -- new + unavailableCriticalExtension (12), -- new + confidentialityRequired (13), -- new + saslBindInProgress (14), -- new + noSuchAttribute (16), + undefinedAttributeType (17), + inappropriateMatching (18), + constraintViolation (19), + attributeOrValueExists (20), + invalidAttributeSyntax (21), + noSuchObject (32), + aliasProblem (33), + invalidDNSyntax (34), + -- 35 reserved for undefined isLeaf -- + aliasDereferencingProblem (36), + -- 37-47 unused -- + inappropriateAuthentication (48), + invalidCredentials (49), + insufficientAccessRights (50), + busy (51), + unavailable (52), + unwillingToPerform (53), + loopDetect (54), + -- 55-63 unused -- + namingViolation (64), + objectClassViolation (65), + notAllowedOnNonLeaf (66), + notAllowedOnRDN (67), + entryAlreadyExists (68), + objectClassModsProhibited (69), + -- 70 reserved for CLDAP -- + affectsMultipleDSAs (71), -- new + -- 72-79 unused -- + other (80) }, + -- 81-90 reserved for APIs -- + matchedDN LDAPDN, + errorMessage LDAPString, + referral [3] Referral OPTIONAL } + **/ + + /** Encoder */ + private BerEncoder m_encoder = new BerEncoder(); + /** Logger */ + private static CLogger log = CLogger.getCLogger (LdapResult.class); + + /** + * Bind Response + * @return reponse + */ + public byte[] bindResponse() + { + try + { +/** + m_encoder.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); + for (int i = 0; i < sortKeys.length; i++) { + ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); + ber.encodeString(sortKeys[i].getAttributeID(), true); // v3 + if ((matchingRule = sortKeys[i].getMatchingRuleID()) != null) { + ber.encodeString(matchingRule, (Ber.ASN_CONTEXT | 0), true); + } + if (! sortKeys[i].isAscending()) { + ber.encodeBoolean(true, (Ber.ASN_CONTEXT | 1)); + } + ber.endSeq(); + } +*/ + // payload + m_encoder.beginSeq(Ber.ASN_APPLICATION | LdapMessage.BIND_RESPONSE); + // Response + m_encoder.encodeInt(0); // success + m_encoder.encodeOctetString("cn=testCN".getBytes(), 0); // matched DN + m_encoder.encodeOctetString("".getBytes(), 0); // error mag + // referral + // sasl + // + m_encoder.endSeq(); + log.info("Success"); + } + catch (Exception e) + { + log.log(Level.SEVERE, "", e); + } + return getResult(); + } // bindResponse + + /** + * Get BER Result as byte array + * @return byte array + */ + public byte[] getResult() + { + return m_encoder.getTrimmedBuf(); + } // getResult + +} // LdapResult diff --git a/serverRoot/src/main/server/org/compiere/server/AcctProcessor.java b/serverRoot/src/main/server/org/compiere/server/AcctProcessor.java new file mode 100644 index 0000000000..865aa2142b --- /dev/null +++ b/serverRoot/src/main/server/org/compiere/server/AcctProcessor.java @@ -0,0 +1,172 @@ +/****************************************************************************** + * 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.server; + +import java.sql.*; +import java.util.logging.*; +import org.compiere.acct.*; +import org.compiere.model.*; +import org.compiere.util.*; + + +/** + * Accounting Processor + * + * @author Jorg Janke + * @version $Id: AcctProcessor.java,v 1.3 2006/07/30 00:53:33 jjanke Exp $ + */ +public class AcctProcessor extends AdempiereServer +{ + /** + * Accounting Processor + * @param model model + */ + public AcctProcessor (MAcctProcessor model) + { + super (model, 30); // 30 seconds delay + m_model = model; + m_client = MClient.get(model.getCtx(), model.getAD_Client_ID()); + } // AcctProcessor + + /** The Concrete Model */ + private MAcctProcessor m_model = null; + /** Last Summary */ + private StringBuffer m_summary = new StringBuffer(); + /** Client onfo */ + private MClient m_client = null; + /** Accounting Schemata */ + private MAcctSchema[] m_ass = null; + + /** + * Work + */ + protected void doWork () + { + m_summary = new StringBuffer(); + // Get Schemata + if (m_model.getC_AcctSchema_ID() == 0) + m_ass = MAcctSchema.getClientAcctSchema(getCtx(), m_model.getAD_Client_ID()); + else // only specific accounting schema + m_ass = new MAcctSchema[] {new MAcctSchema (getCtx(), m_model.getC_AcctSchema_ID(), null)}; + // + postSession(); + MCost.create(m_client); + // + int no = m_model.deleteLog(); + m_summary.append("Logs deleted=").append(no); + // + MAcctProcessorLog pLog = new MAcctProcessorLog(m_model, m_summary.toString()); + pLog.setReference("#" + String.valueOf(p_runCount) + + " - " + TimeUtil.formatElapsed(new Timestamp(p_startWork))); + pLog.save(); + } // doWork + + /** + * Post Session + */ + private void postSession() + { + for (int i = 0; i < Doc.documentsTableID.length; i++) + { + int AD_Table_ID = Doc.documentsTableID[i]; + String TableName = Doc.documentsTableName[i]; + // Post only special documents + if (m_model.getAD_Table_ID() != 0 + && m_model.getAD_Table_ID() != AD_Table_ID) + continue; + // SELECT * FROM table + StringBuffer sql = new StringBuffer ("SELECT * FROM ").append(TableName) + .append(" WHERE AD_Client_ID=?") + .append(" AND Processed='Y' AND Posted='N' AND IsActive='Y'") + .append(" ORDER BY Created"); + // + int count = 0; + int countError = 0; + PreparedStatement pstmt = null; + try + { + pstmt = DB.prepareStatement(sql.toString(), null); + pstmt.setInt(1, m_model.getAD_Client_ID()); + ResultSet rs = pstmt.executeQuery(); + while (!isInterrupted() && rs.next()) + { + count++; + boolean ok = true; + try + { + Doc doc = Doc.get (m_ass, AD_Table_ID, rs, null); + if (doc == null) + { + log.severe(getName() + ": No Doc for " + TableName); + ok = false; + } + else + { + String error = doc.post(false, false); // post no force/repost + ok = error == null; + } + } + catch (Exception e) + { + log.log(Level.SEVERE, getName() + ": " + TableName, e); + ok = false; + } + if (!ok) + countError++; + } + rs.close(); + pstmt.close(); + pstmt = null; + } + catch (Exception e) + { + log.log(Level.SEVERE, sql.toString(), e); + } + if (pstmt != null) + { + try + { + pstmt.close(); + } + catch (Exception e) + { + } + } + // + if (count > 0) + { + m_summary.append(TableName).append("=").append(count); + if (countError > 0) + m_summary.append("(Errors=").append(countError).append(")"); + m_summary.append(" - "); + log.finer(getName() + ": " + m_summary.toString()); + } + else + log.finer(getName() + ": " + TableName + " - no work"); + } + } // postSession + + /** + * Get Server Info + * @return info + */ + public String getServerInfo() + { + return "#" + p_runCount + " - Last=" + m_summary.toString(); + } // getServerInfo + +} // AcctProcessor diff --git a/serverRoot/src/main/server/org/compiere/server/AdempiereServer.java b/serverRoot/src/main/server/org/compiere/server/AdempiereServer.java new file mode 100644 index 0000000000..4dde40ed9c --- /dev/null +++ b/serverRoot/src/main/server/org/compiere/server/AdempiereServer.java @@ -0,0 +1,388 @@ +/****************************************************************************** + * 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.server; + +import java.sql.*; +import java.util.*; +import java.util.logging.*; +import org.compiere.ldap.*; +import org.compiere.model.*; +import org.compiere.util.*; +import org.compiere.wf.*; + +/** + * Adempiere Server Base + * + * @author Jorg Janke + * @version $Id: AdempiereServer.java,v 1.3 2006/10/09 00:23:26 jjanke Exp $ + */ +public abstract class AdempiereServer extends Thread +{ + /** + * Create New Server Thead + * @param model model + * @return server tread or null + */ + public static AdempiereServer create (AdempiereProcessor model) + { + if (model instanceof MRequestProcessor) + return new RequestProcessor ((MRequestProcessor)model); + if (model instanceof MWorkflowProcessor) + return new WorkflowProcessor ((MWorkflowProcessor)model); + if (model instanceof MAcctProcessor) + return new AcctProcessor ((MAcctProcessor)model); + if (model instanceof MAlertProcessor) + return new AlertProcessor ((MAlertProcessor)model); + if (model instanceof MScheduler) + return new Scheduler ((MScheduler)model); + if (model instanceof LdapProcessorModel) + return new LdapProcessor((LdapProcessorModel)model); + // + throw new IllegalArgumentException("Unknown Processor"); + } // create + + + /************************************************************************** + * Server Base Class + * @param model model + * @param initialNap delay time running in sec + */ + protected AdempiereServer (AdempiereProcessor model, int initialNap) + { + super (AdempiereServerGroup.get(), null, model.getName(), 0); + p_model = model; + m_ctx = new Properties(model.getCtx()); + if (p_system == null) + p_system = MSystem.get(m_ctx); + p_client = MClient.get(m_ctx); + Env.setContext(m_ctx, "#AD_Client_ID", p_client.getAD_Client_ID()); + m_initialNap = initialNap; + // log.info(model.getName() + " - " + getThreadGroup()); + } // ServerBase + + /** The Processor Model */ + protected AdempiereProcessor p_model; + /** Initial nap is seconds */ + private int m_initialNap = 0; + + /** Miliseconds to sleep - 10 Min default */ + private long m_sleepMS = 600000; + /** Sleeping */ + private volatile boolean m_sleeping = false; + /** Server start time */ + private long m_start = 0; + /** Number of Work executions */ + protected int p_runCount = 0; + /** Tine start of work */ + protected long p_startWork = 0; + /** Number MS of last Run */ + private long m_runLastMS = 0; + /** Number of MS total */ + private long m_runTotalMS = 0; + /** When to run next */ + private long m_nextWork = 0; + + /** Logger */ + protected CLogger log = CLogger.getCLogger(getClass()); + /** Context */ + private Properties m_ctx = null; + /** System */ + protected static MSystem p_system = null; + /** Client */ + protected MClient p_client = null; + + /** + * Get Server Context + * @return context + */ + public Properties getCtx() + { + return m_ctx; + } // getCtx + + /** + * @return Returns the sleepMS. + */ + public long getSleepMS () + { + return m_sleepMS; + } // getSleepMS + + + /** + * Sleep for set time + * @return true if not interrupted + */ + public boolean sleep() + { + if (isInterrupted()) + { + log.info (getName() + ": interrupted"); + return false; + } + log.fine(getName() + ": sleeping " + TimeUtil.formatElapsed(m_sleepMS)); + m_sleeping = true; + try + { + sleep (m_sleepMS); + } + catch (InterruptedException e) + { + log.info (getName() + ": interrupted"); + m_sleeping = false; + return false; + } + m_sleeping = false; + return true; + } // sleep + + /** + * Run Now + */ + public void runNow() + { + log.info(getName()); + p_startWork = System.currentTimeMillis(); + doWork(); + long now = System.currentTimeMillis(); + // --------------- + + p_runCount++; + m_runLastMS = now - p_startWork; + m_runTotalMS += m_runLastMS; + // + p_model.setDateLastRun(new Timestamp(now)); + p_model.save(); + // + log.fine(getName() + ": " + getStatistics()); + } // runNow + + /************************************************************************** + * Run async + */ + public void run () + { + try + { + log.fine(getName() + ": pre-nap - " + m_initialNap); + sleep (m_initialNap * 1000); + } + catch (InterruptedException e) + { + log.log(Level.SEVERE, getName() + ": pre-nap interrupted", e); + return; + } + + m_start = System.currentTimeMillis(); + while (true) + { + if (m_nextWork == 0) + { + Timestamp dateNextRun = getDateNextRun(true); + if (dateNextRun != null) + m_nextWork = dateNextRun.getTime(); + } + long now = System.currentTimeMillis(); + if (m_nextWork > now) + { + m_sleepMS = m_nextWork - now; + if (!sleep ()) + break; + } + if (isInterrupted()) + { + log.info (getName() + ": interrupted"); + break; + } + + // --------------- + p_startWork = System.currentTimeMillis(); + doWork(); + now = System.currentTimeMillis(); + // --------------- + + p_runCount++; + m_runLastMS = now - p_startWork; + m_runTotalMS += m_runLastMS; + // + m_sleepMS = calculateSleep(); + m_nextWork = now + m_sleepMS; + // + p_model.setDateLastRun(new Timestamp(now)); + p_model.setDateNextRun(new Timestamp(m_nextWork)); + p_model.save(); + // + log.fine(getName() + ": " + getStatistics()); + if (!sleep()) + break; + } + m_start = 0; + } // run + + /** + * Get Run Statistics + * @return Statistic info + */ + public String getStatistics() + { + return "Run #" + p_runCount + + " - Last=" + TimeUtil.formatElapsed(m_runLastMS) + + " - Total=" + TimeUtil.formatElapsed(m_runTotalMS) + + " - Next " + TimeUtil.formatElapsed(m_nextWork - System.currentTimeMillis()); + } // getStatistics + + /** + * Do the actual Work + */ + protected abstract void doWork(); + + /** + * Get Server Info + * @return info + */ + public abstract String getServerInfo(); + + /** + * Get Unique ID + * @return Unique ID + */ + public String getServerID() + { + return p_model.getServerID(); + } // getServerID + + /** + * Get the date Next run + * @param requery requery database + * @return date next run + */ + public Timestamp getDateNextRun (boolean requery) + { + return p_model.getDateNextRun(requery); + } // getDateNextRun + + /** + * Get the date Last run + * @return date lext run + */ + public Timestamp getDateLastRun () + { + return p_model.getDateLastRun(); + } // getDateLastRun + + /** + * Get Description + * @return Description + */ + public String getDescription() + { + return p_model.getDescription(); + } // getDescription + + /** + * Get Model + * @return Model + */ + public AdempiereProcessor getModel() + { + return p_model; + } // getModel + + /** + * Calculate Sleep ms + * @return miliseconds + */ + private long calculateSleep () + { + String frequencyType = p_model.getFrequencyType(); + int frequency = p_model.getFrequency(); + if (frequency < 1) + frequency = 1; + // + long typeSec = 600; // 10 minutes + if (frequencyType == null) + typeSec = 300; // 5 minutes + else if (X_R_RequestProcessor.FREQUENCYTYPE_Minute.equals(frequencyType)) + typeSec = 60; + else if (X_R_RequestProcessor.FREQUENCYTYPE_Hour.equals(frequencyType)) + typeSec = 3600; + else if (X_R_RequestProcessor.FREQUENCYTYPE_Day.equals(frequencyType)) + typeSec = 86400; + // + return typeSec * 1000 * frequency; // ms + } // calculateSleep + + /** + * Is Sleeping + * @return sleeping + */ + public boolean isSleeping() + { + return m_sleeping; + } // isSleeping + + /** + * String Representation + * @return info + */ + public String toString () + { + StringBuffer sb = new StringBuffer (getName()) + .append (",Prio=").append(getPriority()) + .append (",").append (getThreadGroup()) + .append (",Alive=").append(isAlive()) + .append (",Sleeping=").append(m_sleeping) + .append (",Last=").append(getDateLastRun()); + if (m_sleeping) + sb.append (",Next=").append(getDateNextRun(false)); + return sb.toString (); + } // toString + + /** + * Get Seconds Alive + * @return seconds alive + */ + public int getSecondsAlive() + { + if (m_start == 0) + return 0; + long now = System.currentTimeMillis(); + long ms = (now-m_start) / 1000; + return (int)ms; + } // getSecondsAlive + + /** + * Get Start Time + * @return start time + */ + public Timestamp getStartTime() + { + if (m_start == 0) + return null; + return new Timestamp (m_start); + } // getStartTime + + /** + * Get Processor Logs + * @return logs + */ + public AdempiereProcessorLog[] getLogs() + { + return p_model.getLogs(); + } // getLogs + +} // AdempiereServer diff --git a/serverRoot/src/main/server/org/compiere/server/AdempiereServerGroup.java b/serverRoot/src/main/server/org/compiere/server/AdempiereServerGroup.java new file mode 100644 index 0000000000..d2a14d6358 --- /dev/null +++ b/serverRoot/src/main/server/org/compiere/server/AdempiereServerGroup.java @@ -0,0 +1,90 @@ +/****************************************************************************** + * 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.server; + +import org.compiere.util.*; + +/** + * Adempiere Server Group + * + * @author Jorg Janke + * @version $Id: AdempiereServerGroup.java,v 1.2 2006/07/30 00:53:33 jjanke Exp $ + */ +public class AdempiereServerGroup extends ThreadGroup +{ + /** + * Get Adempiere Server Group + * @return Server Group + */ + public static AdempiereServerGroup get() + { + if (s_group == null || s_group.isDestroyed()) + s_group = new AdempiereServerGroup(); + return s_group; + } // get + + /** Group */ + private static AdempiereServerGroup s_group = null; + + /** + * AdempiereServerGroup + */ + private AdempiereServerGroup () + { + super ("AdempiereServers"); + setDaemon(true); + setMaxPriority(Thread.MAX_PRIORITY); + log.info(getName() + " - Parent=" + getParent()); + } // AdempiereServerGroup + + /** Logger */ + protected CLogger log = CLogger.getCLogger(getClass()); + + /** + * Uncaught Exception + * @param t thread + * @param e exception + */ + public void uncaughtException (Thread t, Throwable e) + { + log.info ("uncaughtException = " + e.toString()); + super.uncaughtException (t, e); + } // uncaughtException + + /** + * String Representation + * @return name + */ + public String toString () + { + return getName(); + } // toString + + /** + * Dump Info + */ + public void dump () + { + log.fine(getName() + (isDestroyed() ? " (destroyed)" : "")); + log.fine("- Parent=" + getParent()); + Thread[] list = new Thread[activeCount()]; + log.fine("- Count=" + enumerate(list, true)); + for (int i = 0; i < list.length; i++) + log.fine("-- " + list[i]); + } // dump + +} // AdempiereServerGroup diff --git a/serverRoot/src/main/server/org/compiere/server/AdempiereServerMgr.java b/serverRoot/src/main/server/org/compiere/server/AdempiereServerMgr.java new file mode 100644 index 0000000000..5285c1cdd6 --- /dev/null +++ b/serverRoot/src/main/server/org/compiere/server/AdempiereServerMgr.java @@ -0,0 +1,532 @@ +/****************************************************************************** + * 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.server; + +import java.sql.*; +import java.util.*; +import java.util.logging.*; +import org.compiere.*; +import org.compiere.ldap.*; +import org.compiere.model.*; +import org.compiere.util.*; +import org.compiere.wf.*; + +/** + * Adempiere Server Manager + * + * @author Jorg Janke + * @version $Id: AdempiereServerMgr.java,v 1.4 2006/10/09 00:23:26 jjanke Exp $ + */ +public class AdempiereServerMgr +{ + /** + * Get Adempiere Server Manager + * @return mgr + */ + public static AdempiereServerMgr get() + { + if (m_serverMgr == null) + { + // for faster subsequent calls + m_serverMgr = new AdempiereServerMgr(); + m_serverMgr.startServers(); + m_serverMgr.log.info(m_serverMgr.toString()); + } + return m_serverMgr; + } // get + + /** Singleton */ + private static AdempiereServerMgr m_serverMgr = null; + /** Logger */ + protected CLogger log = CLogger.getCLogger(getClass()); + + /************************************************************************** + * Adempiere Server Manager + */ + private AdempiereServerMgr () + { + super(); + startEnvironment(); + // m_serverMgr.startServers(); + } // AdempiereServerMgr + + /** The Servers */ + private ArrayList m_servers = new ArrayList(); + /** Context */ + private Properties m_ctx = Env.getCtx(); + /** Start */ + private Timestamp m_start = new Timestamp(System.currentTimeMillis()); + + /** + * Start Environment + * @return true if started + */ + private boolean startEnvironment() + { + Adempiere.startup(false); + log.info(""); + + // Set Session + MSession session = MSession.get(getCtx(), true); + session.setWebStoreSession(false); + session.setWebSession("Server"); + session.save(); + // + return true; + } // startEnvironment + + /** + * Start Environment + * @return true if started + */ + private boolean startServers() + { + log.info(""); + int noServers = 0; + // Accounting + MAcctProcessor[] acctModels = MAcctProcessor.getActive(m_ctx); + for (int i = 0; i < acctModels.length; i++) + { + MAcctProcessor pModel = acctModels[i]; + AdempiereServer server = AdempiereServer.create(pModel); + server.start(); + server.setPriority(Thread.NORM_PRIORITY-2); + m_servers.add(server); + } + // Request + MRequestProcessor[] requestModels = MRequestProcessor.getActive(m_ctx); + for (int i = 0; i < requestModels.length; i++) + { + MRequestProcessor pModel = requestModels[i]; + AdempiereServer server = AdempiereServer.create(pModel); + server.start(); + server.setPriority(Thread.NORM_PRIORITY-2); + m_servers.add(server); + } + // Workflow + MWorkflowProcessor[] workflowModels = MWorkflowProcessor.getActive(m_ctx); + for (int i = 0; i < workflowModels.length; i++) + { + MWorkflowProcessor pModel = workflowModels[i]; + AdempiereServer server = AdempiereServer.create(pModel); + server.start(); + server.setPriority(Thread.NORM_PRIORITY-2); + m_servers.add(server); + } + // Alert + MAlertProcessor[] alertModels = MAlertProcessor.getActive(m_ctx); + for (int i = 0; i < alertModels.length; i++) + { + MAlertProcessor pModel = alertModels[i]; + AdempiereServer server = AdempiereServer.create(pModel); + server.start(); + server.setPriority(Thread.NORM_PRIORITY-2); + m_servers.add(server); + } + // Scheduler + MScheduler[] schedulerModels = MScheduler.getActive(m_ctx); + for (int i = 0; i < schedulerModels.length; i++) + { + MScheduler pModel = schedulerModels[i]; + AdempiereServer server = AdempiereServer.create(pModel); + server.start(); + server.setPriority(Thread.NORM_PRIORITY-2); + m_servers.add(server); + } + // LDAP + LdapProcessorModel lp = new LdapProcessorModel(m_ctx); + AdempiereServer server = AdempiereServer.create(lp); + server.start(); + server.setPriority(Thread.NORM_PRIORITY-2); + m_servers.add(server); + + + log.fine("#" + noServers); + return startAll(); + } // startEnvironment + + /** + * Get Server Context + * @return ctx + */ + public Properties getCtx() + { + return m_ctx; + } // getCtx + + /** + * Start all servers + * @return true if started + */ + public boolean startAll() + { + log.info (""); + AdempiereServer[] servers = getInActive(); + for (int i = 0; i < servers.length; i++) + { + AdempiereServer server = servers[i]; + try + { + if (server.isAlive()) + continue; + // Wait until dead + if (server.isInterrupted()) + { + int maxWait = 10; // 10 iterations = 1 sec + while (server.isAlive()) + { + if (maxWait-- == 0) + { + log.severe ("Wait timeout for interruped " + server); + break; + } + try + { + Thread.sleep(100); // 1/10 sec + } + catch (InterruptedException e) + { + log.log(Level.SEVERE, "While sleeping", e); + } + } + } + // Do start + if (!server.isAlive()) + { + // replace + server = AdempiereServer.create (server.getModel()); + if (server == null) + m_servers.remove(i); + else + m_servers.set(i, server); + server.start(); + server.setPriority(Thread.NORM_PRIORITY-2); + } + } + catch (Exception e) + { + log.log(Level.SEVERE, "Server: " + server, e); + } + } // for all servers + + // Final Check + int noRunning = 0; + int noStopped = 0; + for (int i = 0; i < servers.length; i++) + { + AdempiereServer server = servers[i]; + try + { + if (server.isAlive()) + { + log.info("Alive: " + server); + noRunning++; + } + else + { + log.warning("Dead: " + server); + noStopped++; + } + } + catch (Exception e) + { + log.log(Level.SEVERE, "(checking) - " + server, e); + noStopped++; + } + } + log.fine("Running=" + noRunning + ", Stopped=" + noStopped); + AdempiereServerGroup.get().dump(); + return noStopped == 0; + } // startAll + + /** + * Start Server if not started yet + * @param serverID server ID + * @return true if started + */ + public boolean start (String serverID) + { + AdempiereServer server = getServer(serverID); + if (server == null) + return false; + if (server.isAlive()) + return true; + + try + { + // replace + int index = m_servers.indexOf(server); + server = AdempiereServer.create (server.getModel()); + if (server == null) + m_servers.remove(index); + else + m_servers.set(index, server); + server.start(); + server.setPriority(Thread.NORM_PRIORITY-2); + Thread.yield(); + } + catch (Exception e) + { + log.log(Level.SEVERE, "Server=" + serverID, e); + return false; + } + log.info(server.toString()); + AdempiereServerGroup.get().dump(); + if (server == null) + return false; + return server.isAlive(); + } // startIt + + /** + * Stop all Servers + * @return true if stopped + */ + public boolean stopAll() + { + log.info (""); + AdempiereServer[] servers = getActive(); + // Interrupt + for (int i = 0; i < servers.length; i++) + { + AdempiereServer server = servers[i]; + try + { + if (server.isAlive() && !server.isInterrupted()) + { + server.setPriority(Thread.MAX_PRIORITY-1); + server.interrupt(); + } + } + catch (Exception e) + { + log.log(Level.SEVERE, "(interrupting) - " + server, e); + } + } // for all servers + Thread.yield(); + + // Wait for death + for (int i = 0; i < servers.length; i++) + { + AdempiereServer server = servers[i]; + try + { + int maxWait = 10; // 10 iterations = 1 sec + while (server.isAlive()) + { + if (maxWait-- == 0) + { + log.severe ("Wait timeout for interruped " + server); + break; + } + Thread.sleep(100); // 1/10 + } + } + catch (Exception e) + { + log.log(Level.SEVERE, "(waiting) - " + server, e); + } + } // for all servers + + // Final Check + int noRunning = 0; + int noStopped = 0; + for (int i = 0; i < servers.length; i++) + { + AdempiereServer server = servers[i]; + try + { + if (server.isAlive()) + { + log.warning ("Alive: " + server); + noRunning++; + } + else + { + log.info ("Stopped: " + server); + noStopped++; + } + } + catch (Exception e) + { + log.log(Level.SEVERE, "(checking) - " + server, e); + noRunning++; + } + } + log.fine("Running=" + noRunning + ", Stopped=" + noStopped); + AdempiereServerGroup.get().dump(); + return noRunning == 0; + } // stopAll + + /** + * Stop Server if not stopped + * @param serverID server ID + * @return true if interrupted + */ + public boolean stop (String serverID) + { + AdempiereServer server = getServer(serverID); + if (server == null) + return false; + if (!server.isAlive()) + return true; + + try + { + server.interrupt(); + Thread.sleep(10); // 1/100 sec + } + catch (Exception e) + { + log.log(Level.SEVERE, "stop", e); + return false; + } + log.info(server.toString()); + AdempiereServerGroup.get().dump(); + return !server.isAlive(); + } // stop + + + /** + * Destroy + */ + public void destroy () + { + log.info (""); + stopAll(); + m_servers.clear(); + } // destroy + + /** + * Get Active Servers + * @return array of active servers + */ + protected AdempiereServer[] getActive() + { + ArrayList list = new ArrayList(); + for (int i = 0; i < m_servers.size(); i++) + { + AdempiereServer server = (AdempiereServer)m_servers.get(i); + if (server != null && server.isAlive() && !server.isInterrupted()) + list.add (server); + } + AdempiereServer[] retValue = new AdempiereServer[list.size ()]; + list.toArray (retValue); + return retValue; + } // getActive + + /** + * Get InActive Servers + * @return array of inactive servers + */ + protected AdempiereServer[] getInActive() + { + ArrayList list = new ArrayList(); + for (int i = 0; i < m_servers.size(); i++) + { + AdempiereServer server = (AdempiereServer)m_servers.get(i); + if (server != null && (!server.isAlive() || !server.isInterrupted())) + list.add (server); + } + AdempiereServer[] retValue = new AdempiereServer[list.size()]; + list.toArray (retValue); + return retValue; + } // getInActive + + /** + * Get all Servers + * @return array of servers + */ + public AdempiereServer[] getAll() + { + AdempiereServer[] retValue = new AdempiereServer[m_servers.size()]; + m_servers.toArray (retValue); + return retValue; + } // getAll + + /** + * Get Server with ID + * @param serverID server id + * @return server or null + */ + public AdempiereServer getServer (String serverID) + { + if (serverID == null) + return null; + for (int i = 0; i < m_servers.size(); i++) + { + AdempiereServer server = (AdempiereServer)m_servers.get(i); + if (serverID.equals(server.getServerID())) + return server; + } + return null; + } // getServer + + /** + * String Representation + * @return info + */ + public String toString () + { + StringBuffer sb = new StringBuffer ("AdempiereServerMgr["); + sb.append("Servers=").append(m_servers.size()) + .append(",ContextSize=").append(m_ctx.size()) + .append(",Started=").append(m_start) + .append ("]"); + return sb.toString (); + } // toString + + /** + * Get Description + * @return description + */ + public String getDescription() + { + return "$Revision: 1.4 $"; + } // getDescription + + /** + * Get Number Servers + * @return no of servers + */ + public String getServerCount() + { + int noRunning = 0; + int noStopped = 0; + for (int i = 0; i < m_servers.size(); i++) + { + AdempiereServer server = (AdempiereServer)m_servers.get(i); + if (server.isAlive()) + noRunning++; + else + noStopped++; + } + String info = String.valueOf(m_servers.size()) + + " - Running=" + noRunning + + " - Stopped=" + noStopped; + return info; + } // getServerCount + + /** + * Get start date + * @return start date + */ + public Timestamp getStartTime() + { + return m_start; + } // getStartTime + +} // AdempiereServerMgr diff --git a/serverRoot/src/main/server/org/compiere/server/AlertProcessor.java b/serverRoot/src/main/server/org/compiere/server/AlertProcessor.java new file mode 100644 index 0000000000..1607a67d0a --- /dev/null +++ b/serverRoot/src/main/server/org/compiere/server/AlertProcessor.java @@ -0,0 +1,323 @@ +/****************************************************************************** + * 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.server; + +import java.sql.*; +import java.util.logging.*; +import org.compiere.*; +import org.compiere.model.*; +import org.compiere.util.*; + + +/** + * Alert Processor + * + * @author Jorg Janke + * @version $Id: AlertProcessor.java,v 1.4 2006/07/30 00:53:33 jjanke Exp $ + */ +public class AlertProcessor extends AdempiereServer +{ + /** + * Alert Processor + * @param model model + */ + public AlertProcessor (MAlertProcessor model) + { + super (model, 180); // 3 monute delay + m_model = model; + m_client = MClient.get(model.getCtx(), model.getAD_Client_ID()); + } // AlertProcessor + + /** The Concrete Model */ + private MAlertProcessor m_model = null; + /** Last Summary */ + private StringBuffer m_summary = new StringBuffer(); + /** Last Error Msg */ + private StringBuffer m_errors = new StringBuffer(); + /** Client onfo */ + private MClient m_client = null; + + /** + * Work + */ + protected void doWork () + { + m_summary = new StringBuffer(); + m_errors = new StringBuffer(); + // + int count = 0; + int countError = 0; + MAlert[] alerts = m_model.getAlerts(false); + for (int i = 0; i < alerts.length; i++) + { + if (!processAlert(alerts[i])) + countError++; + count++; + } + // + String summary = "Total=" + count; + if (countError > 0) + summary += ", Not processed=" + countError; + summary += " - "; + m_summary.insert(0, summary); + // + int no = m_model.deleteLog(); + m_summary.append("Logs deleted=").append(no); + // + MAlertProcessorLog pLog = new MAlertProcessorLog(m_model, m_summary.toString()); + pLog.setReference("#" + String.valueOf(p_runCount) + + " - " + TimeUtil.formatElapsed(new Timestamp(p_startWork))); + pLog.setTextMsg(m_errors.toString()); + pLog.save(); + } // doWork + + /** + * Process Alert + * @param alert alert + * @return true if processed + */ + private boolean processAlert (MAlert alert) + { + if (!alert.isValid()) + return false; + log.info("" + alert); + + StringBuffer message = new StringBuffer(alert.getAlertMessage()) + .append(Env.NL); + // + boolean valid = true; + boolean processed = false; + MAlertRule[] rules = alert.getRules(false); + for (int i = 0; i < rules.length; i++) + { + if (i > 0) + message.append(Env.NL).append("================================").append(Env.NL); + String trxName = null; // assume r/o + + MAlertRule rule = rules[i]; + if (!rule.isValid()) + continue; + log.fine("" + rule); + + // Pre + String sql = rule.getPreProcessing(); + if (sql != null && sql.length() > 0) + { + int no = DB.executeUpdate(sql, false, trxName); + if (no == -1) + { + ValueNamePair error = CLogger.retrieveError(); + rule.setErrorMsg("Pre=" + error.getName()); + m_errors.append("Pre=" + error.getName()); + rule.setIsValid(false); + rule.save(); + valid = false; + break; + } + } // Pre + + // The processing + sql = rule.getSql(); + if (alert.isEnforceRoleSecurity() + || alert.isEnforceClientSecurity()) + { + int AD_Role_ID = alert.getFirstAD_Role_ID(); + if (AD_Role_ID == -1) + AD_Role_ID = alert.getFirstUserAD_Role_ID(); + if (AD_Role_ID != -1) + { + MRole role = MRole.get(getCtx(), AD_Role_ID); + sql = role.addAccessSQL(sql, null, true, false); + } + } + + try + { + String text = listSqlSelect(sql, trxName); + if (text != null && text.length() > 0) + { + message.append(text); + processed = true; + } + } + catch (Exception e) + { + rule.setErrorMsg("Select=" + e.getLocalizedMessage()); + m_errors.append("Select=" + e.getLocalizedMessage()); + rule.setIsValid(false); + rule.save(); + valid = false; + break; + } + + // Post + sql = rule.getPostProcessing(); + if (sql != null && sql.length() > 0) + { + int no = DB.executeUpdate(sql, false, trxName); + if (no == -1) + { + ValueNamePair error = CLogger.retrieveError(); + rule.setErrorMsg("Post=" + error.getName()); + m_errors.append("Post=" + error.getName()); + rule.setIsValid(false); + rule.save(); + valid = false; + break; + } + } // Post + + /** Trx */ + if (trxName != null) + { + Trx trx = Trx.get(trxName, false); + if (trx != null) + { + trx.commit(); + trx.close(); + } + } + } // for all rules + + // Update header if error + if (!valid) + { + alert.setIsValid(false); + alert.save(); + return false; + } + + // Nothing to report + if (!processed) + { + m_summary.append(alert.getName()).append("=No Result - "); + return true; + } + + // Send Message + int countMail = 0; + MAlertRecipient[] recipients = alert.getRecipients(false); + for (int i = 0; i < recipients.length; i++) + { + MAlertRecipient recipient = recipients[i]; + if (recipient.getAD_User_ID() >= 0) // System == 0 + if (m_client.sendEMail(recipient.getAD_User_ID(), + alert.getAlertSubject(), message.toString(), null)) + countMail++; + if (recipient.getAD_Role_ID() >= 0) // SystemAdministrator == 0 + { + MUserRoles[] urs = MUserRoles.getOfRole(getCtx(), recipient.getAD_Role_ID()); + for (int j = 0; j < urs.length; j++) + { + MUserRoles ur = urs[j]; + if (!ur.isActive()) + continue; + if (m_client.sendEMail (ur.getAD_User_ID(), + alert.getAlertSubject(), message.toString(), null)) + countMail++; + } + } + } + + m_summary.append(alert.getName()).append(" (EMails=").append(countMail).append(") - "); + return valid; + } // processAlert + + /** + * List Sql Select + * @param sql sql select + * @param trxName transaction + * @return list of rows & values + * @throws Exception + */ + private String listSqlSelect (String sql, String trxName) throws Exception + { + StringBuffer result = new StringBuffer(); + PreparedStatement pstmt = null; + Exception error = null; + try + { + pstmt = DB.prepareStatement (sql, trxName); + ResultSet rs = pstmt.executeQuery (); + ResultSetMetaData meta = rs.getMetaData(); + while (rs.next ()) + { + result.append("------------------").append(Env.NL); + for (int col = 1; col <= meta.getColumnCount(); col++) + { + result.append(meta.getColumnLabel(col)).append(" = "); + result.append(rs.getString(col)); + result.append(Env.NL); + } // for all columns + } + if (result.length() == 0) + log.fine("No rows selected"); + rs.close (); + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + log.log(Level.SEVERE, sql, e); + error = e; + } + try + { + if (pstmt != null) + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + pstmt = null; + } + + // Error occured + if (error != null) + throw new Exception ("(" + sql + ") " + Env.NL + + error.getLocalizedMessage()); + + return result.toString(); + } // listSqlSelect + + + + /** + * Get Server Info + * @return info + */ + public String getServerInfo() + { + return "#" + p_runCount + " - Last=" + m_summary.toString(); + } // getServerInfo + + + /*************************************************************************** + * Test + * @param args ignored + */ + public static void main (String[] args) + { + Adempiere.startup(true); + MAlertProcessor model = new MAlertProcessor (Env.getCtx(), 100, null); + AlertProcessor ap = new AlertProcessor(model); + ap.start(); + + + } // main + +} // AlertProcessor diff --git a/serverRoot/src/main/server/org/compiere/server/EMailProcessor.java b/serverRoot/src/main/server/org/compiere/server/EMailProcessor.java new file mode 100644 index 0000000000..07fe99de59 --- /dev/null +++ b/serverRoot/src/main/server/org/compiere/server/EMailProcessor.java @@ -0,0 +1,700 @@ +/****************************************************************************** + * 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.server; + +import java.io.*; +import java.util.*; +import java.util.logging.*; +import javax.mail.*; +import org.compiere.*; +import org.compiere.model.*; +import org.compiere.util.*; + +/** + * Request Mail Processor + * + * @author Jorg Janke + * @version $Id: EMailProcessor.java,v 1.3 2006/07/30 00:53:33 jjanke Exp $ + */ +public class EMailProcessor +{ + /** + * EMail Processor + * @param client client + */ + public EMailProcessor (MClient client) + { + this (client.getSMTPHost(), client.getRequestUser(), client.getRequestUserPW()); + } // EMailProcessor + + /** + * EMail Processor + * @param host host + * @param user user id + * @param password password + */ + public EMailProcessor (String host, String user, String password) + { + m_host = host; + m_user = user; + m_pass = password; + } // EMail + + /** EMail Host Parameter */ + private String m_host = null; + /** EMail User Parameter */ + private String m_user = null; + /** Password Parameter */ + private String m_pass = null; + + /** Session */ + private Session m_session = null; + /** Store */ + private Store m_store = null; + + + /** Logger */ + protected CLogger log = CLogger.getCLogger(getClass()); + + /** Process Error */ + private static final int ERROR = 0; + /** Process Request */ + private static final int REQUEST = 1; + /** Process Workflow */ + private static final int WORKFLOW = 2; + + /** Process Delivery Confirm */ + private static final int DELIVERY = 9; + + /** + * Process Messages in InBox + * @return number of mails processed + */ + public int processMessages() + { + int processed = 0; + try + { + getSession(); + getStore(); + processed = processInBox(); + } + catch (Exception e) + { + log.log(Level.SEVERE, "processInBox", e); + } + // Cleanup + try + { + if (m_store.isConnected()) + m_store.close(); + } + catch (Exception e) + { + } + m_store = null; + return processed; + } // processMessages + + + /************************************************************************** + * Get Session + * @return Session + * @throws Exception + */ + private Session getSession() throws Exception + { + if (m_session != null) + return m_session; + + // Session + Properties props = System.getProperties(); + props.put("mail.store.protocol", "smtp"); + props.put("mail.transport.protocol", "smtp"); + props.put("mail.host", m_host); + props.put("mail.smtp.auth","true"); + EMailAuthenticator auth = new EMailAuthenticator (m_user, m_pass); + // + m_session = Session.getDefaultInstance(props, auth); + m_session.setDebug(CLogMgt.isLevelFinest()); + log.fine("getSession - " + m_session); + return m_session; + } // getSession + + + /** + * Get Store + * @return Store + * @throws Exception + */ + private Store getStore() throws Exception + { + if (m_store != null) + return m_store; + if (getSession() == null) + throw new IllegalStateException("No Session"); + + // Get IMAP Store + m_store = m_session.getStore("imap"); + // Connect + m_store.connect(); + // + log.fine("getStore - " + m_store); + return m_store; + } // getStore + + + /** + * Process InBox + * @return number of processed + * @throws Exception + */ + private int processInBox() throws Exception + { + // Folder + Folder folder; + folder = m_store.getDefaultFolder(); + if (folder == null) + throw new IllegalStateException("No default folder"); + // Open Inbox + Folder inbox = folder.getFolder("INBOX"); + if (!inbox.exists()) + throw new IllegalStateException("No Inbox"); + inbox.open(Folder.READ_WRITE); + log.fine("processInBox - " + inbox.getName() + + "; Messages Total=" + inbox.getMessageCount() + + "; New=" + inbox.getNewMessageCount()); + + // Open Request + Folder requestFolder = folder.getFolder("CRequest"); + if (!requestFolder.exists() && !requestFolder.create(Folder.HOLDS_MESSAGES)) + throw new IllegalStateException("Cannot create Request Folder"); + requestFolder.open(Folder.READ_WRITE); + + // Open Workflow + Folder workflowFolder = folder.getFolder("CWorkflow"); + if (!workflowFolder.exists() && !workflowFolder.create(Folder.HOLDS_MESSAGES)) + throw new IllegalStateException("Cannot create Workflow Folder"); + workflowFolder.open(Folder.READ_WRITE); + + // Open Error + Folder errorFolder = folder.getFolder("AdempiereError"); + if (!errorFolder.exists() && !errorFolder.create(Folder.HOLDS_MESSAGES)) + throw new IllegalStateException("Cannot create Error Folder"); + errorFolder.open(Folder.READ_WRITE); + + // Messages + Message[] messages = inbox.getMessages(); + /** + FetchProfile fp = new FetchProfile(); + fp.add(FetchProfile.Item.ENVELOPE); + fp.add(FetchProfile.Item.FLAGS); + fp.add("X-Mailer"); + inbox.fetch(messages, fp); + **/ + // + int noProcessed = 0; + int noError = 0; + for (int i = 0; i < messages.length; i++) +// for (int i = messages.length-1; i >= 0; i--) // newest first + { + Message msg = messages[i]; + int result = processMessage (msg); + if (result == REQUEST) + { + msg.setFlag(Flags.Flag.SEEN, true); + msg.setFlag(Flags.Flag.ANSWERED, true); + // Copy to processed + requestFolder.appendMessages(new Message[]{msg}); + } + else if (result == WORKFLOW) + { + msg.setFlag(Flags.Flag.SEEN, true); + msg.setFlag(Flags.Flag.ANSWERED, true); + // Copy to processed + workflowFolder.appendMessages(new Message[]{msg}); + } + else if (result == DELIVERY) + { + msg.setFlag(Flags.Flag.SEEN, true); + msg.setFlag(Flags.Flag.ANSWERED, true); + } + else // error + { + errorFolder.appendMessages(new Message[]{msg}); + noError++; + } + // Delete in InBox +// msg.setFlag(Flags.Flag.DELETED, true); +// Message[] deleted = inbox.expunge(); + // + noProcessed++; + } + + log.info("processInBox - Total=" + noProcessed + " - Errors=" + noError); + // Fini + errorFolder.close(false); + requestFolder.close(false); + workflowFolder.close(false); + // + inbox.close(true); + return noProcessed; + } // processInBox + + + /** + * Process Message + * @param msg message + * @return Type of Message + * @throws Exception + */ + private int processMessage (Message msg) throws Exception + { + dumpEnvelope (msg); + dumpBody (msg); + printOut (":::::::::::::::"); + printOut (getSubject(msg)); + printOut (":::::::::::::::"); + printOut (getMessage(msg)); + printOut (":::::::::::::::"); + String delivery = getDeliveryReport(msg); + printOut (delivery); + printOut (":::::::::::::::"); + + // if (delivery != null) + // return DELIVERY; + + // Unknown + return ERROR; + } // processMessage + + /** + * Get Subject + * @param msg message + * @return subject or "" + */ + private String getSubject (Message msg) + { + try + { + String str = msg.getSubject(); + if (str != null) + return str.trim(); + } + catch (MessagingException e) + { + log.log(Level.SEVERE, "getSubject", e); + } + return ""; + } // getSubject + + /** + * Get Message + * @param msg Message + * @return message or "" + */ + private String getMessage (Part msg) + { + StringBuffer sb = new StringBuffer(); + try + { + // Text + if (msg.isMimeType("text/plain")) + { + sb.append(msg.getContent()); + } + // Other Text (e.g. html/xml) + else if (msg.isMimeType("text/*")) + { + sb.append(msg.getContent()); + } + // Nested + else if (msg.isMimeType("message/rfc822")) + { + sb.append(msg.getContent()); + } + // Multi Part Alternative + else if (msg.isMimeType("multipart/alternative")) + { + String plainText = null; + String otherStuff = null; + // + Multipart mp = (Multipart)msg.getContent(); + int count = mp.getCount(); + for (int i = 0; i < count; i++) + { + Part part = mp.getBodyPart(i); + Object content = part.getContent(); + if (content == null || content.toString().trim().length() == 0) + continue; + if (part.isMimeType("text/plain")) + plainText = content.toString(); + else + otherStuff = content.toString(); + } + if (plainText != null) + sb.append(plainText); + else if (otherStuff != null) + sb.append(otherStuff); + } + // Multi Part + else if (msg.isMimeType("multipart/*")) + { + Multipart mp = (Multipart)msg.getContent(); + int count = mp.getCount(); + for (int i = 0; i < count; i++) + { + String str = getMessage(mp.getBodyPart(i)); + if (str.length() > 0) + { + if (sb.length() > 0) + sb.append("\n-----\n"); + sb.append(str); + } + } + } + else + { + /* + * If we actually want to see the data, and it's not a + * MIME type we know, fetch it and check its Java type. + */ + Object o = msg.getContent(); + if (o instanceof String) + { + sb.append(o); + } + } + } + catch (Exception e) + { + log.log(Level.SEVERE, "getMessage", e); + } + return sb.toString().trim(); + } // getMessage + + /** + * Get Delivery Report + * @param msg message + * @return delivery info or null + */ + private String getDeliveryReport (Part msg) + { + try + { + if (msg.isMimeType("multipart/report")) + { + String deliveryMessage = null; + String otherStuff = null; + // + Multipart mp = (Multipart)msg.getContent(); + int count = mp.getCount(); + for (int i = 0; i < count; i++) + { + Part part = mp.getBodyPart(i); + Object content = part.getContent(); + if (content == null) + continue; + if (part.isMimeType("message/*")) + deliveryMessage = getDeliveredReportDetail (part); + else + otherStuff = content.toString().trim(); + } + if (deliveryMessage != null) + return deliveryMessage; + return otherStuff; + } + else if (msg.isMimeType("message/*")) + { + return getDeliveredReportDetail (msg); + } + } + catch (Exception e) + { + log.log(Level.SEVERE, "getDeliveryReport", e); + } + // Nothing + return null; + } // getDeliveryReport + + /** + * Get Delivered Report Detail + * @param part Mime Type message/* + * @return info or null + * @throws Exception + */ + private String getDeliveredReportDetail (Part part) throws Exception + { + Object content = part.getContent(); + if (content == null) + return null; + + String deliveryMessage = null; + if (content instanceof InputStream) + { + StringBuffer sb = new StringBuffer(); + InputStream is = (InputStream)content; + int c; + while ((c = is.read()) != -1) + sb.append((char)c); + deliveryMessage = sb.toString().trim(); + } + else + deliveryMessage = content.toString().trim(); + // + if (deliveryMessage == null) + return null; + + // Final-Recipient: RFC822; jjanke@adempiere.org + int index = deliveryMessage.indexOf("Final-Recipient:"); + if (index != -1) + { + String finalRecipient = deliveryMessage.substring(index); + int atIndex = finalRecipient.indexOf("@"); + if (atIndex != -1) + { + index = finalRecipient.lastIndexOf(' ', atIndex); + if (index != -1) + finalRecipient = finalRecipient.substring(index+1); + atIndex = finalRecipient.indexOf("@"); + if (atIndex != -1) + index = finalRecipient.indexOf(' ', atIndex); + if (index != -1) + finalRecipient = finalRecipient.substring(0, index); + index = finalRecipient.indexOf('\n'); + if (index != -1) + finalRecipient = finalRecipient.substring(0, index); + return finalRecipient.trim(); + } + } + return deliveryMessage; + } // getDeliveredReportDetail + + + /************************************************************************** + * Print Envelope + * @param m message + * @throws Exception + */ + private void dumpEnvelope(Message m) throws Exception + { + printOut("-----------------------------------------------------------------"); + Address[] a; + // FROM + if ((a = m.getFrom()) != null) + { + for (int j = 0; j < a.length; j++) + printOut("FROM: " + a[j].toString()); + } + + // TO + if ((a = m.getRecipients(Message.RecipientType.TO)) != null) + { + for (int j = 0; j < a.length; j++) + printOut("TO: " + a[j].toString()); + } + + // SUBJECT + printOut("SUBJECT: " + m.getSubject()); + + // DATE + java.util.Date d = m.getSentDate(); + printOut("SendDate: " + (d != null ? d.toString() : "UNKNOWN")); + + // FLAGS + Flags flags = m.getFlags(); + StringBuffer sb = new StringBuffer(); + Flags.Flag[] sf = flags.getSystemFlags(); // get the system flags + + boolean first = true; + for (int i = 0; i < sf.length; i++) + { + String s; + Flags.Flag f = sf[i]; + if (f == Flags.Flag.ANSWERED) + s = "\\Answered"; + else if (f == Flags.Flag.DELETED) + s = "\\Deleted"; + else if (f == Flags.Flag.DRAFT) + s = "\\Draft"; + else if (f == Flags.Flag.FLAGGED) + s = "\\Flagged"; + else if (f == Flags.Flag.RECENT) + s = "\\Recent"; + else if (f == Flags.Flag.SEEN) + s = "\\Seen"; + else + continue; // skip it + if (first) + first = false; + else + sb.append(' '); + sb.append(s); + } + + String[] uf = flags.getUserFlags(); // get the user flag strings + for (int i = 0; i < uf.length; i++) + { + if (first) + first = false; + else + sb.append(' '); + sb.append(uf[i]); + } + printOut("FLAGS: " + sb.toString()); + + // X-MAILER + String[] hdrs = m.getHeader("X-Mailer"); + if (hdrs != null) + { + StringBuffer sb1 = new StringBuffer("X-Mailer: "); + for (int i = 0; i < hdrs.length; i++) + sb1.append(hdrs[i]).append(" "); + printOut(sb1.toString()); + } + else + printOut("X-Mailer NOT available"); + + // Message ID + hdrs = m.getHeader("Message-ID"); + if (hdrs != null) + { + StringBuffer sb1 = new StringBuffer("Message-ID: "); + for (int i = 0; i < hdrs.length; i++) + sb1.append(hdrs[i]).append(" "); + printOut(sb1.toString()); + } + else + printOut("Message-ID NOT available"); + + // All + printOut("ALL HEADERs:"); + Enumeration en = m.getAllHeaders(); + while (en.hasMoreElements()) + { + Header hdr = (Header)en.nextElement(); + printOut (" " + hdr.getName() + " = " + hdr.getValue()); + } + + + printOut("-----------------------------------------------------------------"); + } // printEnvelope + + /** + * Print Body + * @param p + * @throws Exception + */ + private void dumpBody (Part p) throws Exception + { + // http://www.iana.org/assignments/media-types/ + printOut("================================================================="); + printOut("CONTENT-TYPE: " + p.getContentType()); + /** + Enumeration en = p.getAllHeaders(); + while (en.hasMoreElements()) + { + Header hdr = (Header)en.nextElement(); + printOut (" " + hdr.getName() + " = " + hdr.getValue()); + } + printOut("= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = ="); + /** **/ + + /** + * Using isMimeType to determine the content type avoids + * fetching the actual content data until we need it. + */ + if (p.isMimeType("text/plain")) + { + printOut("Plain text ---------------------------"); + System.out.println((String)p.getContent()); + } + else if (p.getContentType().toUpperCase().startsWith("TEXT")) + { + printOut("Other text ---------------------------"); + System.out.println((String)p.getContent()); + } + else if (p.isMimeType("multipart/*")) + { + printOut("Multipart ---------------------------"); + Multipart mp = (Multipart)p.getContent(); + int count = mp.getCount(); + for (int i = 0; i < count; i++) + dumpBody(mp.getBodyPart(i)); + } + else if (p.isMimeType("message/rfc822")) + { + printOut("Nested ---------------------------"); + dumpBody((Part)p.getContent()); + } + else + { + /* + * If we actually want to see the data, and it's not a + * MIME type we know, fetch it and check its Java type. + */ + Object o = p.getContent(); + if (o instanceof String) + { + printOut("This is a string ---------------------------"); + System.out.println((String)o); + } + else if (o instanceof InputStream) + { + printOut("This is just an input stream ---------------------------"); + InputStream is = (InputStream)o; + int c; + while ((c = is.read()) != -1) + System.out.write(c); + } + else + { + printOut("This is an unknown type ---------------------------"); + printOut(o.toString()); + } + } + printOut("================================================================="); + } // printBody + + /** + * Print + * @param s string + */ + private static void printOut(String s) + { + // System.out.print(indentStr.substring(0, level * 2)); + System.out.println(s); + } + + + /************************************************************************** + * Main Test + * @param args ignored + */ + public static void main (String[] args) + { + Adempiere.startupEnvironment(true); + EMailProcessor m = new EMailProcessor("admin", "test", "testadempiere"); + m.processMessages(); + + // System.out.println(EMailServer.send("main", "jjanke@adempiere.org", "jjanke@yahoo.com", "test1", "test1 message")); + // System.out.println(EMailServer.send("main", "administrator@adempiere.org", "jjanke@yahoo.com", "test2", "test2 message")); + // System.out.println(EMailServer.send("main", "jjanke@adempiere.org", "jjanke@yahoo.com", "test3", "test3 message")); + + } // main + +} // EMailProcessor diff --git a/serverRoot/src/main/server/org/compiere/server/RequestProcessor.java b/serverRoot/src/main/server/org/compiere/server/RequestProcessor.java new file mode 100644 index 0000000000..15135e5852 --- /dev/null +++ b/serverRoot/src/main/server/org/compiere/server/RequestProcessor.java @@ -0,0 +1,655 @@ +/****************************************************************************** + * 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.server; + +import java.sql.*; +import java.util.*; +import java.util.logging.*; + +import org.compiere.model.*; +import org.compiere.util.*; + +/** + * Request Processor + * + * @author Jorg Janke + * @version $Id: RequestProcessor.java,v 1.3 2006/07/30 00:53:33 jjanke Exp $ + */ +public class RequestProcessor extends AdempiereServer +{ + /** + * RequestProcessor + * @param model model + */ + public RequestProcessor (MRequestProcessor model) + { + super (model, 60); // 1 minute delay + m_model = model; + m_client = MClient.get(model.getCtx(), model.getAD_Client_ID()); + } // RequestProcessor + + /** The Concrete Model */ + private MRequestProcessor m_model = null; + /** Last Summary */ + private StringBuffer m_summary = new StringBuffer(); + /** Client onfo */ + private MClient m_client = null; + + /************************************************************************** + * Do the actual Work + */ + protected void doWork() + { + m_summary = new StringBuffer(); + // + processEMail(); + findSalesRep (); + processRequests (); + processStatus(); + processECR(); + // + int no = m_model.deleteLog(); + m_summary.append("Logs deleted=").append(no); + // + MRequestProcessorLog pLog = new MRequestProcessorLog(m_model, m_summary.toString()); + pLog.setReference("#" + String.valueOf(p_runCount) + + " - " + TimeUtil.formatElapsed(new Timestamp(p_startWork))); + pLog.save(); + } // doWork + + + /************************************************************************** + * Process requests. + * Scheduled - are they due? + */ + private void processRequests () + { + /** + * Due Requests + */ + String sql = "SELECT * FROM R_Request " + + "WHERE DueType='" + MRequest.DUETYPE_Scheduled + "' AND Processed='N'" + + " AND DateNextAction > SysDate" + + " AND AD_Client_ID=?"; + if (m_model.getR_RequestType_ID() != 0) + sql += " AND R_RequestType_ID=?"; + PreparedStatement pstmt = null; + int count = 0; + int countEMails = 0; + try + { + pstmt = DB.prepareStatement (sql, null); + pstmt.setInt (1, m_model.getAD_Client_ID()); + if (m_model.getR_RequestType_ID() != 0) + pstmt.setInt(2, m_model.getR_RequestType_ID()); + ResultSet rs = pstmt.executeQuery (); + while (rs.next ()) + { + MRequest request = new MRequest (getCtx(), rs, null); + request.setDueType(); + if (request.isDue()) + { + if (request.getRequestType().isEMailWhenDue()) + { + if (sendEmail (request, "RequestDue")) + { + request.setDateLastAlert(); + countEMails++; + } + } + request.save(); + count++; + } + } + rs.close (); + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + log.log(Level.SEVERE, sql, e); + } + m_summary.append("New Due #").append(count); + if (countEMails > 0) + m_summary.append(" (").append(countEMails).append(" EMail)"); + m_summary.append (" - "); + + /** + * Overdue Requests. + * Due Requests - are they overdue? + */ + sql = "SELECT * FROM R_Request r " + + "WHERE r.DueType='" + MRequest.DUETYPE_Due + "' AND r.Processed='N'" + + " AND AD_Client_ID=?" + + " AND EXISTS (SELECT * FROM R_RequestType rt " + + "WHERE r.R_RequestType_ID=rt.R_RequestType_ID" + + " AND (r.DateNextAction+rt.DueDateTolerance) > SysDate)"; + if (m_model.getR_RequestType_ID() != 0) + sql += " AND r.R_RequestType_ID=?"; + count = 0; + countEMails = 0; + try + { + pstmt = DB.prepareStatement (sql, null); + pstmt.setInt (1, m_model.getAD_Client_ID()); + if (m_model.getR_RequestType_ID() != 0) + pstmt.setInt(2, m_model.getR_RequestType_ID()); + ResultSet rs = pstmt.executeQuery (); + while (rs.next ()) + { + MRequest request = new MRequest (getCtx(), rs, null); + request.setDueType(); + if (request.isOverdue()) + { + if (request.getRequestType().isEMailWhenOverdue() + && !TimeUtil.isSameDay(request.getDateLastAlert(), null)) + { + if (sendEmail (request, "RequestDue")) + { + request.setDateLastAlert(); + countEMails++; + } + } + request.save(); + count++; + } + } + rs.close (); + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + log.log(Level.SEVERE, sql, e); + } + m_summary.append("New Overdue #").append(count); + if (countEMails > 0) + m_summary.append(" (").append(countEMails).append(" EMail)"); + m_summary.append (" - "); + + /** + * Send (over)due alerts + */ + if (m_model.getOverdueAlertDays() > 0) + { + sql = "SELECT * FROM R_Request " + + "WHERE Processed='N'" + + " AND AD_Client_ID=?" + + " AND (DateNextAction+" + m_model.getOverdueAlertDays() + ") > SysDate" + + " AND (DateLastAlert IS NULL"; + if (m_model.getRemindDays() > 0) + sql += " OR (DateLastAlert+" + m_model.getRemindDays() + + ") > SysDate"; + sql += ")"; + if (m_model.getR_RequestType_ID() != 0) + sql += " AND R_RequestType_ID=?"; + count = 0; + countEMails = 0; + try + { + pstmt = DB.prepareStatement(sql, null); + pstmt.setInt(1, m_model.getAD_Client_ID()); + if (m_model.getR_RequestType_ID() != 0) + pstmt.setInt(2, m_model.getR_RequestType_ID()); + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) + { + MRequest request = new MRequest (getCtx(), rs, null); + request.setDueType(); + if (request.getRequestType().isEMailWhenOverdue() + && (request.getDateLastAlert() == null + || !TimeUtil.isSameDay(request.getDateLastAlert(), null))) + { + if (sendEmail (request, "RequestAlert")) + { + request.setDateLastAlert(); + countEMails++; + } + } + request.save(); + count++; + } + rs.close(); + pstmt.close(); + } + catch (SQLException e) + { + log.log(Level.SEVERE, sql, e); + } + m_summary.append("Alerts #").append(count); + if (countEMails > 0) + m_summary.append(" (").append(countEMails).append(" EMail)"); + m_summary.append (" - "); + } // Overdue + + /** + * Escalate + */ + if (m_model.getOverdueAssignDays() > 0) + { + sql = "SELECT * FROM R_Request " + + "WHERE Processed='N'" + + " AND AD_Client_ID=?" + + " AND IsEscalated='N'" + + " AND (DateNextAction+" + m_model.getOverdueAssignDays() + + ") > SysDate"; + if (m_model.getR_RequestType_ID() != 0) + sql += " AND R_RequestType_ID=?"; + count = 0; + countEMails = 0; + try + { + pstmt = DB.prepareStatement(sql, null); + pstmt.setInt(1, m_model.getAD_Client_ID()); + if (m_model.getR_RequestType_ID() != 0) + pstmt.setInt(2, m_model.getR_RequestType_ID()); + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) + { + MRequest request = new MRequest (getCtx(), rs, null); + if (escalate(request)) + count++; + } + rs.close(); + pstmt.close(); + } + catch (SQLException e) + { + log.log(Level.SEVERE, sql, e); + } + m_summary.append("Escalated #").append(count).append(" - "); + } // Esacalate + + /** + * Send inactivity alerts + */ + if (m_model.getInactivityAlertDays() > 0) + { + sql = "SELECT * FROM R_Request " + + "WHERE Processed='N'" + + " AND AD_Client_ID=?" + + " AND (Updated+" + m_model.getInactivityAlertDays() + ") > SysDate" + + " AND (DateLastAlert IS NULL"; + if (m_model.getRemindDays() > 0) + sql += " OR (DateLastAlert+" + m_model.getRemindDays() + + ") < SysDate"; + sql += ")"; + if (m_model.getR_RequestType_ID() != 0) + sql += " AND R_RequestType_ID=?"; + count = 0; + countEMails = 0; + try + { + pstmt = DB.prepareStatement(sql, null); + pstmt.setInt(1, m_model.getAD_Client_ID()); + if (m_model.getR_RequestType_ID() != 0) + pstmt.setInt(2, m_model.getR_RequestType_ID()); + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) + { + MRequest request = new MRequest (getCtx(), rs, null); + request.setDueType(); + if (request.getDateLastAlert() == null + || !TimeUtil.isSameDay(request.getDateLastAlert(), null)) + { + if (sendEmail (request, "RequestInactive")) + { + request.setDateLastAlert(); + countEMails++; + } + request.save(); + count++; + } + } + rs.close(); + pstmt.close(); + } + catch (SQLException e) + { + log.log(Level.SEVERE, sql, e); + } + m_summary.append("Inactivity #").append(count); + if (countEMails > 0) + m_summary.append(" (").append(countEMails).append(" EMail)"); + m_summary.append (" - "); + } // Inactivity + + // + try + { + if (pstmt != null) + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + pstmt = null; + } + } // processRequests + + /** + * Send Alert EMail + * @param request request + * @param AD_Message message + * @return true if sent + */ + private boolean sendEmail (MRequest request, String AD_Message) + { + // Alert: Request {0} overdue + String subject = Msg.getMsg(m_client.getAD_Language(), AD_Message, + new String[] {request.getDocumentNo()}); + return m_client.sendEMail(request.getSalesRep_ID(), + subject, request.getSummary(), request.createPDF()); + } // sendAlert + + /** + * Escalate + * @param request request + * @return true if saved + */ + private boolean escalate (MRequest request) + { + // Get Supervisor + MUser supervisor = request.getSalesRep(); // self + int supervisor_ID = request.getSalesRep().getSupervisor_ID(); + if (supervisor_ID == 0 && m_model.getSupervisor_ID() != 0) + supervisor_ID = m_model.getSupervisor_ID(); + if (supervisor_ID != 0 && supervisor_ID != request.getAD_User_ID()) + supervisor = MUser.get(getCtx(), supervisor_ID); + + // Escalated: Request {0} to {1} + String subject = Msg.getMsg(m_client.getAD_Language(), "RequestEscalate", + new String[] {request.getDocumentNo(), supervisor.getName()}); + String to = request.getSalesRep().getEMail(); + if (to == null || to.length() == 0) + log.warning("SalesRep has no EMail - " + request.getSalesRep()); + else + m_client.sendEMail(request.getSalesRep_ID(), + subject, request.getSummary(), request.createPDF()); + + // Not the same - send mail to supervisor + if (request.getSalesRep_ID() != supervisor.getAD_User_ID()) + { + to = supervisor.getEMail(); + if (to == null || to.length() == 0) + log.warning("Supervisor has no EMail - " + supervisor); + else + m_client.sendEMail(supervisor.getAD_User_ID(), + subject, request.getSummary(), request.createPDF()); + } + + // ---------------- + request.setDueType(); + request.setIsEscalated(true); + request.setResult(subject); + return request.save(); + } // escalate + + + /************************************************************************** + * Process Request Status + */ + private void processStatus() + { + int count = 0; + // Requests with status with after timeout + String sql = "SELECT * FROM R_Request r WHERE EXISTS (" + + "SELECT * FROM R_Status s " + + "WHERE r.R_Status_ID=s.R_Status_ID" + + " AND s.TimeoutDays > 0 AND s.Next_Status_ID > 0" + + " AND r.Updated+s.TimeoutDays < SysDate" + + ") " + + "ORDER BY R_Status_ID"; + PreparedStatement pstmt = null; + MStatus status = null; + MStatus next = null; + try + { + pstmt = DB.prepareStatement (sql, null); + ResultSet rs = pstmt.executeQuery (); + while (rs.next ()) + { + MRequest r = new MRequest(getCtx(), rs, null); + // Get/Check Status + if (status == null || status.getR_Status_ID() != r.getR_Status_ID()) + status = MStatus.get(getCtx(), r.getR_Status_ID()); + if (status.getTimeoutDays() <= 0 + || status.getNext_Status_ID() == 0) + continue; + // Next Status + if (next == null || next.getR_Status_ID() != status.getNext_Status_ID()) + next = MStatus.get(getCtx(), status.getNext_Status_ID()); + // + String result = Msg.getMsg(getCtx(), "RequestStatusTimeout") + + ": " + status.getName() + " -> " + next.getName(); + r.setResult(result); + r.setR_Status_ID(status.getNext_Status_ID()); + if (r.save()) + count++; + } + rs.close (); + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + log.log (Level.SEVERE, sql, e); + } + try + { + if (pstmt != null) + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + pstmt = null; + } + + m_summary.append("Status Timeout #").append(count) + .append(" - "); + } // processStatus + + /** + * Create ECR + */ + private void processECR() + { + // Get Requests with Request Type-AutoChangeRequest and Group with info + String sql = "SELECT * FROM R_Request r " + + "WHERE M_ChangeRequest_ID IS NULL" + + " AND EXISTS (" + + "SELECT * FROM R_RequestType rt " + + "WHERE rt.R_RequestType_ID=r.R_RequestType_ID" + + " AND rt.IsAutoChangeRequest='Y')" + + "AND EXISTS (" + + "SELECT * FROM R_Group g " + + "WHERE g.R_Group_ID=r.R_Group_ID" + + " AND (g.M_BOM_ID IS NOT NULL OR g.M_ChangeNotice_ID IS NOT NULL) )"; + // + int count = 0; + int failure = 0; + // + PreparedStatement pstmt = null; + try + { + pstmt = DB.prepareStatement (sql, null); + ResultSet rs = pstmt.executeQuery (); + while (rs.next ()) + { + MRequest r = new MRequest (getCtx(), rs, null); + MGroup rg = MGroup.get(getCtx(), r.getR_Group_ID()); + MChangeRequest ecr = new MChangeRequest (r, rg); + if (r.save()) + { + r.setM_ChangeRequest_ID(ecr.getM_ChangeRequest_ID()); + if (r.save()) + count++; + else + failure++; + } + else + failure++; + } + rs.close (); + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + log.log (Level.SEVERE, sql, e); + } + try + { + if (pstmt != null) + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + pstmt = null; + } + m_summary.append("Auto Change Request #").append(count); + if (failure > 0) + m_summary.append("(fail=").append(failure).append(")"); + m_summary.append(" - "); + } // processECR + + + /************************************************************************** + * Create Reauest / Updates from EMail + */ + private void processEMail () + { + // m_summary.append("Mail #").append(count) + // .append(" - "); + } // processEMail + + + /************************************************************************** + * Allocate Sales Rep + */ + private void findSalesRep () + { + int changed = 0; + int notFound = 0; + Properties ctx = new Properties(); + // + String sql = "SELECT * FROM R_Request " + + "WHERE AD_Client_ID=?" + + " AND SalesRep_ID=0 AND Processed='N'"; + if (m_model.getR_RequestType_ID() != 0) + sql += " AND R_RequestType_ID=?"; + PreparedStatement pstmt = null; + try + { + pstmt = DB.prepareStatement(sql, null); + pstmt.setInt(1, m_model.getAD_Client_ID()); + if (m_model.getR_RequestType_ID() != 0) + pstmt.setInt(2, m_model.getR_RequestType_ID()); + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) + { + MRequest request = new MRequest (ctx, rs, null); + if (request.getSalesRep_ID() != 0) + continue; + int SalesRep_ID = findSalesRep(request); + if (SalesRep_ID != 0) + { + request.setSalesRep_ID(SalesRep_ID); + request.save(); + changed++; + } + else + notFound++; + } + rs.close(); + pstmt.close(); + pstmt = null; + } + catch (SQLException ex) + { + log.log(Level.SEVERE, sql, ex); + } + try + { + if (pstmt != null) + pstmt.close(); + } + catch (SQLException ex1) + { + } + pstmt = null; + // + if (changed == 0 && notFound == 0) + m_summary.append("No unallocated Requests"); + else + m_summary.append("Allocated SalesRep=").append(changed); + if (notFound > 0) + m_summary.append(",Not=").append(notFound); + m_summary.append(" - "); + } // findSalesRep + + /** + * Find SalesRep/User based on Request Type and Question. + * @param request request + * @return SalesRep_ID user + */ + private int findSalesRep (MRequest request) + { + String QText = request.getSummary(); + if (QText == null) + QText = ""; + else + QText = QText.toUpperCase(); + // + MRequestProcessorRoute[] routes = m_model.getRoutes(false); + for (int i = 0; i < routes.length; i++) + { + MRequestProcessorRoute route = routes[i]; + + // Match first on Request Type + if (request.getR_RequestType_ID() == route.getR_RequestType_ID() + && route.getR_RequestType_ID() != 0) + return route.getAD_User_ID(); + + // Match on element of keyword + String keyword = route.getKeyword(); + if (keyword != null) + { + StringTokenizer st = new StringTokenizer(keyword.toUpperCase(), " ,;\t\n\r\f"); + while (st.hasMoreElements()) + { + if (QText.indexOf(st.nextToken()) != -1) + return route.getAD_User_ID(); + } + } + } // for all routes + + return m_model.getSupervisor_ID(); + } // findSalesRep + + /** + * Get Server Info + * @return info + */ + public String getServerInfo() + { + return "#" + p_runCount + " - Last=" + m_summary.toString(); + } // getServerInfo + +} // RequestProcessor diff --git a/serverRoot/src/main/server/org/compiere/server/Scheduler.java b/serverRoot/src/main/server/org/compiere/server/Scheduler.java new file mode 100644 index 0000000000..ac38d4154c --- /dev/null +++ b/serverRoot/src/main/server/org/compiere/server/Scheduler.java @@ -0,0 +1,290 @@ +/****************************************************************************** + * 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.server; + +import java.io.*; +import java.math.*; +import java.sql.*; +import java.util.logging.*; +import org.compiere.model.*; +import org.compiere.print.*; +import org.compiere.process.*; +import org.compiere.util.*; + + +/** + * Scheduler + * + * @author Jorg Janke + * @version $Id: Scheduler.java,v 1.5 2006/07/30 00:53:33 jjanke Exp $ + */ +public class Scheduler extends AdempiereServer +{ + /** + * Scheduler + * @param model model + */ + public Scheduler (MScheduler model) + { + super (model, 240); // nap + m_model = model; + // m_client = MClient.get(model.getCtx(), model.getAD_Client_ID()); + } // Scheduler + + /** The Concrete Model */ + private MScheduler m_model = null; + /** Last Summary */ + private StringBuffer m_summary = new StringBuffer(); + /** Transaction */ + private Trx m_trx = null; + + /** + * Work + */ + protected void doWork () + { + m_summary = new StringBuffer(m_model.toString()) + .append(" - "); + // + MProcess process = m_model.getProcess(); + try + { + m_trx = Trx.get(Trx.createTrxName("Scheduler"), true); + if (process.isReport()) + m_summary.append(runReport(process)); + else + m_summary.append(runProcess(process)); + m_trx.commit(); + } + catch (Exception e) + { + if (m_trx != null) + m_trx.rollback(); + log.log(Level.WARNING, process.toString(), e); + m_summary.append(e.toString()); + } + if (m_trx != null) + m_trx.close(); + // + int no = m_model.deleteLog(); + m_summary.append("Logs deleted=").append(no); + // + MSchedulerLog pLog = new MSchedulerLog(m_model, m_summary.toString()); + pLog.setReference("#" + String.valueOf(p_runCount) + + " - " + TimeUtil.formatElapsed(new Timestamp(p_startWork))); + pLog.save(); + } // doWork + + /** + * Run Report + * @param process + * @return summary + * @throws Exception + */ + private String runReport(MProcess process) throws Exception + { log.info(process.toString()); + if (!process.isReport() || process.getAD_ReportView_ID() == 0) + return "Not a Report AD_Process_ID=" + process.getAD_Process_ID() + + " - " + process.getName(); + // Process + int AD_Table_ID = 0; + int Record_ID = 0; + // + MPInstance pInstance = new MPInstance(process, Record_ID); + fillParameter(pInstance); + // + ProcessInfo pi = new ProcessInfo (process.getName(), process.getAD_Process_ID(), + AD_Table_ID, Record_ID); + pi.setAD_User_ID(m_model.getUpdatedBy()); + pi.setAD_Client_ID(m_model.getAD_Client_ID()); + pi.setAD_PInstance_ID(pInstance.getAD_PInstance_ID()); + if (!process.processIt(pi, m_trx) && pi.getClassName() != null) + return "Process failed: (" + pi.getClassName() + ") " + pi.getSummary(); + + // Report + ReportEngine re = ReportEngine.get(getCtx(), pi); + if (re == null) + return "Cannot create Report AD_Process_ID=" + process.getAD_Process_ID() + + " - " + process.getName(); + File report = re.getPDF(); + // Notice + int AD_Message_ID = 884; // HARDCODED SchedulerResult + Integer[] userIDs = m_model.getRecipientAD_User_IDs(); + for (int i = 0; i < userIDs.length; i++) + { + MNote note = new MNote(getCtx(), + AD_Message_ID, userIDs[i].intValue(), m_trx.getTrxName()); + note.setClientOrg(m_model.getAD_Client_ID(), m_model.getAD_Org_ID()); + note.setTextMsg(m_model.getName()); + note.setDescription(m_model.getDescription()); + note.setRecord(AD_Table_ID, Record_ID); + note.save(); + // Attachment + MAttachment attachment = new MAttachment (getCtx(), + X_AD_Note.Table_ID, note.getAD_Note_ID(), m_trx.getTrxName()); + attachment.setClientOrg(m_model.getAD_Client_ID(), m_model.getAD_Org_ID()); + attachment.addEntry(report); + attachment.setTextMsg(m_model.getName()); + attachment.save(); + } + // + return pi.getSummary(); + } // runReport + + /** + * Run Process + * @param process process + * @return summary + * @throws Exception + */ + private String runProcess(MProcess process) throws Exception + { + log.info(process.toString()); + // Process (see also MWFActivity.performWork + int AD_Table_ID = 0; + int Record_ID = 0; + // + MPInstance pInstance = new MPInstance(process, Record_ID); + fillParameter(pInstance); + // + ProcessInfo pi = new ProcessInfo (process.getName(), process.getAD_Process_ID(), + AD_Table_ID, Record_ID); + pi.setAD_User_ID(m_model.getUpdatedBy()); + pi.setAD_Client_ID(m_model.getAD_Client_ID()); + pi.setAD_PInstance_ID(pInstance.getAD_PInstance_ID()); + process.processIt(pi, m_trx); + return pi.getSummary(); + } // runProcess + + /** + * Fill Parameter + * @param pInstance process instance + */ + private void fillParameter(MPInstance pInstance) + { + MSchedulerPara[] sParams = m_model.getParameters (false); + MPInstancePara[] iParams = pInstance.getParameters(); + for (int pi = 0; pi < iParams.length; pi++) + { + MPInstancePara iPara = iParams[pi]; + for (int np = 0; np < sParams.length; np++) + { + MSchedulerPara sPara = sParams[np]; + if (iPara.getParameterName().equals(sPara.getColumnName())) + { + String variable = sPara.getParameterDefault(); + log.fine(sPara.getColumnName() + " = " + variable); + // Value - Constant/Variable + Object value = variable; + if (variable == null + || (variable != null && variable.length() == 0)) + value = null; + else if (variable.indexOf("@") != -1) // we have a variable + { + // Strip + int index = variable.indexOf("@"); + String columnName = variable.substring(index+1); + index = columnName.indexOf("@"); + if (index == -1) + { + log.warning(sPara.getColumnName() + + " - cannot evaluate=" + variable); + break; + } + columnName = columnName.substring(0, index); + // try Env + String env = Env.getContext(getCtx(), columnName); + if (env.length() == 0) + { + log.warning(sPara.getColumnName() + + " - not in environment =" + columnName + + "(" + variable + ")"); + break; + } + else + value = env; + } // @variable@ + + // No Value + if (value == null) + { + log.fine(sPara.getColumnName() + " - empty"); + break; + } + + // Convert to Type + try + { + if (DisplayType.isNumeric(sPara.getDisplayType()) + || DisplayType.isID(sPara.getDisplayType())) + { + BigDecimal bd = null; + if (value instanceof BigDecimal) + bd = (BigDecimal)value; + else if (value instanceof Integer) + bd = new BigDecimal (((Integer)value).intValue()); + else + bd = new BigDecimal (value.toString()); + iPara.setP_Number(bd); + log.fine(sPara.getColumnName() + + " = " + variable + " (=" + bd + "=)"); + } + else if (DisplayType.isDate(sPara.getDisplayType())) + { + Timestamp ts = null; + if (value instanceof Timestamp) + ts = (Timestamp)value; + else + ts = Timestamp.valueOf(value.toString()); + iPara.setP_Date(ts); + log.fine(sPara.getColumnName() + + " = " + variable + " (=" + ts + "=)"); + } + else + { + iPara.setP_String(value.toString()); + log.fine(sPara.getColumnName() + + " = " + variable + + " (=" + value + "=) " + value.getClass().getName()); + } + if (!iPara.save()) + log.warning("Not Saved - " + sPara.getColumnName()); + } + catch (Exception e) + { + log.warning(sPara.getColumnName() + + " = " + variable + " (" + value + + ") " + value.getClass().getName() + + " - " + e.getLocalizedMessage()); + } + break; + } // parameter match + } // scheduler parameter loop + } // instance parameter loop + } // fillParameter + + + /** + * Get Server Info + * @return info + */ + public String getServerInfo() + { + return "#" + p_runCount + " - Last=" + m_summary.toString(); + } // getServerInfo + +} // Scheduler diff --git a/serverRoot/src/main/server/org/compiere/server/WorkflowProcessor.java b/serverRoot/src/main/server/org/compiere/server/WorkflowProcessor.java new file mode 100644 index 0000000000..120bd0c0be --- /dev/null +++ b/serverRoot/src/main/server/org/compiere/server/WorkflowProcessor.java @@ -0,0 +1,485 @@ +/****************************************************************************** + * 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.server; + +import java.sql.*; +import java.util.*; +import java.io.*; + +import java.util.logging.*; +import org.compiere.util.*; +import org.compiere.wf.*; +import org.compiere.model.*; +import org.compiere.process.*; + + +/** + * Workflow Processor + * + * @author Jorg Janke + * @version $Id: WorkflowProcessor.java,v 1.4 2006/07/30 00:53:33 jjanke Exp $ + */ +public class WorkflowProcessor extends AdempiereServer +{ + /** + * WorkflowProcessor + * @param model model + */ + public WorkflowProcessor (MWorkflowProcessor model) + { + super (model, 120); // 2 minute dalay + m_model = model; + m_client = MClient.get(model.getCtx(), model.getAD_Client_ID()); + } // WorkflowProcessor + + /** The Concrete Model */ + private MWorkflowProcessor m_model = null; + /** Last Summary */ + private StringBuffer m_summary = new StringBuffer(); + /** Client onfo */ + private MClient m_client = null; + + /** + * Work + */ + protected void doWork () + { + m_summary = new StringBuffer(); + // + wakeup(); + dynamicPriority(); + sendAlerts(); + // + int no = m_model.deleteLog(); + m_summary.append("Logs deleted=").append(no); + // + MWorkflowProcessorLog pLog = new MWorkflowProcessorLog(m_model, m_summary.toString()); + pLog.setReference("#" + String.valueOf(p_runCount) + + " - " + TimeUtil.formatElapsed(new Timestamp(p_startWork))); + pLog.save(); + } // doWork + + /** + * Continue Workflow After Sleep + */ + private void wakeup() + { + String sql = "SELECT * " + + "FROM AD_WF_Activity a " + + "WHERE Processed='N' AND WFState='OS'" // suspended + + " AND EndWaitTime > SysDate" + + " AND AD_Client_ID=?" + + " AND EXISTS (SELECT * FROM AD_Workflow wf " + + " INNER JOIN AD_WF_Node wfn ON (wf.AD_Workflow_ID=wfn.AD_Workflow_ID) " + + "WHERE a.AD_WF_Node_ID=wfn.AD_WF_Node_ID" + + " AND wfn.Action='Z'" // sleeping + + " AND wf.AD_WorkflowProcessor_ID IS NULL OR wf.AD_WorkflowProcessor_ID=?)"; + PreparedStatement pstmt = null; + int count = 0; + int countEMails = 0; + try + { + pstmt = DB.prepareStatement (sql, null); + pstmt.setInt (1, m_model.getAD_Client_ID()); + pstmt.setInt (2, m_model.getAD_WorkflowProcessor_ID()); + ResultSet rs = pstmt.executeQuery (); + while (rs.next ()) + { + MWFActivity activity = new MWFActivity (getCtx(), rs, null); + activity.setWFState (StateEngine.STATE_Completed); + // saves and calls MWFProcess.checkActivities(); + count++; + } + rs.close (); + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + log.log(Level.SEVERE, "wakeup", e); + } + m_summary.append("Wakeup #").append(count).append (" - "); + } // wakeup + + /** + * Set/Increase Priority dynamically + */ + private void dynamicPriority() + { + // suspened activities with dynamic priority node + String sql = "SELECT * " + + "FROM AD_WF_Activity a " + + "WHERE Processed='N' AND WFState='OS'" // suspended + + " AND EXISTS (SELECT * FROM AD_Workflow wf" + + " INNER JOIN AD_WF_Node wfn ON (wf.AD_Workflow_ID=wfn.AD_Workflow_ID) " + + "WHERE a.AD_WF_Node_ID=wfn.AD_WF_Node_ID AND wf.AD_WorkflowProcessor_ID=?" + + " AND wfn.DynPriorityUnit IS NOT NULL AND wfn.DynPriorityChange IS NOT NULL)"; + PreparedStatement pstmt = null; + int count = 0; + int countEMails = 0; + try + { + pstmt = DB.prepareStatement (sql, null); + pstmt.setInt(1, m_model.getAD_WorkflowProcessor_ID()); + ResultSet rs = pstmt.executeQuery (); + while (rs.next ()) + { + MWFActivity activity = new MWFActivity (getCtx(), rs, null); + if (activity.getDynPriorityStart() == 0) + activity.setDynPriorityStart(activity.getPriority()); + long ms = System.currentTimeMillis() - activity.getCreated().getTime(); + MWFNode node = activity.getNode(); + int prioDiff = node.calculateDynamicPriority ((int)(ms / 1000)); + activity.setPriority(activity.getDynPriorityStart() + prioDiff); + activity.save(); + count++; + } + rs.close (); + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + log.log(Level.SEVERE, sql, e); + } + m_summary.append("DynPriority #").append(count).append (" - "); + + // Clean-up + try + { + if (pstmt != null) + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + pstmt = null; + } + } // setPriority + + + /** + * Send Alerts + */ + private void sendAlerts() + { + // Alert over Priority + if (m_model.getAlertOverPriority() > 0) + { + String sql = "SELECT * " + + "FROM AD_WF_Activity a " + + "WHERE Processed='N' AND WFState='OS'" // suspended + + " AND Priority >= ?" // ##1 + + " AND (DateLastAlert IS NULL"; + if (m_model.getRemindDays() > 0) + sql += " OR (DateLastAlert+" + m_model.getRemindDays() + + ") < SysDate"; + sql += ") AND EXISTS (SELECT * FROM AD_Workflow wf " + + " INNER JOIN AD_WF_Node wfn ON (wf.AD_Workflow_ID=wfn.AD_Workflow_ID) " + + "WHERE a.AD_WF_Node_ID=wfn.AD_WF_Node_ID" + + " AND wf.AD_WorkflowProcessor_ID IS NULL OR wf.AD_WorkflowProcessor_ID=?)"; + int count = 0; + int countEMails = 0; + try + { + PreparedStatement pstmt = DB.prepareStatement(sql, null); + pstmt.setInt (1, m_model.getAlertOverPriority()); + pstmt.setInt (2, m_model.getAD_WorkflowProcessor_ID()); + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) + { + MWFActivity activity = new MWFActivity (getCtx(), rs, null); + boolean escalate = activity.getDateLastAlert() != null; + countEMails += sendEmail (activity, "ActivityOverPriority", + escalate, true); + activity.setDateLastAlert(new Timestamp(System.currentTimeMillis())); + activity.save(); + count++; + } + rs.close(); + pstmt.close(); + } + catch (SQLException e) + { + log.log(Level.SEVERE, "(Priority) - " + sql, e); + } + m_summary.append("OverPriority #").append(count); + if (countEMails > 0) + m_summary.append(" (").append(countEMails).append(" EMail)"); + m_summary.append (" - "); + } // Alert over Priority + + /** + * Over End Wait + */ + String sql = "SELECT * " + + "FROM AD_WF_Activity a " + + "WHERE Processed='N' AND WFState='OS'" // suspended + + " AND EndWaitTime > SysDate" + + " AND (DateLastAlert IS NULL"; + if (m_model.getRemindDays() > 0) + sql += " OR (DateLastAlert+" + m_model.getRemindDays() + + ") < SysDate"; + sql += ") AND EXISTS (SELECT * FROM AD_Workflow wf " + + " INNER JOIN AD_WF_Node wfn ON (wf.AD_Workflow_ID=wfn.AD_Workflow_ID) " + + "WHERE a.AD_WF_Node_ID=wfn.AD_WF_Node_ID" + + " AND wfn.Action<>'Z'" // not sleeping + + " AND wf.AD_WorkflowProcessor_ID IS NULL OR wf.AD_WorkflowProcessor_ID=?)"; + PreparedStatement pstmt = null; + int count = 0; + int countEMails = 0; + try + { + pstmt = DB.prepareStatement (sql, null); + pstmt.setInt(1, m_model.getAD_WorkflowProcessor_ID()); + ResultSet rs = pstmt.executeQuery (); + while (rs.next ()) + { + MWFActivity activity = new MWFActivity (getCtx(), rs, null); + boolean escalate = activity.getDateLastAlert() != null; + countEMails += sendEmail (activity, "ActivityEndWaitTime", + escalate, false); + activity.setDateLastAlert(new Timestamp(System.currentTimeMillis())); + activity.save(); + count++; + } + rs.close (); + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + log.log(Level.SEVERE, "(EndWaitTime) - " + sql, e); + } + m_summary.append("EndWaitTime #").append(count); + if (countEMails > 0) + m_summary.append(" (").append(countEMails).append(" EMail)"); + m_summary.append (" - "); + + /** + * Send inactivity alerts + */ + if (m_model.getInactivityAlertDays() > 0) + { + sql = "SELECT * " + + "FROM AD_WF_Activity a " + + "WHERE Processed='N' AND WFState='OS'" // suspended + + " AND (Updated+" + m_model.getInactivityAlertDays() + ") < SysDate" + + " AND (DateLastAlert IS NULL"; + if (m_model.getRemindDays() > 0) + sql += " OR (DateLastAlert+" + m_model.getRemindDays() + + ") < SysDate"; + sql += ") AND EXISTS (SELECT * FROM AD_Workflow wf " + + " INNER JOIN AD_WF_Node wfn ON (wf.AD_Workflow_ID=wfn.AD_Workflow_ID) " + + "WHERE a.AD_WF_Node_ID=wfn.AD_WF_Node_ID" + + " AND wf.AD_WorkflowProcessor_ID IS NULL OR wf.AD_WorkflowProcessor_ID=?)"; + count = 0; + countEMails = 0; + try + { + pstmt = DB.prepareStatement(sql, null); + pstmt.setInt (1, m_model.getAD_WorkflowProcessor_ID()); + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) + { + MWFActivity activity = new MWFActivity (getCtx(), rs, null); + boolean escalate = activity.getDateLastAlert() != null; + countEMails += sendEmail (activity, "ActivityInactivity", + escalate, false); + activity.setDateLastAlert(new Timestamp(System.currentTimeMillis())); + activity.save(); + count++; + } + rs.close(); + pstmt.close(); + } + catch (SQLException e) + { + log.log(Level.SEVERE, "(Inactivity): " + sql, e); + } + m_summary.append("Inactivity #").append(count); + if (countEMails > 0) + m_summary.append(" (").append(countEMails).append(" EMail)"); + m_summary.append (" - "); + } // Inactivity + + + // Clean-up + try + { + if (pstmt != null) + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + pstmt = null; + } + } // sendAlerts + + /** + * Send Alert EMail + * @param activity activity + * @param AD_Message message + * @param toProcess true if to process owner + * @param toSupervisor true if to Supervisor + * @return number of mails sent + */ + private int sendEmail (MWFActivity activity, String AD_Message, + boolean toProcess, boolean toSupervisor) + { + if (m_client == null || m_client.getAD_Client_ID() != activity.getAD_Client_ID()) + m_client = MClient.get(getCtx(), activity.getAD_Client_ID()); + + MWFProcess process = new MWFProcess (getCtx(), activity.getAD_WF_Process_ID(), null); + + String subjectVar = activity.getNode().getName(); + String message = activity.getTextMsg(); + if (message == null || message.length() == 0) + message = process.getTextMsg(); + File pdf = null; + PO po = activity.getPO(); + if (po instanceof DocAction) + { + message = ((DocAction)po).getDocumentInfo() + "\n" + message; + pdf = ((DocAction)po).createPDF(); + } + + // Inactivity Alert: Workflow Activity {0} + String subject = Msg.getMsg(m_client.getAD_Language(), AD_Message, + new Object[] {subjectVar}); + + // Prevent duplicates + ArrayList list = new ArrayList(); + int counter = 0; + + // To Activity Owner + if (m_client.sendEMail(activity.getAD_User_ID(), subject, message, pdf)) + counter++; + list.add (new Integer(activity.getAD_User_ID())); + + // To Process Owner + if (toProcess + && process.getAD_User_ID() != activity.getAD_User_ID()) + { + if (m_client.sendEMail(process.getAD_User_ID(), subject, message, pdf)) + counter++; + list.add (new Integer(process.getAD_User_ID())); + } + + // To Activity Responsible + MWFResponsible responsible = MWFResponsible.get(getCtx(), activity.getAD_WF_Responsible_ID()); + counter += sendAlertToResponsible (responsible, list, process, + subject, message, pdf); + + // To Process Responsible + if (toProcess + && process.getAD_WF_Responsible_ID() != activity.getAD_WF_Responsible_ID()) + { + responsible = MWFResponsible.get(getCtx(), process.getAD_WF_Responsible_ID()); + counter += sendAlertToResponsible (responsible, list, process, + subject, message, pdf); + } + + // Processor SuperVisor + if (toSupervisor + && m_model.getSupervisor_ID() != 0 + && !list.contains(new Integer(m_model.getSupervisor_ID()))) + { + if (m_client.sendEMail(m_model.getSupervisor_ID(), subject, message, pdf)) + counter++; + list.add (new Integer(m_model.getSupervisor_ID())); + } + + return counter; + } // sendAlert + + /** + * Send Alert To Responsible + * @param responsible responsible + * @param list list of already sent users + * @param process process + * @param subject subject + * @param message message + * @param pdf optional pdf + * @return number of mail sent + */ + private int sendAlertToResponsible (MWFResponsible responsible, + ArrayList list, MWFProcess process, + String subject, String message, File pdf) + { + int counter = 0; + if (responsible.isInvoker()) + ; + // Human + else if (MWFResponsible.RESPONSIBLETYPE_Human.equals(responsible.getResponsibleType()) + && responsible.getAD_User_ID() != 0 + && !list.contains(new Integer(responsible.getAD_User_ID()))) + { + if (m_client.sendEMail(responsible.getAD_User_ID(), subject, message, pdf)) + counter++; + list.add (new Integer(responsible.getAD_User_ID())); + } + // Org of the Document + else if (MWFResponsible.RESPONSIBLETYPE_Organization.equals(responsible.getResponsibleType())) + { + PO document = process.getPO(); + if (document != null) + { + MOrgInfo org = MOrgInfo.get (getCtx(), document.getAD_Org_ID()); + if (org.getSupervisor_ID() != 0 + && !list.contains(new Integer(org.getSupervisor_ID()))) + { + if (m_client.sendEMail(org.getSupervisor_ID(), subject, message, pdf)) + counter++; + list.add (new Integer(org.getSupervisor_ID())); + } + } + } + // Role + else if (MWFResponsible.RESPONSIBLETYPE_Role.equals(responsible.getResponsibleType()) + && responsible.getAD_Role_ID() != 0) + { + MUserRoles[] userRoles = MUserRoles.getOfRole(getCtx(), responsible.getAD_Role_ID()); + for (int i = 0; i < userRoles.length; i++) + { + MUserRoles roles = userRoles[i]; + if (!roles.isActive()) + continue; + int AD_User_ID = roles.getAD_User_ID(); + if (!list.contains(new Integer(AD_User_ID))) + { + if (m_client.sendEMail(AD_User_ID, subject, message, pdf)) + counter++; + list.add (new Integer(AD_User_ID)); + } + } + } + return counter; + } // sendAlertToResponsible + + + /** + * Get Server Info + * @return info + */ + public String getServerInfo() + { + return "#" + p_runCount + " - Last=" + m_summary.toString(); + } // getServerInfo + +} // WorkflowProcessor diff --git a/serverRoot/src/main/servlet/org/compiere/web/AdempiereMonitor.java b/serverRoot/src/main/servlet/org/compiere/web/AdempiereMonitor.java new file mode 100644 index 0000000000..dcf1e54d1b --- /dev/null +++ b/serverRoot/src/main/servlet/org/compiere/web/AdempiereMonitor.java @@ -0,0 +1,869 @@ +/****************************************************************************** + * 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.web; + +import java.io.*; +import java.lang.management.*; +import java.sql.*; +import java.util.*; +import java.util.logging.*; +import javax.servlet.*; +import javax.servlet.http.*; +import org.apache.ecs.*; +import org.apache.ecs.xhtml.*; +import org.compiere.*; +import org.compiere.db.*; +import org.compiere.model.*; +import org.compiere.server.*; +import org.compiere.util.*; + +/** + * Adempiere Server Monitor + * + * @author Jorg Janke + * @version $Id: AdempiereMonitor.java,v 1.3 2006/07/30 00:53:33 jjanke Exp $ + */ +public class AdempiereMonitor extends HttpServlet +{ + /** Logger */ + private static CLogger log = CLogger.getCLogger(AdempiereMonitor.class); + /** The Server */ + private AdempiereServerMgr m_serverMgr = null; + /** Message */ + private p m_message = null; + + + + /** + * Get + * @param request request + * @param response response + * @throws javax.servlet.ServletException + * @throws java.io.IOException + */ + protected void doGet (HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException + { + m_message = null; + if (processLogParameter (request, response)) + return; + if (processTraceParameter (request, response)) + return; + if (processEMailParameter (request, response)) + return; + if (processCacheParameter (request, response)) + return; + // + if (processRunNowParameter (request)) + ; + else + processActionParameter (request); + createSummaryPage(request, response); + } // doGet + + /** + * Post + * @param request request + * @param response response + * @throws ServletException + * @throws IOException + */ + protected void doPost (HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException + { + doGet(request, response); + } // doPost + + /** + * Process Log Parameter and return log page + * @param request request + * @param response response + * @return true if it was a log request + * @throws ServletException + * @throws IOException + */ + private boolean processLogParameter (HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException + { + String serverID = WebUtil.getParameter (request, "Log"); + if (serverID == null || serverID.length() == 0) + return false; + + log.info ("ServerID=" + serverID); + AdempiereServer server = m_serverMgr.getServer(serverID); + if (server == null) + { + m_message = new p(); + m_message.addElement(new strong("Server not found: ")); + m_message.addElement(serverID); + return false; + } + + WebDoc doc = WebDoc.create ("Adempiere Server Monitor Log"); + // Body + body b = doc.getBody(); + // + p para = new p(); + a link = new a ("adempiereMonitor#" + serverID, "Return"); + para.addElement(link); + b.addElement(para); + // + b.addElement(new h2(server.getName())); + // + table table = new table(); + table.setBorder(1); + table.setCellSpacing(2); + table.setCellPadding(2); + + // Header + tr line = new tr(); + line.addElement(new th().addElement("Created")); + line.addElement(new th().addElement("Summary")); + // line.addElement(new th().addElement("Error")); + line.addElement(new th().addElement("Reference")); + line.addElement(new th().addElement("TextMsg")); + // line.addElement(new th().addElement("Description")); + table.addElement(line); + + AdempiereProcessorLog[] logs = server.getLogs(); + for (int i = 0; i < logs.length; i++) + { + AdempiereProcessorLog pLog = logs[i]; + line = new tr(); + line.addElement(new td().addElement(WebEnv.getCellContent(pLog.getCreated()))); + line.addElement(new td().addElement(WebEnv.getCellContent(pLog.getSummary()))); + line.addElement(new td().addElement(WebEnv.getCellContent(pLog.getReference()))); + line.addElement(new td().addElement(WebEnv.getCellContent(pLog.getTextMsg()))); + table.addElement(line); + } + // + b.addElement(table); + link = new a ("#top", "Top"); + b.addElement(link); + + // fini + WebUtil.createResponse (request, response, this, null, doc, false); + return true; + } // processLogParameter + + /** + * Process Run Parameter + * @param request request + * @return true if it was a Run request + * @throws ServletException + * @throws IOException + */ + private boolean processRunNowParameter (HttpServletRequest request) + throws ServletException, IOException + { + String serverID = WebUtil.getParameter (request, "RunNow"); + if (serverID == null || serverID.length() == 0) + return false; + + log.info ("ServerID=" + serverID); + AdempiereServer server = m_serverMgr.getServer(serverID); + if (server == null) + { + m_message = new p(); + m_message.addElement(new strong("Server not found: ")); + m_message.addElement(serverID); + return false; + } + // + server.runNow(); + // + return true; + } // processRunParameter + + /** + * Process Action Parameter + * @param request request + */ + private void processActionParameter (HttpServletRequest request) + { + String action = WebUtil.getParameter (request, "Action"); + if (action == null || action.length() == 0) + return; + log.info ("Action=" + action); + try + { + boolean start = action.startsWith("Start"); + m_message = new p(); + String msg = (start ? "Started" : "Stopped") + ": "; + m_message.addElement(new strong(msg)); + // + String serverID = action.substring(action.indexOf("_")+1); + boolean ok = false; + if (serverID.equals("All")) + { + if (start) + ok = m_serverMgr.startAll(); + else + ok = m_serverMgr.stopAll(); + m_message.addElement("All"); + } + else + { + AdempiereServer server = m_serverMgr.getServer(serverID); + if (server == null) + { + m_message = new p(); + m_message.addElement(new strong("Server not found: ")); + m_message.addElement(serverID); + return; + } + else + { + if (start) + ok = m_serverMgr.start (serverID); + else + ok = m_serverMgr.stop (serverID); + m_message.addElement(server.getName()); + } + } + m_message.addElement(ok ? " - OK" : " - Error!"); + } + catch (Exception e) + { + m_message = new p(); + m_message.addElement(new strong("Error processing parameter: " + action)); + m_message.addElement(new br()); + m_message.addElement(e.toString()); + } + } // processActionParameter + + /** + * Process Trace Parameter + * @param request request + * @param response response + * @return true if it was a trace request with output + * @throws ServletException + * @throws IOException + */ + private boolean processTraceParameter (HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException + { + String traceCmd = WebUtil.getParameter (request, "Trace"); + String traceLevel = WebUtil.getParameter (request, "TraceLevel"); + if (traceLevel != null && traceLevel.length() > 0) + { + log.info ("New Level: " + traceLevel); + CLogMgt.setLevel(traceLevel); + Ini.setProperty(Ini.P_TRACELEVEL, traceLevel); + Ini.saveProperties(false); + return false; + } + + if (traceCmd == null || traceCmd.length() == 0) + return false; + + log.info ("Command: " + traceCmd); + CLogFile fileHandler = CLogFile.get (false, null, false); + // + if (traceCmd.equals("ROTATE")) + { + if (fileHandler != null) + fileHandler.rotateLog(); + return false; // re-display + } + else if (traceCmd.equals("DELETE")) + { + File logDir = fileHandler.getLogDirectory(); + if (logDir != null && logDir.isDirectory()) + { + File[] logs = logDir.listFiles(); + for (int i = 0; i < logs.length; i++) + { + String fileName = logs[i].getAbsolutePath(); + if (fileName.equals(fileHandler.getFileName())) + continue; + if (logs[i].delete()) + log.warning("Deleted: " + fileName); + else + log.warning("Not Deleted: " + fileName); + } + } + return false; // re-display + } + + // Display current log File + if (fileHandler != null && fileHandler.getFileName().equals(traceCmd)) + fileHandler.flush(); + + // Spool File + File file = new File (traceCmd); + if (!file.exists()) + { + log.warning ("Did not find File: " + traceCmd); + return false; + } + if (file.length() == 0) + { + log.warning ("File Length=0: " + traceCmd); + return false; + } + + // Stream Log + log.info ("Streaming: " + traceCmd); + try + { + long time = System.currentTimeMillis(); // timer start + int fileLength = (int)file.length(); + int bufferSize = 2048; // 2k Buffer + byte[] buffer = new byte[bufferSize]; + // + response.setContentType("text/plain"); + response.setBufferSize(bufferSize); + response.setContentLength(fileLength); + // + FileInputStream fis = new FileInputStream(file); + ServletOutputStream out = response.getOutputStream (); + int read = 0; + while ((read = fis.read(buffer)) > 0) + out.write (buffer, 0, read); + out.flush(); + out.close(); + fis.close(); + // + time = System.currentTimeMillis() - time; + double speed = (fileLength/1024) / ((double)time/1000); + log.info("length=" + + fileLength + " - " + + time + " ms - " + + speed + " kB/sec"); + } + catch (IOException ex) + { + log.log(Level.SEVERE, "stream" + ex); + } + return true; + } // processTraceParameter + + /** + * Process EMail Parameter + * @param request request + * @param response response + * @return true if it was a email request with output + * @throws ServletException + * @throws IOException + */ + private boolean processEMailParameter (HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException + { + String email = WebUtil.getParameter (request, "EMail"); + if (email == null || email.length() == 0) + return false; + + int AD_Client_ID = -1; + try + { + AD_Client_ID = Integer.parseInt(email); + } + catch (Exception e) + { + log.warning("Parsing: " + email + " - " + e.toString()); + } + if (AD_Client_ID < 0) + { + m_message = new p(); + m_message.addElement("No EMail: " + email); + return false; + } + + // log.info ("Test EMail: " + AD_Client_ID); + MClient client = MClient.get(new Properties(), AD_Client_ID); + log.info ("Test: " + client); + + m_message = new p(); + m_message.addElement(client.getName() + ": " + client.testEMail()); + return false; + } // processEMailParameter + + + /** + * Process Cache Parameter + * @param request request + * @param response response + * @return true if it was a email request with output + * @throws ServletException + * @throws IOException + */ + private boolean processCacheParameter (HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException + { + String cmd = WebUtil.getParameter (request, "CacheReset"); + if (cmd == null || cmd.length() == 0) + return false; + String tableName = WebUtil.getParameter (request, "CacheTableName"); + String record_ID = WebUtil.getParameter (request, "CacheRecord_ID"); + + m_message = new p(); + try + { + if (tableName == null || tableName.length() == 0) + { + CacheMgt.get().reset(); + m_message.addElement("Cache Reset: All"); + } + else if (record_ID == null || record_ID.length() == 0) + { + CacheMgt.get().reset(tableName); + m_message.addElement("Cache Reset: " + tableName); + } + else + { + CacheMgt.get().reset(tableName, Integer.parseInt(record_ID)); + m_message.addElement("Cache Reset: " + tableName + ", Record_ID=" + record_ID); + } + } + catch (Exception e) + { + log.severe(e.toString()); + m_message.addElement("Error: " + e.toString()); + } + return false; // continue + } // processEMailParameter + + /************************************************************************** + * Create & Return Summary Page + * @param request request + * @param response response + * @throws ServletException + * @throws IOException + */ + private void createSummaryPage (HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException + { + WebDoc doc = WebDoc.create ("Adempiere Server Monitor"); + // log.info("ServletConfig=" + getServletConfig()); + // AdempiereServerGroup.get().dump(); + + // Body + body bb = doc.getBody(); + // Message + if (m_message != null) + { + bb.addElement(new hr()); + bb.addElement(m_message); + bb.addElement(new hr()); + } + + // Summary + table table = new table(); + table.setBorder(1); + table.setCellSpacing(2); + table.setCellPadding(2); + // + tr line = new tr(); + line.addElement(new th().addElement(Adempiere.getName())); + line.addElement(new td().addElement(Adempiere.getVersion())); + table.addElement(line); + line = new tr(); + line.addElement(new th().addElement(Adempiere.getImplementationVendor())); + line.addElement(new td().addElement(Adempiere.getImplementationVersion())); + table.addElement(line); + line = new tr(); + line.addElement(new th().addElement("Manager")); + line.addElement(new td().addElement(WebEnv.getCellContent(m_serverMgr.getDescription()))); + table.addElement(line); + line = new tr(); + line.addElement(new th().addElement("Start - Elapsed")); + line.addElement(new td().addElement(WebEnv.getCellContent(m_serverMgr.getStartTime()) + + " - " + TimeUtil.formatElapsed(m_serverMgr.getStartTime()))); + table.addElement(line); + line = new tr(); + line.addElement(new th().addElement("Servers")); + line.addElement(new td().addElement(WebEnv.getCellContent(m_serverMgr.getServerCount()))); + table.addElement(line); + line = new tr(); + line.addElement(new th().addElement("Last Updated")); + line.addElement(new td().addElement(new Timestamp(System.currentTimeMillis()).toString())); + table.addElement(line); + bb.addElement(table); + // + p para = new p(); + a link = new a ("adempiereMonitor?Action=Start_All", "Start All"); + para.addElement(link); + para.addElement(" - "); + link = new a ("adempiereMonitor?Action=Stop_All", "Stop All"); + para.addElement(link); + para.addElement(" - "); + link = new a ("adempiereMonitor", "Refresh"); + para.addElement(link); + bb.addElement(para); + + // ***** Server Links ***** + bb.addElement(new hr()); + para = new p(); + AdempiereServer[] servers = m_serverMgr.getAll(); + for (int i = 0; i < servers.length; i++) + { + if (i > 0) + para.addElement(new br()); + AdempiereServer server = servers[i]; + link = new a ("#" + server.getServerID(), server.getName()); + para.addElement(link); + font status = null; + if (server.isAlive()) + status = new font().setColor(HtmlColor.GREEN).addElement(" (Running)"); + else + status = new font().setColor(HtmlColor.RED).addElement(" (Stopped)"); + para.addElement(status); + } + bb.addElement(para); + + // **** Log Management **** + createLogMgtPage(bb); + + // ***** Server Details ***** + for (int i = 0; i < servers.length; i++) + { + AdempiereServer server = servers[i]; + bb.addElement(new hr()); + bb.addElement(new a().setName(server.getServerID())); + bb.addElement(new h2(server.getName())); + // + table = new table(); + table.setBorder(1); + table.setCellSpacing(2); + table.setCellPadding(2); + // Status + line = new tr(); + if (server.isAlive()) + { + String msg = "Stop"; + if (server.isInterrupted()) + msg += " (Interrupted)"; + link = new a ("adempiereMonitor?Action=Stop_" + server.getServerID(), msg); + if (server.isSleeping()) + { + line.addElement(new th().addElement("Sleeping")); + line.addElement(new td().addElement(link)); + } + else + { + line.addElement(new th().addElement("Running")); + line.addElement(new td().addElement(link)); + } + table.addElement(line); + line = new tr(); + line.addElement(new th().addElement("Start - Elapsed")); + line.addElement(new td().addElement(WebEnv.getCellContent(server.getStartTime()) + + " - " + TimeUtil.formatElapsed(server.getStartTime()))); + } + else + { + String msg = "Start"; + if (server.isInterrupted()) + msg += " (Interrupted)"; + line.addElement(new th().addElement("Not Started")); + link = new a ("adempiereMonitor?Action=Start_" + server.getServerID(), msg); + line.addElement(new td().addElement(link)); + } + table.addElement(line); + // + line = new tr(); + line.addElement(new th().addElement("Description")); + line.addElement(new td().addElement(WebEnv.getCellContent(server.getDescription()))); + table.addElement(line); + // + line = new tr(); + line.addElement(new th().addElement("Last Run")); + line.addElement(new td().addElement(WebEnv.getCellContent(server.getDateLastRun()))); + table.addElement(line); + line = new tr(); + line.addElement(new th().addElement("Info")); + line.addElement(new td().addElement(WebEnv.getCellContent(server.getServerInfo()))); + table.addElement(line); + // + line = new tr(); + line.addElement(new th().addElement("Next Run")); + td td = new td(); + td.addElement(WebEnv.getCellContent(server.getDateNextRun(false))); + td.addElement(" - "); + link = new a ("adempiereMonitor?RunNow=" + server.getServerID(), "(Run Now)"); + td.addElement(link); + line.addElement(td); + table.addElement(line); + // + line = new tr(); + line.addElement(new th().addElement("Statistics")); + line.addElement(new td().addElement(server.getStatistics())); + table.addElement(line); + // + + // Add table to Body + bb.addElement(table); + link = new a ("#top", "Top"); + bb.addElement(link); + bb.addElement(" - "); + link = new a ("adempiereMonitor?Log=" + server.getServerID(), "Log"); + bb.addElement(link); + bb.addElement(" - "); + link = new a ("adempiereMonitor", "Refresh"); + bb.addElement(link); + } + + // fini + WebUtil.createResponse (request, response, this, null, doc, false); + } // createSummaryPage + + /** + * Add Log Management to page + * @param bb body + */ + private void createLogMgtPage (body bb) + { + bb.addElement(new hr()); + + // Ini Parameters + table table = new table(); + table.setBorder(1); + table.setCellSpacing(2); + table.setCellPadding(2); + // + Properties ctx = new Properties(); + MSystem system = MSystem.get(ctx); + tr line = new tr(); + line.addElement(new th().addElement(system.getDBAddress())); + line.addElement(new td().addElement(Ini.getAdempiereHome())); + table.addElement(line); + // OS + Name + line = new tr(); + String info = System.getProperty("os.name") + + " " + System.getProperty("os.version"); + String s = System.getProperty("sun.os.patch.level"); + if (s != null && s.length() > 0) + info += " (" + s + ")"; + line.addElement(new th().addElement(info)); + info = system.getName(); + if (system.getCustomPrefix() != null) + info += " (" + system.getCustomPrefix() + ")"; + line.addElement(new td().addElement(info)); + table.addElement(line); + // Java + email + line = new tr(); + info = System.getProperty("java.vm.name") + + " " + System.getProperty("java.vm.version"); + line.addElement(new th().addElement(info)); + line.addElement(new td().addElement(system.getUserName())); + table.addElement(line); + // DB + Instance + line = new tr(); + CConnection cc = CConnection.get(); + AdempiereDatabase db = cc.getDatabase(); + info = db.getDescription(); + line.addElement(new th().addElement(info)); + line.addElement(new td().addElement(cc.getConnectionURL())); +// line.addElement(new td().addElement(system.getDBInstance())); + table.addElement(line); + // Processors/Support + line = new tr(); + line.addElement(new th().addElement("Processor/Support")); + line.addElement(new td().addElement(system.getNoProcessors() + "/" + system.getSupportUnits())); + table.addElement(line); + // Memory + line = new tr(); + MemoryMXBean memory = ManagementFactory.getMemoryMXBean(); + line.addElement(new th().addElement("VM Memory")); + line.addElement(new td().addElement(new CMemoryUsage(memory.getNonHeapMemoryUsage()).toString())); + table.addElement(line); + line = new tr(); + line.addElement(new th().addElement("Heap Memory")); + line.addElement(new td().addElement(new CMemoryUsage(memory.getHeapMemoryUsage()).toString())); + table.addElement(line); + // Runtime + line = new tr(); + RuntimeMXBean rt = ManagementFactory.getRuntimeMXBean(); + line.addElement(new th().addElement("Runtime " + rt.getName())); + line.addElement(new td().addElement(TimeUtil.formatElapsed(rt.getUptime()))); + table.addElement(line); + // Threads + line = new tr(); + ThreadMXBean th = ManagementFactory.getThreadMXBean(); + line.addElement(new th().addElement("Threads " + th.getThreadCount())); + line.addElement(new td().addElement("Peak=" + th.getPeakThreadCount() + + ", Demons=" + th.getDaemonThreadCount() + + ", Total=" + th.getTotalStartedThreadCount())); + table.addElement(line); + // Cache Reset + line = new tr(); + line.addElement(new th().addElement(CacheMgt.get().toStringX())); + line.addElement(new td().addElement(new a ("adempiereMonitor?CacheReset=Yes", "Reset Cache"))); + table.addElement(line); + + // Trace Level + line = new tr(); + line.addElement(new th().addElement(new label("TraceLevel").addElement("Trace Log Level"))); + form myForm = new form("adempiereMonitor", form.METHOD_POST, form.ENC_DEFAULT); + // LogLevel Selection + option[] options = new option[CLogMgt.LEVELS.length]; + for (int i = 0; i < options.length; i++) + { + options[i] = new option(CLogMgt.LEVELS[i].getName()); + options[i].addElement(CLogMgt.LEVELS[i].getName()); + if (CLogMgt.LEVELS[i] == CLogMgt.getLevel()) + options[i].setSelected(true); + } + select sel = new select("TraceLevel", options); + myForm.addElement(sel); + myForm.addElement(new input(input.TYPE_SUBMIT, "Set", "Set")); + line.addElement(new td().addElement(myForm)); + table.addElement(line); + // + line = new tr(); + CLogFile fileHandler = CLogFile.get (true, null, false); + line.addElement(new th().addElement("Trace File")); + line.addElement(new td().addElement(new a ("adempiereMonitor?Trace=" + fileHandler.getFileName(), "Current"))); + table.addElement(line); + // + line = new tr(); + line.addElement(new td().addElement(new a ("adempiereMonitor?Trace=ROTATE", "Rotate Trace Log"))); + line.addElement(new td().addElement(new a ("adempiereMonitor?Trace=DELETE", "Delete all Trace Logs"))); + table.addElement(line); + // + bb.addElement(table); + + // List Log Files + p p = new p(); + p.addElement(new b("All Log Files: ")); + // All in dir + File logDir = fileHandler.getLogDirectory(); + if (logDir != null && logDir.isDirectory()) + { + File[] logs = logDir.listFiles(); + for (int i = 0; i < logs.length; i++) + { + if (i != 0) + p.addElement(" - "); + String fileName = logs[i].getAbsolutePath(); + a link = new a ("adempiereMonitor?Trace=" + fileName, fileName); + p.addElement(link); + int size = (int)(logs[i].length()/1024); + if (size < 1024) + p.addElement(" (" + size + "k)"); + else + p.addElement(" (" + size/1024 + "M)"); + } + } + bb.addElement(p); + + // Clients and Web Stores + table = new table(); + table.setBorder(1); + table.setCellSpacing(2); + table.setCellPadding(2); + // + line = new tr(); + MClient[] clients = MClient.getAll(ctx); + line.addElement(new th().addElement("Client #" + clients.length + " - EMail Test:")); + p = new p(); + for (int i = 0; i < clients.length; i++) + { + MClient client = clients[i]; + if (i > 0) + p.addElement(" - "); + p.addElement(new a("adempiereMonitor?EMail=" + client.getAD_Client_ID(), client.getName())); + } + if (clients.length == 0) + p.addElement(" "); + line.addElement(new td().addElement(p)); + table.addElement(line); + // + line = new tr(); + MStore[] wstores = MStore.getActive(); + line.addElement(new th().addElement("Active Web Stores #" + wstores.length)); + p = new p(); + for (int i = 0; i < wstores.length; i++) + { + MStore store = wstores[i]; + if (i > 0) + p.addElement(" - "); + a a = new a(store.getWebContext(), store.getName()); + a.setTarget("t" + i); + p.addElement(a); + } + if (wstores.length == 0) + p.addElement(" "); + line.addElement(new td().addElement(p)); + table.addElement(line); + // + bb.addElement(table); + } // createLogMgtPage + + /************************************************************************** + * Init + * @param config config + * @throws javax.servlet.ServletException + */ + public void init (ServletConfig config) throws ServletException + { + WebEnv.initWeb(config); + log.info (""); + m_serverMgr = AdempiereServerMgr.get(); + } // init + + /** + * Destroy + */ + public void destroy () + { + log.info ("destroy"); + m_serverMgr = null; + } // destroy + + /** + * Log error/warning + * @param message message + * @param e exception + */ + public void log (String message, Throwable e) + { + if (e == null) + log.warning (message); + log.log(Level.SEVERE, message, e); + } // log + + /** + * Log debug + * @param message message + */ + public void log (String message) + { + log.fine(message); + } // log + + + /** + * Get Servlet Name + * @return servlet name + */ + public String getServletName () + { + return "AdempiereMonitor"; + } // getServletName + + /** + * Get Servlet Info + * @return servlet info + */ + public String getServletInfo () + { + return "Adempiere Server Monitor"; + } // getServletName + +} // AdempiereMonitor diff --git a/serverRoot/src/main/servlet/org/compiere/web/AdempiereMonitorFilter.java b/serverRoot/src/main/servlet/org/compiere/web/AdempiereMonitorFilter.java new file mode 100644 index 0000000000..ebdf14f890 --- /dev/null +++ b/serverRoot/src/main/servlet/org/compiere/web/AdempiereMonitorFilter.java @@ -0,0 +1,166 @@ +/****************************************************************************** + * 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.web; + +import java.io.*; +import java.util.logging.*; +import javax.servlet.*; +import javax.servlet.Filter; +import javax.servlet.http.*; +import org.compiere.model.*; +import org.compiere.util.*; +import sun.misc.*; + +/** + * Adempiere Monitor Filter. + * Application Server independent check of username/password + * + * @author Jorg Janke + * @version $Id: AdempiereMonitorFilter.java,v 1.2 2006/07/30 00:53:33 jjanke Exp $ + */ +public class AdempiereMonitorFilter implements Filter +{ + /** + * AdempiereMonitorFilter + */ + public AdempiereMonitorFilter () + { + super (); + m_authorization = new Long (System.currentTimeMillis()); + } // AdempiereMonitorFilter + + /** Logger */ + protected CLogger log = CLogger.getCLogger(getClass()); + + /** Authorization ID */ + private static final String AUTHORIZATION = "AdempiereAuthorization"; + /** Authorization Marker */ + private Long m_authorization = null; + + /** + * Init + * @param config configuration + * @throws ServletException + */ + public void init (FilterConfig config) + throws ServletException + { + log.info (""); + } // Init + + /** + * Filter + * @param request request + * @param response response + * @param chain chain + * @throws IOException + * @throws ServletException + */ + public void doFilter (ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException + { + boolean error = false; + String errorPage = "/error.html"; + boolean pass = false; + try + { + if (!(request instanceof HttpServletRequest && response instanceof HttpServletResponse)) + { + request.getRequestDispatcher(errorPage).forward(request, response); + return; + } + HttpServletRequest req = (HttpServletRequest)request; + HttpServletResponse resp = (HttpServletResponse)response; + // Previously checked + HttpSession session = req.getSession(true); + Long compare = (Long)session.getAttribute(AUTHORIZATION); + if (compare != null && compare.compareTo(m_authorization) == 0) + { + pass = true; + } + else if (checkAuthorization (req.getHeader("Authorization"))) + { + session.setAttribute(AUTHORIZATION, m_authorization); + pass = true; + } + // -------------------------------------------- + if (pass) + { + chain.doFilter(request, response); + } + else + { + resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + resp.setHeader("WWW-Authenticate", "BASIC realm=\"Adempiere Server\""); + } + return; + } + catch (Exception e) + { + log.log(Level.SEVERE, "filter", e); + } + request.getRequestDispatcher(errorPage).forward(request, response); + } // doFilter + + /** + * Check Authorization + * @param authorization authorization + * @return true if authenticated + */ + private boolean checkAuthorization (String authorization) + { + if (authorization == null) + return false; + try + { + String userInfo = authorization.substring(6).trim(); + BASE64Decoder decoder = new BASE64Decoder(); + String namePassword = new String (decoder.decodeBuffer(userInfo)); + // log.fine("checkAuthorization - Name:Password=" + namePassword); + int index = namePassword.indexOf(":"); + String name = namePassword.substring(0, index); + String password = namePassword.substring(index+1); + MUser user = MUser.get(Env.getCtx(), name, password); + if (user == null) + { + log.warning ("User not found: '" + name + "/" + password + "'"); + return false; + } + if (!user.isAdministrator()) + { + log.warning ("Not a Sys Admin = " + name); + return false; + } + log.info ("Name=" + name); + return true; + } + catch (Exception e) + { + log.log(Level.SEVERE, "check", e); + } + return false; + } // check + + /** + * Destroy + */ + public void destroy () + { + log.info (""); + } // destroy + +} // AdempiereMonitorFilter diff --git a/serverRoot/src/main/servlet/org/compiere/web/ServerStatus.java b/serverRoot/src/main/servlet/org/compiere/web/ServerStatus.java new file mode 100644 index 0000000000..95ef842027 --- /dev/null +++ b/serverRoot/src/main/servlet/org/compiere/web/ServerStatus.java @@ -0,0 +1,96 @@ +/****************************************************************************** + * 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.web; + +import java.io.*; + +import javax.servlet.*; +import javax.servlet.http.*; + + +/** + * + * + * @author Jorg Janke + * @version $Id: ServerStatus.java,v 1.2 2006/07/30 00:53:33 jjanke Exp $ + */ +public class ServerStatus extends HttpServlet +{ + /** + * doGet + * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) + * @param arg0 + * @param arg1 + * @throws javax.servlet.ServletException + * @throws java.io.IOException + */ + protected void doGet (HttpServletRequest arg0, HttpServletResponse arg1) + throws ServletException, IOException + { + super.doGet (arg0, arg1); + } + + /** + * doPost + * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) + * @param arg0 + * @param arg1 + * @throws javax.servlet.ServletException + * @throws java.io.IOException + */ + protected void doPost (HttpServletRequest arg0, HttpServletResponse arg1) + throws ServletException, IOException + { + // TODO Auto-generated method stub + super.doPost (arg0, arg1); + } + + /** + * getServletInfo + * @see javax.servlet.Servlet#getServletInfo() + * @return servlet info + */ + public String getServletInfo () + { + return super.getServletInfo (); + } + + /** + * init + * @see javax.servlet.GenericServlet#init() + * @throws javax.servlet.ServletException + */ + public void init () + throws ServletException + { + super.init (); + } + + /** + * init + * @see javax.servlet.Servlet#init(javax.servlet.ServletConfig) + * @param arg0 + * @throws javax.servlet.ServletException + */ + public void init (ServletConfig arg0) + throws ServletException + { + // TODO Auto-generated method stub + super.init (arg0); + } + +} // ServerStatus diff --git a/serverRoot/src/main/servlet/org/compiere/web/StatusInfo.java b/serverRoot/src/main/servlet/org/compiere/web/StatusInfo.java new file mode 100644 index 0000000000..d6a5be14fc --- /dev/null +++ b/serverRoot/src/main/servlet/org/compiere/web/StatusInfo.java @@ -0,0 +1,168 @@ +/****************************************************************************** + * 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.web; + +import javax.servlet.*; +import javax.servlet.http.*; +import java.io.*; +import javax.naming.*; + +import javax.sql.*; +import java.sql.*; + +import org.compiere.interfaces.*; + +/** + * Status Info Servlet + * + * @author Jorg Janke + * @version $Id: StatusInfo.java,v 1.3 2006/07/30 00:53:33 jjanke Exp $ + */ +public class StatusInfo extends HttpServlet +{ + static final private String CONTENT_TYPE = "text/html"; + /** + * Initialize global variables + * @throws ServletException + */ + public void init() throws ServletException + { + getServletContext().log("StatusInfo.init"); + } + + /** + * Get + * @param request + * @param response + * @throws ServletException + * @throws IOException + */ + public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + { + response.setContentType(CONTENT_TYPE); + PrintWriter out = response.getWriter(); + out.println(""); + out.println("Status Info"); + out.println(""); + + InitialContext context = null; + try + { + context = new InitialContext(); + } + catch (Exception ex) + { + out.println("

" + ex + "

"); + } + + try + { + StatusHome statusHome = (StatusHome)context.lookup (StatusHome.JNDI_NAME); + Status status = statusHome.create(); + out.println("

" + status.getStatus() + "

"); + status.remove(); + } + catch (Exception ex) + { + out.println("

" + ex + "

"); + } + + try + { + ServerHome serverHome = (ServerHome)context.lookup (ServerHome.JNDI_NAME); + Server server = serverHome.create(); + out.println("

" + server.getStatus() + "

"); + server.remove(); + } + catch (Exception ex) + { + out.println("

" + ex + "

"); + } + + try + { + out.println("

-- /

"); + NamingEnumeration ne = context.list("/"); + while (ne.hasMore()) + out.println("
" + ne.nextElement()); + out.println("

-- java

"); + ne = context.list("java:"); + while (ne.hasMore()) + out.println("
" + ne.nextElement()); + out.println("

-- ejb

"); + ne = context.list("ejb"); + while (ne.hasMore()) + out.println("
" + ne.nextElement()); + + // + + out.println("

-- DS

"); + DataSource ds = (DataSource)context.lookup("java:/OracleDS"); + out.println("
DataSource " + ds.getClass().getName() + " LoginTimeout=" + ds.getLoginTimeout()); + + Connection con = ds.getConnection("adempiere","adempiere"); + out.println("
Connection "); + + getServletContext().log("Connection closed=" + con.isClosed()); + DatabaseMetaData dbmd = con.getMetaData(); + getServletContext().log("DB " + dbmd.getDatabaseProductName()); + getServletContext().log("DB V " + dbmd.getDatabaseProductVersion()); + getServletContext().log("Driver " + dbmd.getDriverName()); + getServletContext().log("Driver V " + dbmd.getDriverVersion()); + getServletContext().log("JDBC " + dbmd.getJDBCMajorVersion()); + getServletContext().log("JDBC mV " + dbmd.getJDBCMinorVersion()); + + getServletContext().log("User " + dbmd.getUserName()); + + getServletContext().log("ANSI 92 " + dbmd.supportsANSI92FullSQL()); + getServletContext().log("Connection Alter Table ADD" + dbmd.supportsAlterTableWithAddColumn()); + getServletContext().log("Connection Alter Table DROP " + dbmd.supportsAlterTableWithDropColumn()); + getServletContext().log("Connection DDL&DML " + dbmd.supportsDataDefinitionAndDataManipulationTransactions()); + getServletContext().log("Connection CatalogsIn DML " + dbmd.supportsCatalogsInDataManipulation()); + getServletContext().log("Connection Schema In DML " + dbmd.supportsSchemasInDataManipulation()); + + + + } + catch (Exception e) + { + out.println("

" + e + "

"); + } + + + out.println(""); + } + + /** + * Put - Processes Get + * @param request + * @param response + * @throws ServletException + * @throws IOException + */ + public void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + { + doGet (request, response); + } + /** + * Destroy + */ + public void destroy() + { + getServletContext().log("StatusInfo.destroy"); + } +} diff --git a/serverRoot/src/web/AdemPiere150x50.gif b/serverRoot/src/web/AdemPiere150x50.gif new file mode 100644 index 0000000000..6244fce225 Binary files /dev/null and b/serverRoot/src/web/AdemPiere150x50.gif differ diff --git a/serverRoot/src/web/Adempiere120x60.gif b/serverRoot/src/web/Adempiere120x60.gif new file mode 100644 index 0000000000..07ca4d0ff0 Binary files /dev/null and b/serverRoot/src/web/Adempiere120x60.gif differ diff --git a/serverRoot/src/web/Background.gif b/serverRoot/src/web/Background.gif new file mode 100644 index 0000000000..11988728ed Binary files /dev/null and b/serverRoot/src/web/Background.gif differ diff --git a/serverRoot/src/web/C32.gif b/serverRoot/src/web/C32.gif new file mode 100644 index 0000000000..c83d267cbd Binary files /dev/null and b/serverRoot/src/web/C32.gif differ diff --git a/serverRoot/src/web/Logo.gif b/serverRoot/src/web/Logo.gif new file mode 100644 index 0000000000..f3e3c93682 Binary files /dev/null and b/serverRoot/src/web/Logo.gif differ diff --git a/serverRoot/src/web/WEB-INF/web.xml b/serverRoot/src/web/WEB-INF/web.xml new file mode 100644 index 0000000000..295ca650dd --- /dev/null +++ b/serverRoot/src/web/WEB-INF/web.xml @@ -0,0 +1,75 @@ + + + + Adempiere Root + $Id: web.xml,v 1.1 2006/04/21 18:04:14 jjanke Exp $ + + ServerRoot + 1 + + + AdempiereMonitorFilter + Adempiere Monitor Filter + org.compiere.web.AdempiereMonitorFilter + + + AdempiereMonitorFilter + /adempiereMonitor + + + JnlpDownloadServlet + JNLP Download Servlet + jnlp.sample.servlet.JnlpDownloadServlet + + logLevel + INFORMATIONAL + + 1 + + + StatusInfo + Server Status + Adempiere Server Status Info + org.compiere.web.StatusInfo + + + AdempiereMonitor + Adempiere Monitor + Adempiere Server Monitor + org.compiere.web.AdempiereMonitor + 1 + + + JnlpDownloadServlet + *.jnlp + + + StatusInfo + /statusInfo + + + AdempiereMonitor + /adempiereMonitor/* + + + 15 + + + jar + application/x-java-archive + + + jardiff + application/x-java-archive-diff + + + jnlp + application/x-java-jnlp-file + + + adempiere.html + + diff --git a/serverRoot/src/web/adempiere.html b/serverRoot/src/web/adempiere.html new file mode 100644 index 0000000000..eaf1a29be5 --- /dev/null +++ b/serverRoot/src/web/adempiere.html @@ -0,0 +1,242 @@ + + + + Adempiere Application Home + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + +
Adempiere Logo
+
+ + + + + + + + + + +
Welcome to the Adempiere 3.1.1 Home Page!
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

+ Web Start +

+ +
+

+ + Adempiere WebStart + + +Install  Java 1.5.0 JRE (Runtime) +and press WebStart
+ +Check also WebStart Details.
+ +If you have trouble starting, try this +(or via Applet) +

+ +
+

Local Install

+
Adempiere Client ZipInstall  Java 1.5.0 JRE (Runtime); Download and extract the Adempiere Client zip file on your Client;.
+ +Start Adempiere via RUN_Adempiere
+

Web Application

+
+

Adempiere Web Application Login (alternative)

+
+

Self Service

+
Web Store - Send Request
+

Server Admin

+
+

Adempiere Server Managment + +

+
+

Adempiere Support

+
+

Wiki - Manual - IRC Channel
+ + Forums - Support Requests

+
+
+ + + + + + + + + + +
© Copyright 2006 Adempiere - All rights reserved - Adempiere License is GPL
+
+ +

 

+ + + diff --git a/serverRoot/src/web/adempiere.jnlp b/serverRoot/src/web/adempiere.jnlp new file mode 100644 index 0000000000..858d1e54d3 --- /dev/null +++ b/serverRoot/src/web/adempiere.jnlp @@ -0,0 +1,30 @@ + + + + Adempiere Client 3.1.1 $$context + ComPiere, Inc. + + + Adempiere ERP+CRM ($$context) - Smart Business Solution for Distribution and Service - globally + Adempiere ERP+CRM ($$context) + Adempiere ERP+CRM + Adempiere ERP+CRM ($$context) + + + + + + + + + + + + + + + + + diff --git a/serverRoot/src/web/adempiere_logo.gif b/serverRoot/src/web/adempiere_logo.gif new file mode 100644 index 0000000000..2f61d547e1 Binary files /dev/null and b/serverRoot/src/web/adempiere_logo.gif differ diff --git a/serverRoot/src/web/adempiere_logo.png b/serverRoot/src/web/adempiere_logo.png new file mode 100644 index 0000000000..cd78410b72 Binary files /dev/null and b/serverRoot/src/web/adempiere_logo.png differ diff --git a/serverRoot/src/web/adempiere_logo2.gif b/serverRoot/src/web/adempiere_logo2.gif new file mode 100644 index 0000000000..2f61d547e1 Binary files /dev/null and b/serverRoot/src/web/adempiere_logo2.gif differ diff --git a/serverRoot/src/web/adempiere_logo3.png b/serverRoot/src/web/adempiere_logo3.png new file mode 100644 index 0000000000..679097b795 Binary files /dev/null and b/serverRoot/src/web/adempiere_logo3.png differ diff --git a/serverRoot/src/web/applet.html b/serverRoot/src/web/applet.html new file mode 100644 index 0000000000..2173c13967 --- /dev/null +++ b/serverRoot/src/web/applet.html @@ -0,0 +1,81 @@ + + + + +Adempiere Client Applet Start + + + + +

Adempiere Client Applet Start

+

Due to Browser incompatibilities, here is a list of different ways to start the Adempiere Java Client from a Browser. +This requires that your browser supports Java.
+Applet Start Details +

+

After the Adempiere Client started DO NOT close/use this window as that will crash the application!

+ + + + + + + + + + + + + +
Microsoft IE
+ + + + + + Internet Explorer: No Java 2 SDK, Standard Edition v 1.5.0 support for APPLET + +
+ Non IE Browsers
+ + + +
Browsers supporting the EMBED tag
+ Code commented out - modify source if required!
+ +
Browsers supporting the APPLET tag
+ Code commented out - modify source if required!
+ +
+

 

+ + diff --git a/serverRoot/src/web/error.html b/serverRoot/src/web/error.html new file mode 100644 index 0000000000..da01ce500d --- /dev/null +++ b/serverRoot/src/web/error.html @@ -0,0 +1,13 @@ + + + + +Adempiere Server Error + + + +

Adempiere Server Error

+

The Adempiere Server encountered a unrecoverable error.

+

Please notify the administrator.

+ + diff --git a/serverRoot/src/web/favicon.ico b/serverRoot/src/web/favicon.ico new file mode 100644 index 0000000000..14c3ee6ebd Binary files /dev/null and b/serverRoot/src/web/favicon.ico differ diff --git a/serverRoot/src/web/footer_bar.gif b/serverRoot/src/web/footer_bar.gif new file mode 100644 index 0000000000..f55164bf37 Binary files /dev/null and b/serverRoot/src/web/footer_bar.gif differ diff --git a/serverRoot/src/web/header_banner.jpg b/serverRoot/src/web/header_banner.jpg new file mode 100644 index 0000000000..d4560783b7 Binary files /dev/null and b/serverRoot/src/web/header_banner.jpg differ diff --git a/serverRoot/src/web/header_banner.png b/serverRoot/src/web/header_banner.png new file mode 100644 index 0000000000..ec656052db Binary files /dev/null and b/serverRoot/src/web/header_banner.png differ diff --git a/serverRoot/src/web/header_bar.jpg b/serverRoot/src/web/header_bar.jpg new file mode 100644 index 0000000000..dd96b2ab75 Binary files /dev/null and b/serverRoot/src/web/header_bar.jpg differ diff --git a/serverRoot/src/web/index.html b/serverRoot/src/web/index.html new file mode 100644 index 0000000000..b980ea8473 --- /dev/null +++ b/serverRoot/src/web/index.html @@ -0,0 +1,13 @@ + + +Adempiere + + + + + +

Click here if not forwarded automatically

+ + diff --git a/serverRoot/src/web/robots.txt b/serverRoot/src/web/robots.txt new file mode 100644 index 0000000000..991b78a1d2 --- /dev/null +++ b/serverRoot/src/web/robots.txt @@ -0,0 +1,4 @@ +User-agent: * +Disallow: / +Disallow: /adempiere +Disallow: /jmx-console diff --git a/serverRoot/src/web/standard.css b/serverRoot/src/web/standard.css new file mode 100644 index 0000000000..f60bed7ed2 --- /dev/null +++ b/serverRoot/src/web/standard.css @@ -0,0 +1,151 @@ +/* Adempiere Root (c) Jorg Janke */ +/* $Id: standard.css,v 1.1 2006/04/21 18:04:14 jjanke Exp $ */ +body{ + background-color: #FFFFFF; + color: #000000; + font-size: 76%; + font-family: Verdana, Arial, sans-serif; + line-height: 1.3em; +} + +a{ + color: #3465a4; + text-decoration: none; +} + +a:hover{ + text-decoration: none; +} + +h1{ + color: #FF0000; + font-size: x-large; + margin-bottom: 10px; + margin-top: 0; +} + +h2{ + color: #000066; + font-size: large; +} + +h3{ + color: #0000CC; + font-size: medium; + font-style: normal; + font-weight: bold; +} + +h4{ + color: #6600CC; + font-size: medium; + font-style: italic; +} + +h5{ + color: #660099; + font-size: medium; + font-weight: normal; +} + +h6{ + font-size: larger; + font-weight: bold; +} + +hr{ + color: #000099; + padding-bottom: 0; + padding-top: 0; +} + +p{ + text-align: justify; +} + +th{ + background-color: #E6E6FA; + text-align: left; +} + +caption{ + color: #660099; + text-align: left; + font-style: italic; + font-weight: bolder; +} + + +.menuDetail{ + color: #660099; + font-family: Arial,Helvetica,sans-serif; + font-size: 12px; + padding-bottom: 0; + padding-left: 20px; + padding-top: 0; + text-decoration: none; +} + +.menuDetail:hover{ + color: #660099; + font-family: Arial,Helvetica,sans-serif; + font-size: 12px; + padding-bottom: 0; + padding-left: 20px; + padding-top: 0; + text-decoration: none; + background-color: #99FFFF; +} + +.menuMain{ + color: #000066; + font-family: Arial,Helvetica,sans-serif; + font-size: 16px; + text-align: left; + text-decoration: none; +} + +.menuMain:hover{ + color: #000066; + font-family: Arial,Helvetica,sans-serif; + font-size: 16px; + text-align: left; + text-decoration: none; + background-color: #99FFFF; +} + +.menuSub{ + color: #000066; + font-family: Arial,Helvetica,sans-serif; + font-size: 14px; + padding-left: 10px; + text-align: left; + text-decoration: none; +} + +.menuSub:hover{ + color: #000066; + font-family: Arial,Helvetica,sans-serif; + font-size: 14px; + padding-left: 10px; + text-align: left; + text-decoration: none; + background-color: #99FFFF; +} + +.Cerror{ +background:#FF4A4A; +} +.Cmandatory{ +background:#9DFFFF; +} +.Cbasket{ + font-size: 9px; + display: inline; +} +#imgButton{ +border-style:outset; +} +#imgButtonPressed{ +border-style:inset; +} diff --git a/serverRoot/src/web/webstart.jpg b/serverRoot/src/web/webstart.jpg new file mode 100644 index 0000000000..aa7d066107 Binary files /dev/null and b/serverRoot/src/web/webstart.jpg differ diff --git a/serverRoot/src/web/webstart_s.jpg b/serverRoot/src/web/webstart_s.jpg new file mode 100644 index 0000000000..7dc44d8c2e Binary files /dev/null and b/serverRoot/src/web/webstart_s.jpg differ diff --git a/serverRoot/src/web/zip.gif b/serverRoot/src/web/zip.gif new file mode 100644 index 0000000000..f5d41b6386 Binary files /dev/null and b/serverRoot/src/web/zip.gif differ diff --git a/serverRoot/xdoclet-build.xml b/serverRoot/xdoclet-build.xml new file mode 100644 index 0000000000..3c61bae1ad --- /dev/null +++ b/serverRoot/xdoclet-build.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +