/********************************************************************** * This file is part of iDempiere ERP Open Source * * http://www.idempiere.org * * * * Copyright (C) Contributors * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License * * as published by the Free Software Foundation; either version 2 * * of the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * * MA 02110-1301, USA. * * * * Contributors: * * - Carlos Ruiz - globalqss * **********************************************************************/ package org.globalqss.process; import java.math.BigDecimal; import java.math.MathContext; import java.math.RoundingMode; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Timestamp; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import org.adempiere.exceptions.AdempiereException; import org.adempiere.exceptions.DBException; import org.compiere.model.MAccount; import org.compiere.model.MAcctSchema; import org.compiere.model.MAcctSchemaElement; import org.compiere.model.MBPartner; import org.compiere.model.MConversionType; import org.compiere.model.MElementValue; import org.compiere.model.MJournal; import org.compiere.model.MJournalGenerator; import org.compiere.model.MJournalGeneratorLine; import org.compiere.model.MJournalGeneratorSource; import org.compiere.model.MJournalLine; import org.compiere.model.MPeriod; import org.compiere.model.MProcessPara; import org.compiere.model.MProduct; import org.compiere.model.MTable; import org.compiere.model.PO; import org.compiere.process.ProcessInfoParameter; import org.compiere.process.SvrProcess; import org.compiere.report.MReportTree; import org.compiere.util.DB; import org.compiere.util.Env; import org.compiere.util.Language; import org.compiere.util.Msg; /** * GL Journal Generator * * @author Carlos Ruiz - Quality Systems & Solutions - globalqss */ @org.adempiere.base.annotation.Process public class GLJournalGenerate extends SvrProcess { /** Processing date from/to */ private Timestamp p_ProcessingDateFrom; private Timestamp p_ProcessingDateTo; /** Date of GL Journal */ private Timestamp p_DateAcct; /** Flag is simulation */ private boolean p_IsSimulation = true; /** Document Action */ private String p_DocAction = null; /** Document No */ private String p_DocumentNo; /** BPartner to filter */ private int p_C_BPartner_ID = 0; /** Product to filter */ private int p_M_Product_ID = 0; /* The Journal Generator */ private int p_QSS_JournalGenerator_ID; /** * Prepare - e.g., get Parameters. */ @Override protected void prepare() { ProcessInfoParameter[] para = getParameter(); for (int i = 0; i < para.length; i++) { String name = para[i].getParameterName(); if (name.equals("ProcessingDate")) { p_ProcessingDateFrom = (Timestamp)para[i].getParameter(); p_ProcessingDateTo = (Timestamp)para[i].getParameter_To(); } else if (name.equals("DateAcct")) p_DateAcct = (Timestamp)para[i].getParameter(); else if (name.equals("IsSimulation")) p_IsSimulation = para[i].getParameterAsBoolean(); else if (name.equals("DocAction")) p_DocAction = (String) para[i].getParameter(); else if (name.equals("DocumentNo")) p_DocumentNo = (String) para[i].getParameter(); else if (name.equals("C_BPartner_ID")) p_C_BPartner_ID = para[i].getParameterAsInt(); else if (name.equals("M_Product_ID")) p_M_Product_ID = para[i].getParameterAsInt(); else MProcessPara.validateUnknownParameter(getProcessInfo().getAD_Process_ID(), para[i]); } p_QSS_JournalGenerator_ID = getRecord_ID(); } // prepare /** * Perform process. * @return Message * @throws Exception */ @Override protected String doIt() throws Exception { if (log.isLoggable(Level.INFO)) log.info("QSS_Journal_Generator_ID=" + p_QSS_JournalGenerator_ID + ", ProcessingDate=" + p_ProcessingDateFrom + "/" + p_ProcessingDateTo + ", DateAcct=" + p_DateAcct + ", IsSimulation=" + p_IsSimulation + ", DocAction=" + p_DocAction + ", DocumentNo=" + p_DocumentNo + ", C_BPartner_ID=" + p_C_BPartner_ID + ", M_Product_ID=" + p_M_Product_ID ); MJournalGenerator journalGenerator = new MJournalGenerator(getCtx(), p_QSS_JournalGenerator_ID, get_TrxName()); MAcctSchema as = (MAcctSchema) journalGenerator.getC_AcctSchema(); BigDecimal totalAmount = Env.ZERO; List> listDimOut = new ArrayList>(); List> listColOut = new ArrayList>(); List listDrAmtOut = new ArrayList(); List listCrAmtOut = new ArrayList(); List listAccountOut = new ArrayList(); List listLinesOut = new ArrayList(); for (MJournalGeneratorLine line : journalGenerator.getLines()) { int idxBPColumn = -1; List groupColumns = new ArrayList(); groupColumns.add("AD_Client_ID"); if (line.isCopyAllDimensions()) { for (MAcctSchemaElement element : as.getAcctSchemaElements()) { String columnName = MAcctSchemaElement.getColumnName(element.getElementType()); if (! groupColumns.contains(columnName)) groupColumns.add(columnName); } } else { if (MJournalGeneratorLine.BPDIMENSIONTYPE_Same.equals(line.getBPDimensionType())) groupColumns.add("C_BPartner_ID"); else if (MJournalGeneratorLine.BPDIMENSIONTYPE_Column.equals(line.getBPDimensionType())) { String[] parts = line.getBPColumn().split("\\."); String tablename = parts[0]; groupColumns.add(tablename + "_ID"); idxBPColumn = groupColumns.size()-1; } if (line.isSameProduct()) groupColumns.add("M_Product_ID"); } String groupBy = ""; for (String column : groupColumns) { if (groupBy.length() == 0) groupBy += column; else groupBy += ","+column; } List> listDim = new ArrayList>(); List listAmt = new ArrayList(); int p_AD_Org_ID = line.getAD_Org_ID(); if (p_AD_Org_ID == 0) p_AD_Org_ID = journalGenerator.getAD_Org_ID(); if (p_AD_Org_ID == 0) p_AD_Org_ID = Env.getAD_Org_ID(getCtx()); for (MJournalGeneratorSource source : line.getSources()) { String sql = "SELECT SUM(AmtAcctDr-AmtAcctCr), " + groupBy + " FROM Fact_Acct " + " WHERE PostingType='A' " + " AND IsActive='Y' " + " AND DateAcct BETWEEN ? AND ? " + " AND C_AcctSchema_ID=? " + " AND AD_Client_ID=? "; if (p_AD_Org_ID > 0) sql += " AND AD_Org_ID=" + p_AD_Org_ID; if (p_C_BPartner_ID > 0) sql += " AND C_BPartner_ID=" + p_C_BPartner_ID; if (p_M_Product_ID > 0) sql += " AND M_Product_ID=" + p_M_Product_ID; if (source.getGL_Category_ID() > 0) sql += " AND GL_Category_ID=" + source.getGL_Category_ID(); sql += " AND ("; /* TODO: Get all accounts below tree of source */ sql += MReportTree.getWhereClause(getCtx(), 0, MAcctSchemaElement.ELEMENTTYPE_Account, source.getC_ElementValue_ID()); sql += ")"; sql += " GROUP BY " + groupBy; PreparedStatement pstmt = null; ResultSet rs = null; try { pstmt = DB.prepareStatement(sql, get_TrxName()); pstmt.setTimestamp(1, p_ProcessingDateFrom); pstmt.setTimestamp(2, p_ProcessingDateTo); pstmt.setInt(3, journalGenerator.getC_AcctSchema_ID()); pstmt.setInt(4, getAD_Client_ID()); rs = pstmt.executeQuery(); while (rs.next()) { BigDecimal sourceAmt = rs.getBigDecimal(1); if (sourceAmt != null && sourceAmt.signum() != 0) { sourceAmt = applyMultiplierAndFactor(sourceAmt, source.getAmtMultiplier(), source.getRoundFactor()); List dimensions = new ArrayList(); int idxCol = 0; for (String column : groupColumns) { if (idxBPColumn == idxCol) { // Assign the BPDIMENSIONTYPE_Column to the output BP int bpIdOut = 0; String[] parts = line.getBPColumn().split("\\."); String tablename = parts[0]; String columnname = parts[1]; int id = rs.getInt(column); if (id > 0) { MTable table = MTable.get(getCtx(), tablename); PO po = table.getPO(id, get_TrxName()); bpIdOut = po.get_ValueAsInt(columnname); } dimensions.add(bpIdOut); } else { dimensions.add(rs.getInt(column)); } idxCol++; } if (listDim.contains(dimensions)) { int idx = listDim.indexOf(dimensions); BigDecimal dimAmt = listAmt.get(idx); dimAmt = dimAmt.add(sourceAmt); listAmt.set(idx, dimAmt); } else { listDim.add(dimensions); listAmt.add(sourceAmt); } } } } catch (SQLException e) { //log.log(Level.SEVERE, sql, getSQLException(e)); throw new DBException(e, sql); } finally { DB.close(rs, pstmt); rs = null; pstmt = null; } } /* Ready, here I have the input array with all sources to take into account, construct the output */ List columnsOut = new ArrayList(); if (line.isCopyAllDimensions()) { columnsOut = groupColumns; } else { columnsOut.add("AD_Client_ID"); if ( MJournalGeneratorLine.BPDIMENSIONTYPE_Same.equals(line.getBPDimensionType()) || MJournalGeneratorLine.BPDIMENSIONTYPE_Fixed.equals(line.getBPDimensionType()) || MJournalGeneratorLine.BPDIMENSIONTYPE_Column.equals(line.getBPDimensionType())) columnsOut.add("C_BPartner_ID"); if (line.isSameProduct()) columnsOut.add("M_Product_ID"); } for (int i = 0; i < listDim.size(); i++) { List dimensions = listDim.get(i); BigDecimal amount = listAmt.get(i); List dimensionsOut = new ArrayList(); if (amount != null && amount.signum() != 0) { amount = applyMultiplierAndFactor(amount, line.getAmtMultiplier(), line.getRoundFactor()); if (line.isCopyAllDimensions()) { dimensionsOut = dimensions; } else { /* First dimension is always AD_Client_ID */ dimensionsOut.add(dimensions.get(0)); if (MJournalGeneratorLine.BPDIMENSIONTYPE_Same.equals(line.getBPDimensionType())) { dimensionsOut.add(dimensions.get(groupColumns.indexOf("C_BPartner_ID"))); } else if (MJournalGeneratorLine.BPDIMENSIONTYPE_Fixed.equals(line.getBPDimensionType())) { dimensionsOut.add(line.getC_BPartner_ID()); } else if (MJournalGeneratorLine.BPDIMENSIONTYPE_Column.equals(line.getBPDimensionType())) { String[] parts = line.getBPColumn().split("\\."); String tablename = parts[0]; dimensionsOut.add(dimensions.get(groupColumns.indexOf(tablename + "_ID"))); } if (line.isSameProduct()) { dimensionsOut.add(dimensions.get(groupColumns.indexOf("M_Product_ID"))); } } if (line.getC_ElementValueDR_ID() > 0) { listAccountOut.add(line.getC_ElementValueDR_ID()); listDrAmtOut.add(amount); listCrAmtOut.add(Env.ZERO); listDimOut.add(dimensionsOut); listColOut.add(columnsOut); listLinesOut.add(line); totalAmount = totalAmount.add(amount); } if (line.getC_ElementValueCR_ID() > 0) { listAccountOut.add(line.getC_ElementValueCR_ID()); listDrAmtOut.add(Env.ZERO); listCrAmtOut.add(amount); listDimOut.add(dimensionsOut); listColOut.add(columnsOut); listLinesOut.add(line); totalAmount = totalAmount.subtract(amount); } if (line.getC_ElementValueDR_ID() == 0 && line.getC_ElementValueCR_ID() == 0 && line.isCopyAllDimensions()) { int accountId = dimensions.get(groupColumns.indexOf("Account_ID")); listAccountOut.add(accountId); if (amount.signum() > 0) { listDrAmtOut.add(amount); listCrAmtOut.add(Env.ZERO); totalAmount = totalAmount.add(amount); } else { listDrAmtOut.add(Env.ZERO); listCrAmtOut.add(amount.negate()); totalAmount = totalAmount.add(amount); } listDimOut.add(dimensionsOut); listColOut.add(columnsOut); listLinesOut.add(line); } } } } // generatorLines // Add adjust line if (totalAmount.signum() < 0 && journalGenerator.getC_ElementValueAdjustDR_ID() > 0) { listAccountOut.add(journalGenerator.getC_ElementValueAdjustDR_ID()); listDrAmtOut.add(totalAmount.negate()); listCrAmtOut.add(Env.ZERO); listDimOut.add(null); listColOut.add(null); listLinesOut.add(null); } else if (totalAmount.signum() > 0 && journalGenerator.getC_ElementValueAdjustCR_ID() > 0) { listAccountOut.add(journalGenerator.getC_ElementValueAdjustCR_ID()); listDrAmtOut.add(Env.ZERO); listCrAmtOut.add(totalAmount); listDimOut.add(null); listColOut.add(null); listLinesOut.add(null); } MJournal j = null; int lineNo = 10; /* Ready, here we have an array of output calculated journals, let's process them */ for (int i = 0; i < listDimOut.size(); i++) { List dimensions = listDimOut.get(i); List columnsOut = listColOut.get(i); BigDecimal dr = listDrAmtOut.get(i); BigDecimal cr = listCrAmtOut.get(i); int accountId = listAccountOut.get(i); MJournalGeneratorLine line = listLinesOut.get(i); if (p_IsSimulation) { MElementValue ev = new MElementValue(getCtx(), accountId, get_TrxName()); String msg = "Account=" + ev.getValue() + ", DR=" + dr + ", CR=" + cr; int idxcol = 0; if (columnsOut != null) { for (String col : columnsOut) { int id = dimensions.get(idxcol); if (id == 0) continue; if ("C_BPartner_ID".equals(col)) { MBPartner bp = MBPartner.get(getCtx(), id); msg += ", C_BPartner=" + bp.getValue(); } else if ("M_Product_ID".equals(col)) { MProduct pr = MProduct.get(getCtx(), id); msg += ", M_Product=" + pr.getValue(); } else if ("AD_Client_ID".equals(col)) { } else { msg += ", " + col + "=" + id; } idxcol++; } } addLog(msg); } else { /* Create journals */ if (j == null) { // * Create a GL Journal // conversion type = default j = new MJournal(getCtx(), 0, get_TrxName()); if (journalGenerator.getAD_Org_ID() > 0) j.setAD_Org_ID(journalGenerator.getAD_Org_ID()); else j.setAD_Org_ID(Env.getAD_Org_ID(getCtx())); if (j.getAD_Org_ID() == 0) { throw new AdempiereException(Msg.getMsg(Language.getBaseAD_Language(), "Org0NotAllowed")); } j.setC_Currency_ID(as.getC_Currency_ID()); j.setC_DocType_ID(journalGenerator.getC_DocType_ID()); j.setControlAmt(Env.ZERO); j.setDateAcct(p_DateAcct); int C_Period_ID = MPeriod.getC_Period_ID(getCtx(), p_DateAcct, j.getAD_Org_ID()); if (C_Period_ID > 0) j.setC_Period_ID(C_Period_ID); j.setDateDoc(p_DateAcct); j.setDescription(journalGenerator.getDescription()); j.setDocumentNo(p_DocumentNo); j.set_ValueOfColumn("GL_Category_ID", journalGenerator.getGL_Category_ID()); // can be zero j.setPostingType(journalGenerator.getPostingType()); j.setC_AcctSchema_ID(as.getC_AcctSchema_ID()); j.setC_ConversionType_ID(MConversionType.getDefault(as.getAD_Client_ID())); j.saveEx(); } // Create the journal lines MJournalLine jl = new MJournalLine(j); jl.setLine(lineNo); lineNo += 10; MAccount combination; { int AD_Client_ID = getAD_Client_ID(); int AD_Org_ID = 0; if (line != null) AD_Org_ID = line.getAD_Org_ID(); if (AD_Org_ID == 0) AD_Org_ID = journalGenerator.getAD_Org_ID(); if (AD_Org_ID == 0) AD_Org_ID = Env.getAD_Org_ID(getCtx()); int C_AcctSchema_ID = journalGenerator.getC_AcctSchema_ID(); int Account_ID = accountId; int C_SubAcct_ID = 0; int M_Product_ID = 0; int C_BPartner_ID = 0; int AD_OrgTrx_ID = 0; int C_LocFrom_ID = 0; int C_LocTo_ID = 0; int C_SalesRegion_ID = 0; int C_Project_ID = 0; int C_Campaign_ID = 0; int C_Activity_ID = 0; int User1_ID = 0; int User2_ID = 0; int UserElement1_ID = 0; int UserElement2_ID = 0; if (columnsOut != null) { int idxcol = 0; for (String col : columnsOut) { int id = dimensions.get(idxcol); if ("M_Product_ID".equals(col)) M_Product_ID = id; else if ("C_BPartner_ID".equals(col)) C_BPartner_ID = id; else if ("AD_OrgTrx_ID".equals(col)) AD_OrgTrx_ID = id; else if ("C_LocFrom_ID".equals(col)) C_LocFrom_ID = id; else if ("C_LocTo_ID".equals(col)) C_LocTo_ID = id; else if ("C_SalesRegion_ID".equals(col)) C_SalesRegion_ID = id; else if ("C_Project_ID".equals(col)) C_Project_ID = id; else if ("C_Campaign_ID".equals(col)) C_Campaign_ID = id; else if ("C_Activity_ID".equals(col)) C_Activity_ID = id; else if ("User1_ID".equals(col)) User1_ID = id; else if ("User2_ID".equals(col)) User2_ID = id; else if ("UserElement1_ID".equals(col)) UserElement1_ID = id; else if ("UserElement2_ID".equals(col)) UserElement2_ID = id; idxcol++; } } combination = MAccount.get(getCtx(), AD_Client_ID, AD_Org_ID, C_AcctSchema_ID, Account_ID, C_SubAcct_ID, M_Product_ID, C_BPartner_ID, AD_OrgTrx_ID, C_LocFrom_ID, C_LocTo_ID, C_SalesRegion_ID, C_Project_ID, C_Campaign_ID, C_Activity_ID, User1_ID, User2_ID, UserElement1_ID, UserElement2_ID, get_TrxName()); if (combination == null) throw new AdempiereException("Could not create combination"); } jl.setC_ValidCombination_ID(combination); if (line != null) jl.setDescription(line.getDescription()); jl.setAmtSourceDr(dr); jl.setAmtAcctDr(dr); jl.setAmtSourceCr(cr); jl.setAmtAcctCr(cr); jl.saveEx(); } } if (j != null && p_DocAction != null) { // DocAction if (!j.processIt(p_DocAction)) throw new AdempiereException("Couldn't set docAction: " + p_DocAction + ((org.compiere.process.DocAction) j).getProcessMsg()+ " journal"); j.saveEx(); } if (j != null) { StringBuilder msg = new StringBuilder(Msg.parseTranslation(getCtx(), "@Created@ @GL_Journal_ID@=")).append(j.getDocumentNo()); addLog(j.get_ID(), null, null, msg.toString(), MJournal.Table_ID, j.get_ID()); }else{ return "0 @GL_Journal_ID@ @Created@"; } return "@OK@"; } // doIt /** * @param sourceAmt * @param amtMultiplier * @param roundFactor */ private BigDecimal applyMultiplierAndFactor(BigDecimal sourceAmt, BigDecimal amtMultiplier, int roundFactor) { if (amtMultiplier.compareTo(Env.ONE) != 0) sourceAmt = sourceAmt.multiply(amtMultiplier, MathContext.UNLIMITED); if (roundFactor < 0) { BigDecimal divisor = BigDecimal.valueOf(Math.pow(10, -roundFactor)); sourceAmt = sourceAmt.divide(divisor, MathContext.UNLIMITED); sourceAmt = sourceAmt.setScale(0, RoundingMode.HALF_UP); sourceAmt = sourceAmt.multiply(divisor, MathContext.UNLIMITED); sourceAmt = sourceAmt.setScale(0, RoundingMode.HALF_UP); } else { sourceAmt = sourceAmt.setScale(roundFactor, RoundingMode.HALF_UP); } return sourceAmt; } } // GLJournalGenerate