/****************************************************************************** * Product: Adempiere ERP & CRM Smart Business Solution * * Copyright (C) 1999-2006 ComPiere, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify it * * under the terms version 2 of the GNU General Public License as published * * by the Free Software Foundation. This program is distributed in the hope * * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied * * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * * with this program; if not, write to the Free Software Foundation, Inc., * * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. * * For the text or an alternative of this public license, you may reach us * * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA * * or via info@compiere.org or http://www.compiere.org/license.html * *****************************************************************************/ package org.compiere.model; import java.awt.Color; import java.math.BigDecimal; import java.math.RoundingMode; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.Timestamp; import java.util.ArrayList; import java.util.List; import java.util.Properties; import java.util.logging.Level; import org.compiere.util.CLogger; import org.compiere.util.DB; import org.compiere.util.Env; import org.compiere.util.Msg; import org.compiere.util.Util; /** * Performance Goal * * @author Jorg Janke * @version $Id: MGoal.java,v 1.2 2006/07/30 00:51:03 jjanke Exp $ * * @author Teo Sarca, SC ARHIPAC SERVICE SRL *
  • BF [ 1887674 ] Deadlock when try to modify PA Goal's Measure Target *
  • BF [ 1760482 ] New Dashboard broke old functionality *
  • BF [ 1887691 ] I get NPE if the PA Goal's target is 0 */ public class MGoal extends X_PA_Goal { /** * generated serial id */ private static final long serialVersionUID = -4612113288233473730L; /** * Get User Goals (will call updateGoal for each record) * @param ctx context * @param AD_User_ID user * @return array of goals */ public static MGoal[] getUserGoals(Properties ctx, int AD_User_ID) { if (AD_User_ID < 0) return getTestGoals(ctx); ArrayList list = new ArrayList(); String sql = "SELECT * FROM PA_Goal g " + "WHERE IsActive='Y'" + " AND AD_Client_ID=?" // #1 + " AND ((AD_User_ID IS NULL AND AD_Role_ID IS NULL)" + " OR AD_User_ID=?" // #2 + " OR EXISTS (SELECT * FROM AD_User_Roles ur " + "WHERE ur.AD_User_ID=? AND g.AD_Role_ID=ur.AD_Role_ID AND ur.IsActive='Y')) " + "ORDER BY SeqNo"; PreparedStatement pstmt = null; ResultSet rs = null; try { pstmt = DB.prepareStatement (sql, null); pstmt.setInt (1, Env.getAD_Client_ID(ctx)); pstmt.setInt (2, AD_User_ID); pstmt.setInt (3, AD_User_ID); rs = pstmt.executeQuery (); while (rs.next ()) { MGoal goal = new MGoal (ctx, rs, null); goal.updateGoal(false); list.add (goal); } } catch (Exception e) { s_log.log (Level.SEVERE, sql, e); } finally { DB.close(rs, pstmt); rs = null; pstmt = null; } if (list.size() == 0) s_log.log (Level.INFO, Msg.getMsg(ctx, "FindZeroRecords")); MGoal[] retValue = new MGoal[list.size ()]; list.toArray (retValue); return retValue; } // getUserGoals /** * Get Accessible Goals ((will call updateGoal for each record) * @param ctx context * @return array of goals */ public static MGoal[] getGoals(Properties ctx) { List list = new Query(ctx,I_PA_Goal.Table_Name,null,null) .setOrderBy("SeqNo") .setApplyAccessFilter(false,true) .setOnlyActiveRecords(true) .list(); for(MGoal goal:list) goal.updateGoal(false); MGoal[] retValue = new MGoal[list.size ()]; list.toArray (retValue); return retValue; } // getGoals /** * Create Dummy Test Goals * @param ctx context * @return array of goals */ public static MGoal[] getTestGoals(Properties ctx) { MGoal[] retValue = new MGoal[4]; retValue[0] = new MGoal (ctx, "Test 1", "Description 1", new BigDecimal (1000), null); retValue[0].setMeasureActual(new BigDecimal (200)); retValue[1] = new MGoal (ctx, "Test 2", "Description 2", new BigDecimal (1000), null); retValue[1].setMeasureActual(new BigDecimal (900)); retValue[2] = new MGoal (ctx, "Test 3", "Description 3", new BigDecimal (1000), null); retValue[2].setMeasureActual(new BigDecimal (1200)); retValue[3] = new MGoal (ctx, "Test 4", "Description 4", new BigDecimal (1000), null); retValue[3].setMeasureActual(new BigDecimal (3200)); return retValue; } // getTestGoals /** * Get Goals for a performance measurement * @param ctx context * @param PA_Measure_ID performance measurement * @return goals */ public static MGoal[] getMeasureGoals (Properties ctx, int PA_Measure_ID) { ArrayList list = new ArrayList(); String sql = "SELECT * FROM PA_Goal WHERE IsActive='Y' AND PA_Measure_ID=? " + "ORDER BY SeqNo"; PreparedStatement pstmt = null; ResultSet rs = null; try { pstmt = DB.prepareStatement (sql, null); pstmt.setInt (1, PA_Measure_ID); rs = pstmt.executeQuery (); while (rs.next ()) list.add (new MGoal (ctx, rs, null)); } catch (Exception e) { s_log.log (Level.SEVERE, sql, e); } finally { DB.close(rs, pstmt); rs = null; pstmt = null; } MGoal[] retValue = new MGoal[list.size ()]; list.toArray (retValue); return retValue; } // getMeasureGoals /** * Get Multiplier for the goal's combination of Measurement Scope and Measurement Display * @param goal goal * @return multiplier value or 1 (measure display is null) or null (for MEASURESCOPE_Total and MEASUREDISPLAY_Total) */ public static BigDecimal getMultiplier (MGoal goal) { String MeasureScope = goal.getMeasureScope(); String MeasureDisplay = goal.getMeasureDisplay(); if (MeasureDisplay == null || MeasureScope.equals(MeasureDisplay)) return Env.ONE; // 1:1 if (MeasureScope.equals(MEASURESCOPE_Total) || MeasureDisplay.equals(MEASUREDISPLAY_Total)) return null; // Error BigDecimal Multiplier = null; if (MeasureScope.equals(MEASURESCOPE_Year)) { if (MeasureDisplay.equals(MEASUREDISPLAY_Quarter)) Multiplier = BigDecimal.valueOf(1.0/4.0); else if (MeasureDisplay.equals(MEASUREDISPLAY_Month)) Multiplier = BigDecimal.valueOf(1.0/12.0); else if (MeasureDisplay.equals(MEASUREDISPLAY_Week)) Multiplier = BigDecimal.valueOf(1.0/52.0); else if (MeasureDisplay.equals(MEASUREDISPLAY_Day)) Multiplier = BigDecimal.valueOf(1.0/364.0); } else if (MeasureScope.equals(MEASURESCOPE_Quarter)) { if (MeasureDisplay.equals(MEASUREDISPLAY_Year)) Multiplier = BigDecimal.valueOf(4.0); else if (MeasureDisplay.equals(MEASUREDISPLAY_Month)) Multiplier = BigDecimal.valueOf(1.0/3.0); else if (MeasureDisplay.equals(MEASUREDISPLAY_Week)) Multiplier = BigDecimal.valueOf(1.0/13.0); else if (MeasureDisplay.equals(MEASUREDISPLAY_Day)) Multiplier = BigDecimal.valueOf(1.0/91.0); } else if (MeasureScope.equals(MEASURESCOPE_Month)) { if (MeasureDisplay.equals(MEASUREDISPLAY_Year)) Multiplier = BigDecimal.valueOf(12.0); else if (MeasureDisplay.equals(MEASUREDISPLAY_Quarter)) Multiplier = BigDecimal.valueOf(3.0); else if (MeasureDisplay.equals(MEASUREDISPLAY_Week)) Multiplier = BigDecimal.valueOf(1.0/4.0); else if (MeasureDisplay.equals(MEASUREDISPLAY_Day)) Multiplier = BigDecimal.valueOf(1.0/30.0); } else if (MeasureScope.equals(MEASURESCOPE_Week)) { if (MeasureDisplay.equals(MEASUREDISPLAY_Year)) Multiplier = BigDecimal.valueOf(52.0); else if (MeasureDisplay.equals(MEASUREDISPLAY_Quarter)) Multiplier = BigDecimal.valueOf(13.0); else if (MeasureDisplay.equals(MEASUREDISPLAY_Month)) Multiplier = BigDecimal.valueOf(4.0); else if (MeasureDisplay.equals(MEASUREDISPLAY_Day)) Multiplier = BigDecimal.valueOf(1.0/7.0); } else if (MeasureScope.equals(MEASURESCOPE_Day)) { if (MeasureDisplay.equals(MEASUREDISPLAY_Year)) Multiplier = BigDecimal.valueOf(364.0); else if (MeasureDisplay.equals(MEASUREDISPLAY_Quarter)) Multiplier = BigDecimal.valueOf(91.0); else if (MeasureDisplay.equals(MEASUREDISPLAY_Month)) Multiplier = BigDecimal.valueOf(30.0); else if (MeasureDisplay.equals(MEASUREDISPLAY_Week)) Multiplier = BigDecimal.valueOf(7.0); } return Multiplier; } // getMultiplier /** Logger */ private static CLogger s_log = CLogger.getCLogger (MGoal.class); /** * UUID based Constructor * @param ctx Context * @param PA_Goal_UU UUID key * @param trxName Transaction */ public MGoal(Properties ctx, String PA_Goal_UU, String trxName) { super(ctx, PA_Goal_UU, trxName); if (Util.isEmpty(PA_Goal_UU)) setInitialDefaults(); } /** * Standard Constructor * @param ctx context * @param PA_Goal_ID id * @param trxName trx */ public MGoal (Properties ctx, int PA_Goal_ID, String trxName) { super (ctx, PA_Goal_ID, trxName); if (PA_Goal_ID == 0) setInitialDefaults(); } // MGoal /** * Set the initial defaults for a new record */ private void setInitialDefaults() { setSeqNo (0); setIsSummary (false); setMeasureScope (MEASUREDISPLAY_Year); setGoalPerformance (Env.ZERO); setRelativeWeight (Env.ONE); setMeasureTarget (Env.ZERO); setMeasureActual (Env.ZERO); } /** * Load Constructor * @param ctx context * @param rs result set * @param trxName trx */ public MGoal (Properties ctx, ResultSet rs, String trxName) { super (ctx, rs, trxName); } // MGoal /** * @param ctx context * @param Name Name * @param Description Description * @param MeasureTarget target * @param trxName trx */ public MGoal (Properties ctx, String Name, String Description, BigDecimal MeasureTarget, String trxName) { super (ctx, 0, trxName); setName(Name); setDescription(Description); setMeasureTarget(MeasureTarget); } // MGoal /** Restrictions */ private MGoalRestriction[] m_restrictions = null; /** Performance Color */ private Color m_color = null; /** * Get Restriction Lines * @param reload true to reload data * @return array of lines */ public MGoalRestriction[] getRestrictions (boolean reload) { if (m_restrictions != null && !reload) return m_restrictions; ArrayList list = new ArrayList(); // String sql = "SELECT * FROM PA_GoalRestriction " + "WHERE PA_Goal_ID=? AND IsActive='Y' " + "ORDER BY Org_ID, C_BPartner_ID, M_Product_ID"; PreparedStatement pstmt = null; ResultSet rs = null; try { pstmt = DB.prepareStatement (sql, get_TrxName()); pstmt.setInt (1, getPA_Goal_ID()); rs = pstmt.executeQuery (); while (rs.next ()) list.add (new MGoalRestriction (getCtx(), rs, get_TrxName())); } catch (Exception e) { log.log (Level.SEVERE, sql, e); } finally { DB.close(rs, pstmt); rs = null; pstmt = null; } // m_restrictions = new MGoalRestriction[list.size ()]; list.toArray (m_restrictions); return m_restrictions; } // getRestrictions /** * Get Measure * @return measure or null */ public MMeasure getMeasure() { if (getPA_Measure_ID() != 0) return MMeasure.get(getPA_Measure_ID()); return null; } // getMeasure /** * Update/save measurement for Goals * @param force force to update goal even if it has not reach the next update interval (default is 30 minutes interval) * @return true if updated */ public boolean updateGoal(boolean force) { if (log.isLoggable(Level.CONFIG)) log.config("Force=" + force); if (Env.isReadOnlySession()) return false; MMeasure measure = MMeasure.get(getPA_Measure_ID()); boolean isUpdateByInterfal = false; if (getDateLastRun() != null){ // default 30 minute 1800000 long interval = MSysConfig.getIntValue(MSysConfig.ZK_DASHBOARD_PERFORMANCE_REFRESH_INTERVAL, 1800000, Env.getAD_Client_ID(Env.getCtx())); isUpdateByInterfal = (System.currentTimeMillis() - getDateLastRun().getTime()) > interval; } if (force || getDateLastRun() == null || isUpdateByInterfal) { measure = new MMeasure(Env.getCtx(), measure, get_TrxName()); if (measure.updateGoals()) // saves { load(get_ID(), get_TrxName()); return true; } } return false; } // updateGoal /** * Set Actual Measurement value * @param MeasureActual actual */ @Override public void setMeasureActual (BigDecimal MeasureActual) { if (MeasureActual == null) return; super.setMeasureActual (MeasureActual); setDateLastRun(new Timestamp(System.currentTimeMillis())); setGoalPerformance(); } // setMeasureActual /** * Calculate Performance Goal */ public void setGoalPerformance () { BigDecimal MeasureTarget = getMeasureTarget(); BigDecimal MeasureActual = getMeasureActual(); BigDecimal GoalPerformance = Env.ZERO; if (MeasureTarget.signum() != 0) GoalPerformance = MeasureActual.divide(MeasureTarget, 6, RoundingMode.HALF_UP); super.setGoalPerformance (GoalPerformance); m_color = null; } // setGoalPerformance /** * Get Goal Performance value as Double * @return goal performance value */ public double getGoalPerformanceDouble() { BigDecimal bd = getGoalPerformance(); return bd.doubleValue(); } // getGoalPerformanceDouble /** * Get Goal Performance value in Percent * @return goal performance value in percent (i.e 5 percent if value is 0.05) */ public int getPercent() { BigDecimal bd = getGoalPerformance().multiply(Env.ONEHUNDRED); return bd.intValue(); } // getPercent /** * Get Color * @return color - white if no target */ public Color getColor() { if (m_color == null) { if (getMeasureTarget().signum() == 0) m_color = Color.white; else m_color = MColorSchema.getColor(getCtx(), getPA_ColorSchema_ID(), getPercent()); } return m_color; } // getColor /** * Get the color schema for this goal. * @return the color schema */ public MColorSchema getColorSchema() { return MColorSchema.getCopy(getCtx(), getPA_ColorSchema_ID(), get_TrxName()); } /** * Get Measure Display * @return Measure Display (MEASUREDISPLAY_*) */ public String getMeasureDisplay () { String s = super.getMeasureDisplay (); if (s == null) { if (MEASURESCOPE_Week.equals(getMeasureScope())) s = MEASUREDISPLAY_Week; else if (MEASURESCOPE_Day.equals(getMeasureScope())) s = MEASUREDISPLAY_Day; else s = MEASUREDISPLAY_Month; } return s; } // getMeasureDisplay /** * Get Measure Display Text * @return Measure Display Text for X axis */ public String getXAxisText () { MMeasure measure = getMeasure(); if (measure != null && MMeasure.MEASUREDATATYPE_StatusQtyAmount.equals(measure.getMeasureDataType())) { if (MMeasure.MEASURETYPE_Request.equals(measure.getMeasureType())) return Msg.getElement(getCtx(), "R_Status_ID"); if (MMeasure.MEASURETYPE_Project.equals(measure.getMeasureType())) return Msg.getElement(getCtx(), "C_Phase_ID"); } String value = getMeasureDisplay(); String display = MRefList.getListName(getCtx(), MEASUREDISPLAY_AD_Reference_ID, value); return display==null ? value : display; } // getMeasureDisplayText /** * Goal has Target * @return true if has measurement target value */ public boolean isTarget() { return getMeasureTarget().signum() != 0; } // isTarget /** * String Representation * @return info */ @Override public String toString () { StringBuilder sb = new StringBuilder ("MGoal["); sb.append (get_ID ()) .append ("-").append (getName()) .append(",").append(getGoalPerformance()) .append ("]"); return sb.toString (); } // toString /** * Before Save * @param newRecord new * @return true */ @Override protected boolean beforeSave (boolean newRecord) { // Measure required if nor Summary if (!isSummary() && getPA_Measure_ID() == 0) { log.saveError("FillMandatory", Msg.getElement(getCtx(), "PA_Measure_ID")); return false; } if (isSummary() && getPA_Measure_ID() != 0) setPA_Measure_ID(0); // User/Role Check if ((newRecord || is_ValueChanged("AD_User_ID") || is_ValueChanged("AD_Role_ID")) && getAD_User_ID() != 0) { MUser user = MUser.get(getCtx(), getAD_User_ID()); MRole[] roles = user.getRoles(getAD_Org_ID()); if (roles.length == 0) // No Role setAD_Role_ID(0); else if (roles.length == 1) // One setAD_Role_ID(roles[0].getAD_Role_ID()); else { int AD_Role_ID = getAD_Role_ID(); if (AD_Role_ID != 0) // validate { boolean found = false; for (int i = 0; i < roles.length; i++) { if (AD_Role_ID == roles[i].getAD_Role_ID()) { found = true; break; } } if (!found) AD_Role_ID = 0; } if (AD_Role_ID == 0) // set to first one setAD_Role_ID(roles[0].getAD_Role_ID()); } // multiple roles } // user check return true; } // beforeSave /** * After Save * @param newRecord new * @param success success * @return true */ @Override protected boolean afterSave (boolean newRecord, boolean success) { if (!success) return success; // Update Goal if Target / Scope Changed if (newRecord || is_ValueChanged("MeasureTarget") || is_ValueChanged("MeasureScope")) updateGoal(true); return success; } } // MGoal