/****************************************************************************** * 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.math.BigDecimal; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Timestamp; import java.util.Properties; import java.util.logging.Level; import org.compiere.util.CCache; import org.compiere.util.CLogger; import org.compiere.util.DB; import org.compiere.util.Env; /** * Product Cost Model. * Summarizes Info in MCost. * * @author Jorg Janke * @version $Id: ProductCost.java,v 1.3 2006/07/30 00:51:03 jjanke Exp $ */ public class ProductCost { /** * Constructor * @param ctx context * @param M_Product_ID product * @param M_AttributeSetInstance_ID asi * @param trxName trx */ public ProductCost (Properties ctx, int M_Product_ID, int M_AttributeSetInstance_ID, String trxName) { m_M_Product_ID = M_Product_ID; if (m_M_Product_ID != 0) m_product = MProduct.get(ctx, M_Product_ID); m_M_AttributeSetInstance_ID = M_AttributeSetInstance_ID; m_trxName = trxName; } // ProductCost /** Valid Combination Cache */ private static CCache s_valid_comb_cache = new CCache("ProductCost_ValidCombination", 40); /** Default Valid Compbination Cache */ private static CCache s_default_valid_comb_cache = new CCache("ProductCost_DefaultValidCombination", 40); /** The ID */ private int m_M_Product_ID = 0; /** ASI */ private int m_M_AttributeSetInstance_ID = 0; /** The Product */ private MProduct m_product = null; /** Transaction */ private String m_trxName = null; private int m_C_UOM_ID = 0; private BigDecimal m_qty = Env.ZERO; /** Logger */ private static CLogger log = CLogger.getCLogger (ProductCost.class); /** * Get Product * @return Product might be null */ public MProduct getProduct() { return m_product; } // getProduct /** * Is this a Service * @return true if service */ public boolean isService() { if (m_product != null) return m_product.isService(); return false; } // isService /** * 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; } else m_C_UOM_ID = C_UOM_ID; } // setQty /** * Get quantity in storage UOM * @return qty */ public BigDecimal getQty() { return m_qty; } /** Product Revenue Acct */ public static final int ACCTTYPE_P_Revenue = 1; /** Product Expense Acct */ public static final int ACCTTYPE_P_Expense = 2; /** Product Asset Acct */ public static final int ACCTTYPE_P_Asset = 3; /** Product COGS Acct */ public static final int ACCTTYPE_P_Cogs = 4; /** Purchase Price Variance */ public static final int ACCTTYPE_P_PPV = 5; /** Invoice Price Variance */ public static final int ACCTTYPE_P_IPV = 6; /** Trade Discount Revenue */ public static final int ACCTTYPE_P_TDiscountRec = 7; /** Trade Discount Costs */ public static final int ACCTTYPE_P_TDiscountGrant = 8; /** Cost Adjustment */ public static final int ACCTTYPE_P_CostAdjustment = 9; /** Inventory Clearing */ public static final int ACCTTYPE_P_InventoryClearing = 10; /** Work in Process */ public static final int ACCTTYPE_P_WorkInProcess = 11; /** Method Change Variance */ public static final int ACCTTYPE_P_MethodChangeVariance = 12; /** Material Usage Variance */ public static final int ACCTTYPE_P_UsageVariance = 13; /** Material Rate Variance */ public static final int ACCTTYPE_P_RateVariance = 14; /** Mix Variance */ public static final int ACCTTYPE_P_MixVariance = 15; /** Floor Stock */ public static final int ACCTTYPE_P_FloorStock = 16; /** Cost Production */ public static final int ACCTTYPE_P_CostOfProduction = 17; /** Labor */ public static final int ACCTTYPE_P_Labor = 18; /** Burden */ public static final int ACCTTYPE_P_Burden = 19; /** Outside Processing */ public static final int ACCTTYPE_P_OutsideProcessing = 20; /** Outside Overhead */ public static final int ACCTTYPE_P_Overhead = 21; /** Outside Processing */ public static final int ACCTTYPE_P_Scrap = 22; /** Average costing variance */ public static final int ACCTTYPE_P_AverageCostVariance = 23; /** Landed cost clearing **/ public static final int ACCTTYPE_P_LandedCostClearing = 24; /** * Get Account from Product * * @param AcctType see ACCTTYPE_* (1..8) * @param as Accounting Schema * @return Requested Product Account */ public MAccount getAccount(int AcctType, MAcctSchema as) { if (AcctType < ACCTTYPE_P_Revenue || AcctType > ACCTTYPE_P_LandedCostClearing) return null; // No Product - get Default from Product Category if (m_M_Product_ID == 0) return getAccountDefault(AcctType, as); String key = m_M_Product_ID + "_" + as.getC_AcctSchema_ID() + "_" + AcctType; Integer validCombination_ID = s_valid_comb_cache.get(key); if(validCombination_ID == null || validCombination_ID == 0) { String sql = "SELECT P_Revenue_Acct, P_Expense_Acct, P_Asset_Acct, P_Cogs_Acct, " // 1..4 + "P_PurchasePriceVariance_Acct, P_InvoicePriceVariance_Acct, " // 5..6 + "P_TradeDiscountRec_Acct, P_TradeDiscountGrant_Acct," // 7..8 + "P_CostAdjustment_Acct, P_InventoryClearing_Acct," // 9..10 + "P_WIP_Acct,P_MethodChangeVariance_Acct,P_UsageVariance_Acct," // 11.12.13 + "P_RateVariance_Acct,P_MixVariance_Acct,P_FloorStock_Acct," // 14.15.16 + "P_CostOfProduction_Acct,P_Labor_Acct,P_Burden_Acct,P_OutsideProcessing_Acct," // 17.18,19,20 + "P_Overhead_Acct,P_Scrap_Acct,P_AverageCostVariance_Acct," // 21,23 + "P_LandedCostClearing_Acct " // 24 + "FROM M_Product_Acct " + "WHERE M_Product_ID=? AND C_AcctSchema_ID=?"; // validCombination_ID = 0; PreparedStatement pstmt = null; ResultSet rs = null; try { pstmt = DB.prepareStatement(sql, m_trxName); pstmt.setInt(1, m_M_Product_ID); pstmt.setInt(2, as.getC_AcctSchema_ID()); rs = pstmt.executeQuery(); if (rs.next()) validCombination_ID = rs.getInt(AcctType); } catch (SQLException e) { log.log(Level.SEVERE, sql, e); } finally { DB.close(rs, pstmt); rs = null; pstmt = null; } if (validCombination_ID != 0) s_valid_comb_cache.put(key, validCombination_ID); } if (validCombination_ID == 0) return null; return MAccount.get(as.getCtx(), validCombination_ID); } // getAccount /** * Get Account from Default Product Category * * @param AcctType see ACCTTYPE_* (1..8) * @param as accounting schema * @return Requested Product Account */ public MAccount getAccountDefault (int AcctType, MAcctSchema as) { if (AcctType < ACCTTYPE_P_Revenue || AcctType > ACCTTYPE_P_LandedCostClearing) return null; String key = as.getC_AcctSchema_ID()+ "_" + AcctType; Integer validCombination_ID = s_default_valid_comb_cache.get(key); if(validCombination_ID == null || validCombination_ID == 0) { String sql = "SELECT P_Revenue_Acct, P_Expense_Acct, P_Asset_Acct, P_Cogs_Acct, " + "P_PurchasePriceVariance_Acct, P_InvoicePriceVariance_Acct, " + "P_TradeDiscountRec_Acct, P_TradeDiscountGrant_Acct, " + "P_CostAdjustment_Acct, P_InventoryClearing_Acct, " + "P_WIP_Acct,P_MethodChangeVariance_Acct,P_UsageVariance_Acct," // 11.12.13 + "P_RateVariance_Acct,P_MixVariance_Acct,P_FloorStock_Acct," // 14.15.16 + "P_CostOfProduction_Acct,P_Labor_Acct,P_Burden_Acct,P_OutsideProcessing_Acct," // 17.18,19,20 + "P_Overhead_Acct,P_Scrap_Acct,P_AverageCostVariance_Acct," // 21,23 + "P_LandedCostClearing_Acct " // 24 + "FROM M_Product_Category pc, M_Product_Category_Acct pca " + "WHERE pc.M_Product_Category_ID=pca.M_Product_Category_ID" + " AND pca.C_AcctSchema_ID=? " + "ORDER BY pc.IsDefault DESC, pc.Created"; // validCombination_ID = 0; PreparedStatement pstmt = null; ResultSet rs = null; try { pstmt = DB.prepareStatement(sql, m_trxName); pstmt.setInt(1, as.getC_AcctSchema_ID()); rs = pstmt.executeQuery(); if (rs.next()) validCombination_ID = rs.getInt(AcctType); } catch (SQLException e) { log.log(Level.SEVERE, sql, e); } finally { DB.close(rs, pstmt); rs = null; pstmt = null; } if (validCombination_ID != 0) s_default_valid_comb_cache.put(key, validCombination_ID); } if (validCombination_ID == 0) return null; return MAccount.get(as.getCtx(), validCombination_ID); } // getAccountDefault /** * Get Total Costs (amt*qty) in Accounting Schema Currency * @param as accounting schema * @param AD_Org_ID trx org * @param costingMethod if null uses Accounting Schema - AcctSchema.COSTINGMETHOD_* * @param C_OrderLine_ID optional order line * @param zeroCostsOK zero/no costs are OK * @return cost or null, if qty or costs cannot be determined */ public BigDecimal getProductCosts (MAcctSchema as, int AD_Org_ID, String costingMethod, int C_OrderLine_ID, boolean zeroCostsOK) { return getProductCosts(as, AD_Org_ID, costingMethod, C_OrderLine_ID, zeroCostsOK, null, null, false); } /** * Get Total Costs (amt*qty) in Accounting Schema Currency * @param as accounting schema * @param AD_Org_ID trx org * @param costingMethod if null uses Accounting Schema - AcctSchema.COSTINGMETHOD_* * @param C_OrderLine_ID optional order line * @param zeroCostsOK zero/no costs are OK * @param dateAcct account date * @param costDetail optional cost detail - use to retrieve the cost history * @param isInBackDatePostingProcess in a back-date posting process * @return cost or null, if qty or costs cannot be determined */ public BigDecimal getProductCosts (MAcctSchema as, int AD_Org_ID, String costingMethod, int C_OrderLine_ID, boolean zeroCostsOK, Timestamp dateAcct, MCostDetail costDetail, boolean isInBackDatePostingProcess) { if (m_qty == null) { log.fine("No Qty"); return null; } // No Product if (m_product == null) { log.fine("No Product"); return null; } // BigDecimal cost = MCost.getCost (m_product, m_M_AttributeSetInstance_ID, as, AD_Org_ID, costingMethod, m_qty, C_OrderLine_ID, zeroCostsOK, dateAcct, costDetail, isInBackDatePostingProcess, m_trxName); if (cost == null) { log.fine("No Costs"); return null; } return cost; } // getProductCosts /** * String Representation * @return info */ @Override public String toString() { StringBuilder sb = new StringBuilder ("ProductCost["); sb.append ("M_Product_ID=").append(m_M_Product_ID) .append(",M_AttributeSetInstance_ID").append(m_M_AttributeSetInstance_ID) .append (",Qty=").append(m_qty) .append ("]"); return sb.toString (); } // toString /** * Set M_AttributeSetInstance_ID * @param M_AttributeSetInstance_ID the m_M_AttributeSetInstance_ID to set */ public void setM_M_AttributeSetInstance_ID(int M_AttributeSetInstance_ID) { this.m_M_AttributeSetInstance_ID = M_AttributeSetInstance_ID; } } // ProductCost