/****************************************************************************** * 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.process; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.Timestamp; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.logging.Level; import org.compiere.model.MCommission; import org.compiere.model.MCommissionAmt; import org.compiere.model.MCommissionDetail; import org.compiere.model.MCommissionLine; import org.compiere.model.MCommissionRun; import org.compiere.model.MCurrency; import org.compiere.model.MProcessPara; import org.compiere.model.MUser; import org.compiere.util.AdempiereSystemError; import org.compiere.util.AdempiereUserError; import org.compiere.util.DB; import org.compiere.util.DisplayType; import org.compiere.util.Language; /** * Commission Calculation * * @author Jorg Janke * @version $Id: CommissionCalc.java,v 1.3 2006/09/25 00:59:41 jjanke Exp $ */ @org.adempiere.base.annotation.Process public class CommissionCalc extends SvrProcess { protected Timestamp p_StartDate; // protected Timestamp m_EndDate; protected MCommission m_com; // /** * Prepare - e.g., get Parameters. */ protected void prepare() { ProcessInfoParameter[] para = getParameter(); for (int i = 0; i < para.length; i++) { String name = para[i].getParameterName(); if (para[i].getParameter() == null) ; else if (name.equals("StartDate")) p_StartDate = (Timestamp)para[i].getParameter(); else MProcessPara.validateUnknownParameter(getProcessInfo().getAD_Process_ID(), para[i]); } } // prepare /** * Perform process. * @return Message (text with variables) * @throws Exception if not successful */ protected String doIt() throws Exception { checkParameters(); setStartEndDate(); MCommissionRun comRun = createCommissionRun(); for (MCommissionLine commissionLine : m_com.getLines()) { // Amt for Line - Updated By Trigger MCommissionAmt comAmt = new MCommissionAmt(comRun, commissionLine.getC_CommissionLine_ID()); comAmt.saveEx(); String sql = getCommissionCalculationSQL(commissionLine); if (log.isLoggable(Level.FINE)) log.fine("Line=" + commissionLine.getLine() + " - " + sql); createDetail(sql, comAmt); comAmt.calculateCommission(); comAmt.saveEx(); } // for all commission lines // Save Last Run m_com.setDateLastRun (p_StartDate); m_com.saveEx(); StringBuilder msgreturn = new StringBuilder("@C_CommissionRun_ID@ = ").append(comRun.getDocumentNo()) .append(" - ").append(comRun.getDescription()); addLog(comRun.getC_CommissionRun_ID(), null, null, msgreturn.toString(), MCommissionRun.Table_ID, comRun.getC_CommissionRun_ID()); return "@OK@"; } // doIt /** * Check if the process parameters are valid * @throws Exception if the commission record is not valid */ protected void checkParameters() { if (log.isLoggable(Level.INFO)) log.info("C_Commission_ID=" + getRecord_ID() + ", StartDate=" + p_StartDate); if (p_StartDate == null) p_StartDate = new Timestamp (System.currentTimeMillis()); m_com = new MCommission (getCtx(), getRecord_ID(), get_TrxName()); if (m_com.get_ID() == 0) throw new AdempiereUserError ("No Commission"); } /** * Creates the Commission run with the parameter start date * and sets the description * @return MCommissionRun */ protected MCommissionRun createCommissionRun() { MCommissionRun comRun = new MCommissionRun(m_com); comRun.setStartDate(p_StartDate); comRun.setDescription(getCommissionRunDescription()); comRun.saveEx(); return comRun; } /** * Returns the String that will be used for the description column * in the Commission Run * @return Description String */ protected String getCommissionRunDescription() { // 01-Jan-2000 - 31-Jan-2001 - USD SimpleDateFormat format = DisplayType.getDateFormat(DisplayType.Date); StringBuilder description = new StringBuilder().append(format.format(p_StartDate)) .append(" - ").append(format.format(m_EndDate)) .append(" - ").append(MCurrency.getISO_Code(getCtx(), m_com.getC_Currency_ID())); return description.toString(); } /** * Creates the SQL statement string that is used to generate the * Commission detail records * @param commissionLine MCommissionLine * @return SQL statement */ protected String getCommissionCalculationSQL(MCommissionLine commissionLine) { StringBuilder sql = new StringBuilder(); if (MCommission.DOCBASISTYPE_Receipt.equals(m_com.getDocBasisType())) { sql.append(getPaymentCommissionSQL()); } else if (MCommission.DOCBASISTYPE_Order.equals(m_com.getDocBasisType())) { sql.append(getOrderCommissionSQL()); } else // Invoice Basis { sql.append(getInvoiceCommissionSQL()); } sql.append(getCommissionLineWhereClause(commissionLine)); // Grouping if (!m_com.isListDetails()) sql.append(" GROUP BY h.C_Currency_ID"); return sql.toString(); } /** * Creates the SQL statement for payments (C_Payment) * @return SQL statement */ protected String getPaymentCommissionSQL() { StringBuilder sql = new StringBuilder(); if (m_com.isListDetails()) { sql.append("SELECT h.C_Currency_ID, CASE WHEN h.GrandTotal <> 0 ") .append(" THEN (l.LineNetAmt*al.Amount/h.GrandTotal) ") .append(" ELSE 0 END AS Amt,") .append(" CASE WHEN h.GrandTotal <> 0 ") .append(" THEN (l.QtyInvoiced*al.Amount/h.GrandTotal) ") .append(" ELSE 0 END AS Qty,") .append(" NULL, l.C_InvoiceLine_ID, p.DocumentNo||'_'||h.DocumentNo,") .append(" COALESCE(prd.Value,l.Description), h.DateInvoiced ") .append("FROM C_Payment p") .append(" INNER JOIN C_AllocationLine al ON (p.C_Payment_ID=al.C_Payment_ID)") .append(" INNER JOIN C_Invoice h ON (al.C_Invoice_ID = h.C_Invoice_ID)") .append(" INNER JOIN C_InvoiceLine l ON (h.C_Invoice_ID = l.C_Invoice_ID) ") .append(" LEFT OUTER JOIN M_Product prd ON (l.M_Product_ID = prd.M_Product_ID) ") .append("WHERE p.DocStatus IN ('CL','CO','RE')") .append(" AND h.IsSOTrx='Y'") .append(" AND p.AD_Client_ID = ?") .append(" AND p.DateTrx BETWEEN ? AND ?"); } else { sql.append("SELECT h.C_Currency_ID, ") .append(" SUM(CASE WHEN h.GrandTotal <> 0 ") .append(" THEN l.LineNetAmt*al.Amount/h.GrandTotal ELSE 0 END) AS Amt,") .append(" SUM(CASE WHEN h.GrandTotal <> 0 ") .append(" THEN l.QtyInvoiced*al.Amount/h.GrandTotal ELSE 0 END) AS Qty,") .append(" NULL, NULL, NULL, NULL, MAX(h.DateInvoiced) ") .append("FROM C_Payment p") .append(" INNER JOIN C_AllocationLine al ON (p.C_Payment_ID=al.C_Payment_ID)") .append(" INNER JOIN C_Invoice h ON (al.C_Invoice_ID = h.C_Invoice_ID)") .append(" INNER JOIN C_InvoiceLine l ON (h.C_Invoice_ID = l.C_Invoice_ID) ") .append("WHERE p.DocStatus IN ('CL','CO','RE')") .append(" AND h.IsSOTrx='Y'") .append(" AND p.AD_Client_ID = ?") .append(" AND p.DateTrx BETWEEN ? AND ?"); } return sql.toString(); } /** * Creates the SQL statement for orders (C_Order) * @return SQL statement */ protected String getOrderCommissionSQL() { StringBuilder sql = new StringBuilder(); if (m_com.isListDetails()) { sql.append("SELECT h.C_Currency_ID, l.LineNetAmt, l.QtyOrdered, ") .append("l.C_OrderLine_ID, NULL, h.DocumentNo,") .append(" COALESCE(prd.Value,l.Description),h.DateOrdered ") .append("FROM C_Order h") .append(" INNER JOIN C_OrderLine l ON (h.C_Order_ID = l.C_Order_ID)") .append(" LEFT OUTER JOIN M_Product prd ON (l.M_Product_ID = prd.M_Product_ID) ") .append("WHERE h.DocStatus IN ('CL','CO')") .append(" AND h.IsSOTrx='Y'") .append(" AND h.AD_Client_ID = ?") .append(" AND h.DateOrdered BETWEEN ? AND ?"); } else { sql.append("SELECT h.C_Currency_ID, SUM(l.LineNetAmt) AS Amt,") .append(" SUM(l.QtyOrdered) AS Qty, ") .append("NULL, NULL, NULL, NULL, MAX(h.DateOrdered) ") .append("FROM C_Order h") .append(" INNER JOIN C_OrderLine l ON (h.C_Order_ID = l.C_Order_ID) ") .append("WHERE h.DocStatus IN ('CL','CO')") .append(" AND h.IsSOTrx='Y'") .append(" AND h.AD_Client_ID = ?") .append(" AND h.DateOrdered BETWEEN ? AND ?"); } return sql.toString(); } /** * Creates the SQL statement for invoices (C_Invoice) * @return SQL statement */ protected String getInvoiceCommissionSQL() { StringBuilder sql = new StringBuilder(); if (m_com.isListDetails()) { sql.append("SELECT h.C_Currency_ID, l.LineNetAmt, l.QtyInvoiced, ") .append("NULL, l.C_InvoiceLine_ID, h.DocumentNo,") .append(" COALESCE(prd.Value,l.C_InvoiceLine_Description),h.DateInvoiced ") .append("FROM RV_C_Invoice h") .append(" INNER JOIN RV_C_InvoiceLine l ON (h.C_Invoice_ID = l.C_Invoice_ID)") .append(" LEFT OUTER JOIN M_Product prd ON (l.M_Product_ID = prd.M_Product_ID) ") .append("WHERE h.DocStatus IN ('CL','CO','RE')") .append(" AND h.IsSOTrx='Y'") .append(" AND h.AD_Client_ID = ?") .append(" AND h.DateInvoiced BETWEEN ? AND ?"); } else { sql.append("SELECT h.C_Currency_ID, SUM(l.LineNetAmt) AS Amt,") .append(" SUM(l.QtyInvoiced) AS Qty, ") .append("NULL, NULL, NULL, NULL, MAX(h.DateInvoiced) ") .append("FROM RV_C_Invoice h") .append(" INNER JOIN RV_C_InvoiceLine l ON (h.C_Invoice_ID = l.C_Invoice_ID) ") .append("WHERE h.DocStatus IN ('CL','CO','RE')") .append(" AND h.IsSOTrx='Y'") .append(" AND h.AD_Client_ID = ?") .append(" AND h.DateInvoiced BETWEEN ? AND ?"); } return sql.toString(); } /** * Creates the SQL WHERE clause based on the field values * defined in the Commission Lien record * @return SQL statement */ protected String getCommissionLineWhereClause(MCommissionLine commissionLine) { StringBuilder sql = new StringBuilder(); // CommissionOrders/Invoices if (commissionLine.isCommissionOrders()) { MUser[] users = MUser.getOfBPartner(getCtx(), m_com.getC_BPartner_ID(), get_TrxName()); if (users == null || users.length == 0) throw new AdempiereUserError ("Commission Business Partner has no Users/Contact"); if (users.length == 1) { int SalesRep_ID = users[0].getAD_User_ID(); sql.append(" AND h.SalesRep_ID=").append(SalesRep_ID); } else { log.warning("Not 1 User/Contact for C_BPartner_ID=" + m_com.getC_BPartner_ID() + " but " + users.length); sql.append(" AND h.SalesRep_ID IN (SELECT AD_User_ID FROM AD_User WHERE C_BPartner_ID=") .append(m_com.getC_BPartner_ID()).append(")"); } } // Organization if (commissionLine.getOrg_ID() != 0) sql.append(" AND h.AD_Org_ID=").append(commissionLine.getOrg_ID()); // BPartner if (commissionLine.getC_BPartner_ID() != 0) sql.append(" AND h.C_BPartner_ID=").append(commissionLine.getC_BPartner_ID()); // BPartner Group if (commissionLine.getC_BP_Group_ID() != 0) sql.append(" AND h.C_BPartner_ID IN ") .append("(SELECT C_BPartner_ID FROM C_BPartner WHERE C_BP_Group_ID=").append(commissionLine.getC_BP_Group_ID()).append(")"); // Sales Region if (commissionLine.getC_SalesRegion_ID() != 0) sql.append(" AND h.C_BPartner_Location_ID IN ") .append("(SELECT C_BPartner_Location_ID FROM C_BPartner_Location WHERE C_SalesRegion_ID=").append(commissionLine.getC_SalesRegion_ID()).append(")"); // Product if (commissionLine.getM_Product_ID() != 0) sql.append(" AND l.M_Product_ID=").append(commissionLine.getM_Product_ID()); // Product Category if (commissionLine.getM_Product_Category_ID() != 0) sql.append(" AND l.M_Product_ID IN ") .append("(SELECT M_Product_ID FROM M_Product WHERE M_Product_Category_ID=").append(commissionLine.getM_Product_Category_ID()).append(")"); // Payment Rule if (commissionLine.getPaymentRule() != null) sql.append(" AND h.PaymentRule='").append(commissionLine.getPaymentRule()).append("'"); return sql.toString(); } /** * Set Start and End Date */ protected void setStartEndDate() { GregorianCalendar cal = new GregorianCalendar(Language.getLoginLanguage().getLocale()); cal.setTimeInMillis(p_StartDate.getTime()); cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); cal.set(Calendar.MILLISECOND, 0); // Yearly if (MCommission.FREQUENCYTYPE_Yearly.equals(m_com.getFrequencyType())) { cal.set(Calendar.DAY_OF_YEAR, 1); p_StartDate = new Timestamp (cal.getTimeInMillis()); // cal.add(Calendar.YEAR, 1); cal.add(Calendar.DAY_OF_YEAR, -1); m_EndDate = new Timestamp (cal.getTimeInMillis()); } // Quarterly else if (MCommission.FREQUENCYTYPE_Quarterly.equals(m_com.getFrequencyType())) { cal.set(Calendar.DAY_OF_MONTH, 1); int month = cal.get(Calendar.MONTH); if (month < Calendar.APRIL) cal.set(Calendar.MONTH, Calendar.JANUARY); else if (month < Calendar.JULY) cal.set(Calendar.MONTH, Calendar.APRIL); else if (month < Calendar.OCTOBER) cal.set(Calendar.MONTH, Calendar.JULY); else cal.set(Calendar.MONTH, Calendar.OCTOBER); p_StartDate = new Timestamp (cal.getTimeInMillis()); // cal.add(Calendar.MONTH, 3); cal.add(Calendar.DAY_OF_YEAR, -1); m_EndDate = new Timestamp (cal.getTimeInMillis()); } // Weekly else if (MCommission.FREQUENCYTYPE_Weekly.equals(m_com.getFrequencyType())) { cal.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY); p_StartDate = new Timestamp (cal.getTimeInMillis()); // cal.add(Calendar.DAY_OF_YEAR, 7); m_EndDate = new Timestamp (cal.getTimeInMillis()); } // Monthly else { cal.set(Calendar.DAY_OF_MONTH, 1); p_StartDate = new Timestamp (cal.getTimeInMillis()); // cal.add(Calendar.MONTH, 1); cal.add(Calendar.DAY_OF_YEAR, -1); m_EndDate = new Timestamp (cal.getTimeInMillis()); } if (log.isLoggable(Level.FINE)) log.fine("setStartEndDate = " + p_StartDate + " - " + m_EndDate); } // setStartEndDate /** * Create Commission Detail * @param sql sql statement * @param comAmt parent * @throws Exception */ protected void createDetail (String sql, MCommissionAmt comAmt) throws Exception { try (PreparedStatement pstmt = DB.prepareStatement(sql, get_TrxName());) { pstmt.setInt(1, m_com.getAD_Client_ID()); pstmt.setTimestamp(2, p_StartDate); pstmt.setTimestamp(3, m_EndDate); ResultSet rs = pstmt.executeQuery(); while (rs.next()) { // CommissionAmount, C_Currency_ID, Amt, Qty, MCommissionDetail cd = new MCommissionDetail (comAmt, rs.getInt(1), rs.getBigDecimal(2), rs.getBigDecimal(3)); // C_OrderLine_ID, C_InvoiceLine_ID, cd.setLineIDs(rs.getInt(4), rs.getInt(5)); // Reference, Info, String s = rs.getString(6); if (s != null) cd.setReference(s); s = rs.getString(7); if (s != null) cd.setInfo(s); // Date Timestamp date = rs.getTimestamp(8); cd.setConvertedAmt(date); // if (!cd.save()) throw new IllegalArgumentException ("CommissionCalc - Detail Not saved"); } } catch (Exception e) { throw new AdempiereSystemError("System Error: " + e.getLocalizedMessage(), e); } } // createDetail } // CommissionCalc