From f5bcca0a7bead51e3d0ae1b4adc00b5b1b18b33e Mon Sep 17 00:00:00 2001 From: armenrz Date: Sun, 13 Sep 2009 10:36:14 +0000 Subject: [PATCH] #2822201: Correct Actualization of CurrentQty in MCost https://sourceforge.net/tracker/?func=detail&aid=2822201&group_id=176962&atid=879335 --- base/src/org/compiere/model/MCostDetail.java | 2391 +++++++++--------- 1 file changed, 1223 insertions(+), 1168 deletions(-) diff --git a/base/src/org/compiere/model/MCostDetail.java b/base/src/org/compiere/model/MCostDetail.java index ca7b7f587a..392fa0da03 100644 --- a/base/src/org/compiere/model/MCostDetail.java +++ b/base/src/org/compiere/model/MCostDetail.java @@ -1,1168 +1,1223 @@ -/****************************************************************************** - * 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.util.Properties; -import java.util.logging.Level; - -import org.compiere.util.CLogger; -import org.compiere.util.DB; -import org.compiere.util.Env; - -/** - * Cost Detail Model - * - * @author Jorg Janke - * @author Armen Rizal, Goodwill Consulting - *
  • BF: 2431123 Return Trx changes weighted average cost - *
  • BF: 1568752 Average invoice costing: landed costs incorrectly applied - * @author Armen Rizal & Bayu Cahya - *
  • BF [ 2129781 ] Cost Detail not created properly for multi acc schema - * @author Teo Sarca - *
  • BF [ 2847648 ] Manufacture & shipment cost errors - * https://sourceforge.net/tracker/?func=detail&aid=2847648&group_id=176962&atid=934929 - * @version $Id: MCostDetail.java,v 1.3 2006/07/30 00:51:05 jjanke Exp $ - * - */ -public class MCostDetail extends X_M_CostDetail -{ - /** - * - */ - private static final long serialVersionUID = 5452006110417178583L; - - - /** - * Create New Order Cost Detail for Purchase Orders. - * Called from Doc_MatchPO - * @param as accounting schema - * @param AD_Org_ID org - * @param M_Product_ID product - * @param M_AttributeSetInstance_ID asi - * @param C_OrderLine_ID order - * @param M_CostElement_ID optional cost element for Freight - * @param Amt amt total amount - * @param Qty qty - * @param Description optional description - * @param trxName transaction - * @return true if created - */ - public static boolean createOrder (MAcctSchema as, int AD_Org_ID, - int M_Product_ID, int M_AttributeSetInstance_ID, - int C_OrderLine_ID, int M_CostElement_ID, - BigDecimal Amt, BigDecimal Qty, - String Description, String trxName) - { - // Delete Unprocessed zero Differences - String sql = "DELETE M_CostDetail " - + "WHERE Processed='N' AND COALESCE(DeltaAmt,0)=0 AND COALESCE(DeltaQty,0)=0" - + " AND C_OrderLine_ID=" + C_OrderLine_ID - + " AND C_AcctSchema_ID =" + as.getC_AcctSchema_ID() - + " AND M_AttributeSetInstance_ID=" + M_AttributeSetInstance_ID; - int no = DB.executeUpdate(sql, trxName); - if (no != 0) - s_log.config("Deleted #" + no); - MCostDetail cd = get (as.getCtx(), "C_OrderLine_ID=?", - C_OrderLine_ID, M_AttributeSetInstance_ID, as.getC_AcctSchema_ID(), trxName); - // - if (cd == null) // createNew - { - cd = new MCostDetail (as, AD_Org_ID, - M_Product_ID, M_AttributeSetInstance_ID, - M_CostElement_ID, - Amt, Qty, Description, trxName); - cd.setC_OrderLine_ID (C_OrderLine_ID); - } - else - { - // MZ Goodwill - // set deltaAmt=Amt, deltaQty=qty, and set Cost Detail for Amt and Qty - cd.setDeltaAmt(Amt.subtract(cd.getAmt())); - cd.setDeltaQty(Qty.subtract(cd.getQty())); - if (cd.isDelta()) - { - cd.setProcessed(false); - cd.setAmt(Amt); - cd.setQty(Qty); - } - // end MZ - else - return true; // nothing to do - } - boolean ok = cd.save(); - if (ok && !cd.isProcessed()) - { - MClient client = MClient.get(as.getCtx(), as.getAD_Client_ID()); - if (client.isCostImmediate()) - cd.process(); - } - s_log.config("(" + ok + ") " + cd); - return ok; - } // createOrder - - - /** - * Create New Invoice Cost Detail for AP Invoices. - * Called from Doc_Invoice - for Invoice Adjustments - * @param as accounting schema - * @param AD_Org_ID org - * @param M_Product_ID product - * @param M_AttributeSetInstance_ID asi - * @param C_InvoiceLine_ID invoice - * @param M_CostElement_ID optional cost element for Freight - * @param Amt amt - * @param Qty qty - * @param Description optional description - * @param trxName transaction - * @return true if created - */ - public static boolean createInvoice (MAcctSchema as, int AD_Org_ID, - int M_Product_ID, int M_AttributeSetInstance_ID, - int C_InvoiceLine_ID, int M_CostElement_ID, - BigDecimal Amt, BigDecimal Qty, - String Description, String trxName) - { - // Delete Unprocessed zero Differences - String sql = "DELETE M_CostDetail " - + "WHERE Processed='N' AND COALESCE(DeltaAmt,0)=0 AND COALESCE(DeltaQty,0)=0" - + " AND C_InvoiceLine_ID=" + C_InvoiceLine_ID - + " AND C_AcctSchema_ID =" + as.getC_AcctSchema_ID() - + " AND M_AttributeSetInstance_ID=" + M_AttributeSetInstance_ID; - int no = DB.executeUpdate(sql, trxName); - if (no != 0) - s_log.config("Deleted #" + no); - MCostDetail cd = get (as.getCtx(), "C_InvoiceLine_ID=?", - C_InvoiceLine_ID, M_AttributeSetInstance_ID, as.getC_AcctSchema_ID(), trxName); - // - if (cd == null) // createNew - { - cd = new MCostDetail (as, AD_Org_ID, - M_Product_ID, M_AttributeSetInstance_ID, - M_CostElement_ID, - Amt, Qty, Description, trxName); - cd.setC_InvoiceLine_ID (C_InvoiceLine_ID); - } - else - { - // MZ Goodwill - // set deltaAmt=Amt, deltaQty=qty, and set Cost Detail for Amt and Qty - cd.setDeltaAmt(Amt.subtract(cd.getAmt())); - cd.setDeltaQty(Qty.subtract(cd.getQty())); - if (cd.isDelta()) - { - cd.setProcessed(false); - cd.setAmt(Amt); - cd.setQty(Qty); - } - // end MZ - else - return true; // nothing to do - } - boolean ok = cd.save(); - if (ok && !cd.isProcessed()) - { - MClient client = MClient.get(as.getCtx(), as.getAD_Client_ID()); - if (client.isCostImmediate()) - cd.process(); - } - s_log.config("(" + ok + ") " + cd); - return ok; - } // createInvoice - - /** - * Create New Shipment Cost Detail for SO Shipments. - * Called from Doc_MInOut - for SO Shipments - * @param as accounting schema - * @param AD_Org_ID org - * @param M_Product_ID product - * @param M_AttributeSetInstance_ID asi - * @param M_InOutLine_ID shipment - * @param M_CostElement_ID optional cost element for Freight - * @param Amt amt - * @param Qty qty - * @param Description optional description - * @param IsSOTrx sales order - * @param trxName transaction - * @return true if no error - */ - public static boolean createShipment (MAcctSchema as, int AD_Org_ID, - int M_Product_ID, int M_AttributeSetInstance_ID, - int M_InOutLine_ID, int M_CostElement_ID, - BigDecimal Amt, BigDecimal Qty, - String Description, boolean IsSOTrx, String trxName) - { - // Delete Unprocessed zero Differences - String sql = "DELETE M_CostDetail " - + "WHERE Processed='N' AND COALESCE(DeltaAmt,0)=0 AND COALESCE(DeltaQty,0)=0" - + " AND M_InOutLine_ID=" + M_InOutLine_ID - + " AND C_AcctSchema_ID =" + as.getC_AcctSchema_ID() - + " AND M_AttributeSetInstance_ID=" + M_AttributeSetInstance_ID; - int no = DB.executeUpdate(sql, trxName); - if (no != 0) - s_log.config("Deleted #" + no); - MCostDetail cd = get (as.getCtx(), "M_InOutLine_ID=?", - M_InOutLine_ID, M_AttributeSetInstance_ID, as.getC_AcctSchema_ID(), trxName); - // - if (cd == null) // createNew - { - cd = new MCostDetail (as, AD_Org_ID, - M_Product_ID, M_AttributeSetInstance_ID, - M_CostElement_ID, - Amt, Qty, Description, trxName); - cd.setM_InOutLine_ID(M_InOutLine_ID); - cd.setIsSOTrx(IsSOTrx); - } - else - { - // MZ Goodwill - // set deltaAmt=Amt, deltaQty=qty, and set Cost Detail for Amt and Qty - cd.setDeltaAmt(Amt.subtract(cd.getAmt())); - cd.setDeltaQty(Qty.subtract(cd.getQty())); - if (cd.isDelta()) - { - cd.setProcessed(false); - cd.setAmt(Amt); - cd.setQty(Qty); - } - // end MZ - else - return true; // nothing to do - } - boolean ok = cd.save(); - if (ok && !cd.isProcessed()) - { - MClient client = MClient.get(as.getCtx(), as.getAD_Client_ID()); - if (client.isCostImmediate()) - cd.process(); - } - s_log.config("(" + ok + ") " + cd); - return ok; - } // createShipment - - /** - * Create New Order Cost Detail for Physical Inventory. - * Called from Doc_Inventory - * @param as accounting schema - * @param AD_Org_ID org - * @param M_Product_ID product - * @param M_AttributeSetInstance_ID asi - * @param M_InventoryLine_ID order - * @param M_CostElement_ID optional cost element - * @param Amt amt total amount - * @param Qty qty - * @param Description optional description - * @param trxName transaction - * @return true if no error - */ - public static boolean createInventory (MAcctSchema as, int AD_Org_ID, - int M_Product_ID, int M_AttributeSetInstance_ID, - int M_InventoryLine_ID, int M_CostElement_ID, - BigDecimal Amt, BigDecimal Qty, - String Description, String trxName) - { - // Delete Unprocessed zero Differences - String sql = "DELETE M_CostDetail " - + "WHERE Processed='N' AND COALESCE(DeltaAmt,0)=0 AND COALESCE(DeltaQty,0)=0" - + " AND M_InventoryLine_ID=" + M_InventoryLine_ID - + " AND C_AcctSchema_ID =" + as.getC_AcctSchema_ID() - + " AND M_AttributeSetInstance_ID=" + M_AttributeSetInstance_ID; - int no = DB.executeUpdate(sql, trxName); - if (no != 0) - s_log.config("Deleted #" + no); - MCostDetail cd = get (as.getCtx(), "M_InventoryLine_ID=?", - M_InventoryLine_ID, M_AttributeSetInstance_ID, as.getC_AcctSchema_ID(), trxName); - // - if (cd == null) // createNew - { - cd = new MCostDetail (as, AD_Org_ID, - M_Product_ID, M_AttributeSetInstance_ID, - M_CostElement_ID, - Amt, Qty, Description, trxName); - cd.setM_InventoryLine_ID(M_InventoryLine_ID); - } - else - { - // MZ Goodwill - // set deltaAmt=Amt, deltaQty=qty, and set Cost Detail for Amt and Qty - cd.setDeltaAmt(Amt.subtract(cd.getAmt())); - cd.setDeltaQty(Qty.subtract(cd.getQty())); - if (cd.isDelta()) - { - cd.setProcessed(false); - cd.setAmt(Amt); - cd.setQty(Qty); - } - // end MZ - else - return true; // nothing to do - } - boolean ok = cd.save(); - if (ok && !cd.isProcessed()) - { - MClient client = MClient.get(as.getCtx(), as.getAD_Client_ID()); - if (client.isCostImmediate()) - cd.process(); - } - s_log.config("(" + ok + ") " + cd); - return ok; - } // createInventory - - /** - * Create New Order Cost Detail for Movements. - * Called from Doc_Movement - * @param as accounting schema - * @param AD_Org_ID org - * @param M_Product_ID product - * @param M_AttributeSetInstance_ID asi - * @param M_MovementLine_ID movement - * @param M_CostElement_ID optional cost element for Freight - * @param Amt amt total amount - * @param Qty qty - * @param from if true the from (reduction) - * @param Description optional description - * @param trxName transaction - * @return true if no error - */ - public static boolean createMovement (MAcctSchema as, int AD_Org_ID, - int M_Product_ID, int M_AttributeSetInstance_ID, - int M_MovementLine_ID, int M_CostElement_ID, - BigDecimal Amt, BigDecimal Qty, boolean from, - String Description, String trxName) - { - // Delete Unprocessed zero Differences - String sql = "DELETE M_CostDetail " - + "WHERE Processed='N' AND COALESCE(DeltaAmt,0)=0 AND COALESCE(DeltaQty,0)=0" - + " AND M_MovementLine_ID=" + M_MovementLine_ID - + " AND IsSOTrx=" + (from ? "'Y'" : "'N'") - + " AND C_AcctSchema_ID =" + as.getC_AcctSchema_ID() - + " AND M_AttributeSetInstance_ID=" + M_AttributeSetInstance_ID; - int no = DB.executeUpdate(sql, trxName); - if (no != 0) - s_log.config("Deleted #" + no); - MCostDetail cd = get (as.getCtx(), "M_MovementLine_ID=? AND IsSOTrx=" - + (from ? "'Y'" : "'N'"), - M_MovementLine_ID, M_AttributeSetInstance_ID, as.getC_AcctSchema_ID(), trxName); - // - if (cd == null) // createNew - { - cd = new MCostDetail (as, AD_Org_ID, - M_Product_ID, M_AttributeSetInstance_ID, - M_CostElement_ID, - Amt, Qty, Description, trxName); - cd.setM_MovementLine_ID (M_MovementLine_ID); - cd.setIsSOTrx(from); - } - else - { - // MZ Goodwill - // set deltaAmt=Amt, deltaQty=qty, and set Cost Detail for Amt and Qty - cd.setDeltaAmt(Amt.subtract(cd.getAmt())); - cd.setDeltaQty(Qty.subtract(cd.getQty())); - if (cd.isDelta()) - { - cd.setProcessed(false); - cd.setAmt(Amt); - cd.setQty(Qty); - } - // end MZ - else - return true; // nothing to do - } - boolean ok = cd.save(); - if (ok && !cd.isProcessed()) - { - MClient client = MClient.get(as.getCtx(), as.getAD_Client_ID()); - if (client.isCostImmediate()) - cd.process(); - } - s_log.config("(" + ok + ") " + cd); - return ok; - } // createMovement - - /** - * Create New Order Cost Detail for Production. - * Called from Doc_Production - * @param as accounting schema - * @param AD_Org_ID org - * @param M_Product_ID product - * @param M_AttributeSetInstance_ID asi - * @param M_ProductionLine_ID production line - * @param M_CostElement_ID optional cost element - * @param Amt amt total amount - * @param Qty qty - * @param Description optional description - * @param trxName transaction - * @return true if no error - */ - public static boolean createProduction (MAcctSchema as, int AD_Org_ID, - int M_Product_ID, int M_AttributeSetInstance_ID, - int M_ProductionLine_ID, int M_CostElement_ID, - BigDecimal Amt, BigDecimal Qty, - String Description, String trxName) - { - // Delete Unprocessed zero Differences - String sql = "DELETE M_CostDetail " - + "WHERE Processed='N' AND COALESCE(DeltaAmt,0)=0 AND COALESCE(DeltaQty,0)=0" - + " AND M_ProductionLine_ID=" + M_ProductionLine_ID - + " AND C_AcctSchema_ID =" + as.getC_AcctSchema_ID() - + " AND M_AttributeSetInstance_ID=" + M_AttributeSetInstance_ID; - int no = DB.executeUpdate(sql, trxName); - if (no != 0) - s_log.config("Deleted #" + no); - MCostDetail cd = get (as.getCtx(), "M_ProductionLine_ID=?", - M_ProductionLine_ID, M_AttributeSetInstance_ID, as.getC_AcctSchema_ID(), trxName); - // - if (cd == null) // createNew - { - cd = new MCostDetail (as, AD_Org_ID, - M_Product_ID, M_AttributeSetInstance_ID, - M_CostElement_ID, - Amt, Qty, Description, trxName); - cd.setM_ProductionLine_ID(M_ProductionLine_ID); - } - else - { - // MZ Goodwill - // set deltaAmt=Amt, deltaQty=qty, and set Cost Detail for Amt and Qty - cd.setDeltaAmt(Amt.subtract(cd.getAmt())); - cd.setDeltaQty(Qty.subtract(cd.getQty())); - if (cd.isDelta()) - { - cd.setProcessed(false); - cd.setAmt(Amt); - cd.setQty(Qty); - } - // end MZ - else - return true; // nothing to do - } - boolean ok = cd.save(); - if (ok && !cd.isProcessed()) - { - MClient client = MClient.get(as.getCtx(), as.getAD_Client_ID()); - if (client.isCostImmediate()) - cd.process(); - } - s_log.config("(" + ok + ") " + cd); - return ok; - } // createProduction - - - /************************************************************************** - * Get Cost Detail - * @param ctx context - * @param whereClause where clause - * @param ID 1st parameter - * @param M_AttributeSetInstance_ID ASI - * @param trxName trx - * @return cost detail - */ - public static MCostDetail get (Properties ctx, String whereClause, - int ID, int M_AttributeSetInstance_ID, int C_AcctSchema_ID, String trxName) - { - String sql = "SELECT * FROM M_CostDetail WHERE " + whereClause - + " AND M_AttributeSetInstance_ID=?" - + " AND C_AcctSchema_ID=?"; - MCostDetail retValue = null; - PreparedStatement pstmt = null; - try - { - pstmt = DB.prepareStatement (sql, null); - pstmt.setInt (1, ID); - pstmt.setInt (2, M_AttributeSetInstance_ID); - pstmt.setInt (3, C_AcctSchema_ID); - ResultSet rs = pstmt.executeQuery (); - if (rs.next ()) - retValue = new MCostDetail (ctx, rs, trxName); - rs.close (); - pstmt.close (); - pstmt = null; - } - catch (Exception e) - { - s_log.log (Level.SEVERE, sql + " - " + ID, e); - } - try - { - if (pstmt != null) - pstmt.close (); - pstmt = null; - } - catch (Exception e) - { - pstmt = null; - } - return retValue; - } // get - - /** - * Process Cost Details for product - * @param product product - * @param trxName transaction - * @return true if no error - */ - public static boolean processProduct (MProduct product, String trxName) - { - String sql = "SELECT * FROM M_CostDetail " - + "WHERE M_Product_ID=?" - + " AND Processed='N' " - + "ORDER BY C_AcctSchema_ID, M_CostElement_ID, AD_Org_ID, M_AttributeSetInstance_ID, Created"; - int counterOK = 0; - int counterError = 0; - PreparedStatement pstmt = null; - try - { - pstmt = DB.prepareStatement (sql, trxName); - pstmt.setInt (1, product.getM_Product_ID()); - ResultSet rs = pstmt.executeQuery (); - while (rs.next ()) - { - MCostDetail cd = new MCostDetail(product.getCtx(), rs, trxName); - if (cd.process()) // saves - counterOK++; - else - counterError++; - } - rs.close (); - pstmt.close (); - pstmt = null; - } - catch (Exception e) - { - s_log.log (Level.SEVERE, sql, e); - counterError++; - } - - try - { - if (pstmt != null) - pstmt.close (); - pstmt = null; - } - catch (Exception e) - { - pstmt = null; - } - s_log.config("OK=" + counterOK + ", Errors=" + counterError); - return counterError == 0; - } // processProduct - - /** Logger */ - private static CLogger s_log = CLogger.getCLogger (MCostDetail.class); - - - /************************************************************************** - * Standard Constructor - * @param ctx context - * @param M_CostDetail_ID id - * @param trxName trx - */ - public MCostDetail (Properties ctx, int M_CostDetail_ID, String trxName) - { - super (ctx, M_CostDetail_ID, trxName); - if (M_CostDetail_ID == 0) - { - // setC_AcctSchema_ID (0); - // setM_Product_ID (0); - setM_AttributeSetInstance_ID (0); - // setC_OrderLine_ID (0); - // setM_InOutLine_ID(0); - // setC_InvoiceLine_ID (0); - setProcessed (false); - setAmt (Env.ZERO); - setQty (Env.ZERO); - setIsSOTrx (false); - setDeltaAmt (Env.ZERO); - setDeltaQty (Env.ZERO); - } - } // MCostDetail - - /** - * Load Constructor - * @param ctx context - * @param rs result set - * @param trxName trx - */ - public MCostDetail (Properties ctx, ResultSet rs, String trxName) - { - super (ctx, rs, trxName); - } // MCostDetail - - /** - * New Constructor - * @param as accounting schema - * @param AD_Org_ID org - * @param M_Product_ID product - * @param M_AttributeSetInstance_ID asi - * @param M_CostElement_ID optional cost element for Freight - * @param Amt amt - * @param Qty qty - * @param Description optional description - * @param trxName transaction - */ - public MCostDetail (MAcctSchema as, int AD_Org_ID, - int M_Product_ID, int M_AttributeSetInstance_ID, - int M_CostElement_ID, BigDecimal Amt, BigDecimal Qty, - String Description, String trxName) - { - this (as.getCtx(), 0, trxName); - setClientOrg(as.getAD_Client_ID(), AD_Org_ID); - setC_AcctSchema_ID (as.getC_AcctSchema_ID()); - setM_Product_ID (M_Product_ID); - setM_AttributeSetInstance_ID (M_AttributeSetInstance_ID); - // - setM_CostElement_ID(M_CostElement_ID); - // - setAmt (Amt); - setQty (Qty); - setDescription(Description); - } // MCostDetail - - /** - * Set Amt - * @param Amt amt - */ - public void setAmt (BigDecimal Amt) - { - if (isProcessed()) - throw new IllegalStateException("Cannot change Amt - processed"); - if (Amt == null) - super.setAmt (Env.ZERO); - else - super.setAmt (Amt); - } // setAmt - - /** - * Set Qty - * @param Qty qty - */ - public void setQty (BigDecimal Qty) - { - if (isProcessed()) - throw new IllegalStateException("Cannot change Qty - processed"); - if (Qty == null) - super.setQty (Env.ZERO); - else - super.setQty (Qty); - } // setQty - - /** - * Is Order - * @return true if order line - */ - public boolean isOrder() - { - return getC_OrderLine_ID() != 0; - } // isOrder - - /** - * Is Invoice - * @return true if invoice line - */ - public boolean isInvoice() - { - return getC_InvoiceLine_ID() != 0; - } // isInvoice - - /** - * Is Shipment - * @return true if sales order shipment - */ - public boolean isShipment() - { - return isSOTrx() && getM_InOutLine_ID() != 0; - } // isShipment - - /** - * Is this a Delta Record (previously processed)? - * @return true if delta is not null - */ - public boolean isDelta() - { - return !(getDeltaAmt().signum() == 0 - && getDeltaQty().signum() == 0); - } // isDelta - - - /** - * After Save - * @param newRecord new - * @param success success - * @return true - */ - protected boolean afterSave (boolean newRecord, boolean success) - { - return true; - } // afterSave - - /** - * Before Delete - * @return false if processed - */ - protected boolean beforeDelete () - { - return !isProcessed(); - } // beforeDelete - - - /** - * String Representation - * @return info - */ - public String toString () - { - StringBuffer sb = new StringBuffer ("MCostDetail["); - sb.append (get_ID()); - if (getC_OrderLine_ID() != 0) - sb.append (",C_OrderLine_ID=").append (getC_OrderLine_ID()); - if (getM_InOutLine_ID() != 0) - sb.append (",M_InOutLine_ID=").append (getM_InOutLine_ID()); - if (getC_InvoiceLine_ID() != 0) - sb.append (",C_InvoiceLine_ID=").append (getC_InvoiceLine_ID()); - if (getC_ProjectIssue_ID() != 0) - sb.append (",C_ProjectIssue_ID=").append (getC_ProjectIssue_ID()); - if (getM_MovementLine_ID() != 0) - sb.append (",M_MovementLine_ID=").append (getM_MovementLine_ID()); - if (getM_InventoryLine_ID() != 0) - sb.append (",M_InventoryLine_ID=").append (getM_InventoryLine_ID()); - if (getM_ProductionLine_ID() != 0) - sb.append (",M_ProductionLine_ID=").append (getM_ProductionLine_ID()); - sb.append(",Amt=").append(getAmt()) - .append(",Qty=").append(getQty()); - if (isDelta()) - sb.append(",DeltaAmt=").append(getDeltaAmt()) - .append(",DeltaQty=").append(getDeltaQty()); - sb.append ("]"); - return sb.toString (); - } // toString - - - /************************************************************************** - * Process Cost Detail Record. - * The record is saved if processed. - * @return true if processed - */ - public synchronized boolean process() - { - if (isProcessed()) - { - log.info("Already processed"); - return true; - } - boolean ok = false; - - // get costing level for product - MAcctSchema as = MAcctSchema.get(getCtx(), getC_AcctSchema_ID()); - MProduct product = MProduct.get(getCtx(), getM_Product_ID()); - String CostingLevel = product.getCostingLevel(as); - // Org Element - int Org_ID = getAD_Org_ID(); - int M_ASI_ID = getM_AttributeSetInstance_ID(); - if (MAcctSchema.COSTINGLEVEL_Client.equals(CostingLevel)) - { - Org_ID = 0; - M_ASI_ID = 0; - } - else if (MAcctSchema.COSTINGLEVEL_Organization.equals(CostingLevel)) - M_ASI_ID = 0; - else if (MAcctSchema.COSTINGLEVEL_BatchLot.equals(CostingLevel)) - Org_ID = 0; - - // Create Material Cost elements - if (getM_CostElement_ID() == 0) - { - MCostElement[] ces = MCostElement.getCostingMethods(this); - for (int i = 0; i < ces.length; i++) - { - MCostElement ce = ces[i]; - ok = process (as, product, ce, Org_ID, M_ASI_ID); - if (!ok) - break; - } - } // Material Cost elements - else - { - MCostElement ce = MCostElement.get(getCtx(), getM_CostElement_ID()); - ok = process (as, product, ce, Org_ID, M_ASI_ID); - } - - // Save it - if (ok) - { - setDeltaAmt(null); - setDeltaQty(null); - setProcessed(true); - ok = save(); - } - log.info(ok + " - " + toString()); - return ok; - } // process - - /** - * Process cost detail for cost record - * @param as accounting schema - * @param product product - * @param ce cost element - * @param Org_ID org - corrected for costing level - * @param M_ASI_ID - asi corrected for costing level - * @return true if cost ok - */ - private boolean process (MAcctSchema as, MProduct product, MCostElement ce, - int Org_ID, int M_ASI_ID) - { - MCost cost = MCost.get(product, M_ASI_ID, as, - Org_ID, ce.getM_CostElement_ID(), get_TrxName()); - // if (cost == null) - // cost = new MCost(product, M_ASI_ID, - // as, Org_ID, ce.getM_CostElement_ID()); - - // MZ Goodwill - // used deltaQty and deltaAmt if exist - BigDecimal qty = Env.ZERO; - BigDecimal amt = Env.ZERO; - if (isDelta()) - { - qty = getDeltaQty(); - amt = getDeltaAmt(); - } - else - { - qty = getQty(); - amt = getAmt(); - } - // end MZ - - int precision = as.getCostingPrecision(); - BigDecimal price = amt; - if (qty.signum() != 0) - price = amt.divide(qty, precision, BigDecimal.ROUND_HALF_UP); - - /** All Costing Methods - if (ce.isAverageInvoice()) - else if (ce.isAveragePO()) - else if (ce.isFifo()) - else if (ce.isLifo()) - else if (ce.isLastInvoice()) - else if (ce.isLastPOPrice()) - else if (ce.isStandardCosting()) - else if (ce.isUserDefined()) - else if (!ce.isCostingMethod()) - **/ - - // *** Purchase Order Detail Record *** - if (getC_OrderLine_ID() != 0) - { - boolean isReturnTrx = qty.signum() < 0; - - if (ce.isAveragePO()) - { - cost.setWeightedAverage(amt, qty); - log.finer("PO - AveragePO - " + cost); - } - else if (ce.isLastPOPrice()) - { - if(!isReturnTrx) - { - if (qty.signum() != 0) - cost.setCurrentCostPrice(price); - else - { - BigDecimal cCosts = cost.getCurrentCostPrice().add(amt); - cost.setCurrentCostPrice(cCosts); - } - } - cost.add(amt, qty); - log.finer("PO - LastPO - " + cost); - } - else if (ce.isUserDefined()) - { - // Interface - log.finer("PO - UserDef - " + cost); - } - else if (!ce.isCostingMethod()) - { - log.finer("PO - " + ce + " - " + cost); - } - // else - // log.warning("PO - " + ce + " - " + cost); - } - - // *** AP Invoice Detail Record *** - else if (getC_InvoiceLine_ID() != 0) - { - boolean isReturnTrx = qty.signum() < 0; - - if (ce.isAverageInvoice()) - { - cost.setWeightedAverage(amt, qty); - log.finer("Inv - AverageInv - " + cost); - } - else if (ce.isFifo() - || ce.isLifo()) - { - // Real ASI - costing level Org - MCostQueue cq = MCostQueue.get(product, getM_AttributeSetInstance_ID(), - as, Org_ID, ce.getM_CostElement_ID(), get_TrxName()); - cq.setCosts(amt, qty, precision); - cq.save(); - // Get Costs - costing level Org/ASI - MCostQueue[] cQueue = MCostQueue.getQueue(product, M_ASI_ID, - as, Org_ID, ce, get_TrxName()); - if (cQueue != null && cQueue.length > 0) - cost.setCurrentCostPrice(cQueue[0].getCurrentCostPrice()); - cost.add(amt, qty); - log.finer("Inv - FiFo/LiFo - " + cost); - } - else if (ce.isLastInvoice()) - { - if (!isReturnTrx) - { - if (qty.signum() != 0) - cost.setCurrentCostPrice(price); - else - { - BigDecimal cCosts = cost.getCurrentCostPrice().add(amt); - cost.setCurrentCostPrice(cCosts); - } - } - cost.add(amt, qty); - log.finer("Inv - LastInv - " + cost); - } - else if (ce.isStandardCosting()) - { - // Update cost record only if newly created. - // Elsewhere we risk to set the CurrentCostPrice to an undesired price. - if (cost.is_new() - && cost.getCurrentCostPrice().signum() == 0 - && cost.getCurrentCostPriceLL().signum() == 0) - { - cost.setCurrentCostPrice(price); - // seed initial price - if (cost.getCurrentCostPrice().signum() == 0) - { - cost.setCurrentCostPrice(MCost.getSeedCosts(product, M_ASI_ID, - as, Org_ID, ce.getCostingMethod(), getC_OrderLine_ID())); - log.finest("Inv - Standard - CurrentCostPrice(seed)="+cost.getCurrentCostPrice()+", price="+price); - } - } - cost.add(amt, qty); - log.finer("Inv - Standard - " + cost); - } - else if (ce.isUserDefined()) - { - // Interface - cost.add(amt, qty); - log.finer("Inv - UserDef - " + cost); - } - else if (!ce.isCostingMethod()) // Cost Adjustments - { - // AZ Goodwill - //get costing method for product - String costingMethod = product.getCostingMethod(as); - if (MAcctSchema.COSTINGMETHOD_AveragePO.equals(costingMethod) || - MAcctSchema.COSTINGMETHOD_AverageInvoice.equals(costingMethod)) - { - if (cost.getCurrentQty().compareTo(Env.ZERO) == 0) - { - //initialize current qty for new landed cost element - String sql = "SELECT QtyOnHand FROM M_Storage" - + " WHERE AD_Client_ID=" + cost.getAD_Client_ID() - + " AND AD_Org_ID=" + cost.getAD_Org_ID() - + " AND M_Product_ID=" + cost.getM_Product_ID() - + " AND M_AttributeSetInstance_ID=" + M_ASI_ID; - if (M_ASI_ID == 0) - sql = "SELECT SUM(QtyOnHand) FROM M_Storage" - + " WHERE AD_Client_ID=" + cost.getAD_Client_ID() - + " AND AD_Org_ID=" + cost.getAD_Org_ID() - + " AND M_Product_ID=" + cost.getM_Product_ID(); - BigDecimal bd = DB.getSQLValueBD(get_TrxName(), sql); - if (bd != null) - cost.setCurrentQty(bd.subtract(qty)); // (initial qty = onhand qty - allocated qty) - } - cost.setWeightedAverage(amt, qty); //also get averaged - } - else //original logic from Compiere - { - BigDecimal cCosts = cost.getCurrentCostPrice().add(amt); - cost.setCurrentCostPrice(cCosts); - cost.add(amt, qty); - } - // end AZ - log.finer("Inv - none - " + cost); - } - // else - // log.warning("Inv - " + ce + " - " + cost); - } - - // *** Qty Adjustment Detail Record *** - else if (getM_InOutLine_ID() != 0 // AR Shipment Detail Record - || getM_MovementLine_ID() != 0 - || getM_InventoryLine_ID() != 0 - || getM_ProductionLine_ID() != 0 - || getC_ProjectIssue_ID() != 0 - || getPP_Cost_Collector_ID() != 0) - { - boolean addition = qty.signum() > 0; - // - if (ce.isAverageInvoice()) - { - if (addition) - cost.setWeightedAverage(amt, qty); - else - cost.setCurrentQty(cost.getCurrentQty().add(qty)); - log.finer("QtyAdjust - AverageInv - " + cost); - } - else if (ce.isAveragePO()) - { - if (addition) - cost.setWeightedAverage(amt, qty); - else - cost.setCurrentQty(cost.getCurrentQty().add(qty)); - log.finer("QtyAdjust - AveragePO - " + cost); - } - else if (ce.isFifo() || ce.isLifo()) - { - if (addition) - { - // Real ASI - costing level Org - MCostQueue cq = MCostQueue.get(product, getM_AttributeSetInstance_ID(), - as, Org_ID, ce.getM_CostElement_ID(), get_TrxName()); - cq.setCosts(amt, qty, precision); - cq.save(); - } - else - { - // Adjust Queue - costing level Org/ASI - MCostQueue.adjustQty(product, M_ASI_ID, - as, Org_ID, ce, qty.negate(), get_TrxName()); - } - // Get Costs - costing level Org/ASI - MCostQueue[] cQueue = MCostQueue.getQueue(product, M_ASI_ID, - as, Org_ID, ce, get_TrxName()); - if (cQueue != null && cQueue.length > 0) - cost.setCurrentCostPrice(cQueue[0].getCurrentCostPrice()); - cost.setCurrentQty(cost.getCurrentQty().add(qty)); - log.finer("QtyAdjust - FiFo/Lifo - " + cost); - } - else if (ce.isLastInvoice()) - { - cost.setCurrentQty(cost.getCurrentQty().add(qty)); - log.finer("QtyAdjust - LastInv - " + cost); - } - else if (ce.isLastPOPrice()) - { - cost.setCurrentQty(cost.getCurrentQty().add(qty)); - log.finer("QtyAdjust - LastPO - " + cost); - } - else if (ce.isStandardCosting()) - { - if (addition) - { - cost.add(amt, qty); - // Initial - if (cost.getCurrentCostPrice().signum() == 0 - && cost.getCurrentCostPriceLL().signum() == 0 - && cost.is_new()) - { - cost.setCurrentCostPrice(price); - log.finest("QtyAdjust - Standard - CurrentCostPrice="+price); - } - } - else - { - cost.setCurrentQty(cost.getCurrentQty().add(qty)); - } - log.finer("QtyAdjust - Standard - " + cost); - } - else if (ce.isUserDefined()) - { - // Interface - if (addition) - cost.add(amt, qty); - else - cost.setCurrentQty(cost.getCurrentQty().add(qty)); - log.finer("QtyAdjust - UserDef - " + cost); - } - else if (!ce.isCostingMethod()) - { - // Should not happen - log.finer("QtyAdjust - ?none? - " + cost); - } - else - log.warning("QtyAdjust - " + ce + " - " + cost); - } - else // unknown or no id - { - log.warning("Unknown Type: " + toString()); - return false; - } - return cost.save(); - } // process - - // Elaine 2008/6/20 - protected boolean afterDelete (boolean success) - { - if(success) - { - // recalculate MCost - boolean ok = false; - // get costing level for product - MAcctSchema as = new MAcctSchema (getCtx(), getC_AcctSchema_ID(), null); - MProduct product = MProduct.get(getCtx(), getM_Product_ID()); - String CostingLevel = product.getCostingLevel(as); - // Org Element - int Org_ID = getAD_Org_ID(); - int M_ASI_ID = getM_AttributeSetInstance_ID(); - if (MAcctSchema.COSTINGLEVEL_Client.equals(CostingLevel)) - { - Org_ID = 0; - M_ASI_ID = 0; - } - else if (MAcctSchema.COSTINGLEVEL_Organization.equals(CostingLevel)) - M_ASI_ID = 0; - else if (MAcctSchema.COSTINGLEVEL_BatchLot.equals(CostingLevel)) - Org_ID = 0; - - // Create Material Cost elements - if (getM_CostElement_ID() == 0) - { - MCostElement[] ces = MCostElement.getCostingMethods(this); - for (int i = 0; i < ces.length; i++) - { - MCostElement ce = ces[i]; - ok = process (as, product, ce, Org_ID, M_ASI_ID); - if (!ok) - break; - } - } // Material Cost elements - else - { - MCostElement ce = MCostElement.get(getCtx(), getM_CostElement_ID()); - ok = process (as, product, ce, Org_ID, M_ASI_ID); - } - - return ok; - } - - return super.afterDelete(success); - } - // - -} // MCostDetail +/****************************************************************************** + * 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.util.Properties; +import java.util.logging.Level; + +import org.compiere.util.CLogger; +import org.compiere.util.DB; +import org.compiere.util.Env; + +/** + * Cost Detail Model + * + * @author Jorg Janke + * @author Armen Rizal, Goodwill Consulting + *
  • BF: 2431123 Return Trx changes weighted average cost + *
  • BF: 1568752 Average invoice costing: landed costs incorrectly applied + * @author Armen Rizal & Bayu Cahya + *
  • BF [ 2129781 ] Cost Detail not created properly for multi acc schema + * @author Teo Sarca + *
  • BF [ 2847648 ] Manufacture & shipment cost errors + * https://sourceforge.net/tracker/?func=detail&aid=2847648&group_id=176962&atid=934929 + * @version $Id: MCostDetail.java,v 1.3 2006/07/30 00:51:05 jjanke Exp $ + * + */ +public class MCostDetail extends X_M_CostDetail +{ + /** + * + */ + private static final long serialVersionUID = 5452006110417178583L; + + + /** + * Create New Order Cost Detail for Purchase Orders. + * Called from Doc_MatchPO + * @param as accounting schema + * @param AD_Org_ID org + * @param M_Product_ID product + * @param M_AttributeSetInstance_ID asi + * @param C_OrderLine_ID order + * @param M_CostElement_ID optional cost element for Freight + * @param Amt amt total amount + * @param Qty qty + * @param Description optional description + * @param trxName transaction + * @return true if created + */ + public static boolean createOrder (MAcctSchema as, int AD_Org_ID, + int M_Product_ID, int M_AttributeSetInstance_ID, + int C_OrderLine_ID, int M_CostElement_ID, + BigDecimal Amt, BigDecimal Qty, + String Description, String trxName) + { + // Delete Unprocessed zero Differences + String sql = "DELETE M_CostDetail " + + "WHERE Processed='N' AND COALESCE(DeltaAmt,0)=0 AND COALESCE(DeltaQty,0)=0" + + " AND C_OrderLine_ID=" + C_OrderLine_ID + + " AND C_AcctSchema_ID =" + as.getC_AcctSchema_ID() + + " AND M_AttributeSetInstance_ID=" + M_AttributeSetInstance_ID; + int no = DB.executeUpdate(sql, trxName); + if (no != 0) + s_log.config("Deleted #" + no); + MCostDetail cd = get (as.getCtx(), "C_OrderLine_ID=?", + C_OrderLine_ID, M_AttributeSetInstance_ID, as.getC_AcctSchema_ID(), trxName); + // + if (cd == null) // createNew + { + cd = new MCostDetail (as, AD_Org_ID, + M_Product_ID, M_AttributeSetInstance_ID, + M_CostElement_ID, + Amt, Qty, Description, trxName); + cd.setC_OrderLine_ID (C_OrderLine_ID); + } + else + { + // MZ Goodwill + // set deltaAmt=Amt, deltaQty=qty, and set Cost Detail for Amt and Qty + cd.setDeltaAmt(Amt.subtract(cd.getAmt())); + cd.setDeltaQty(Qty.subtract(cd.getQty())); + if (cd.isDelta()) + { + cd.setProcessed(false); + cd.setAmt(Amt); + cd.setQty(Qty); + } + // end MZ + else + return true; // nothing to do + } + boolean ok = cd.save(); + if (ok && !cd.isProcessed()) + { + MClient client = MClient.get(as.getCtx(), as.getAD_Client_ID()); + if (client.isCostImmediate()) + cd.process(); + } + s_log.config("(" + ok + ") " + cd); + return ok; + } // createOrder + + + /** + * Create New Invoice Cost Detail for AP Invoices. + * Called from Doc_Invoice - for Invoice Adjustments + * @param as accounting schema + * @param AD_Org_ID org + * @param M_Product_ID product + * @param M_AttributeSetInstance_ID asi + * @param C_InvoiceLine_ID invoice + * @param M_CostElement_ID optional cost element for Freight + * @param Amt amt + * @param Qty qty + * @param Description optional description + * @param trxName transaction + * @return true if created + */ + public static boolean createInvoice (MAcctSchema as, int AD_Org_ID, + int M_Product_ID, int M_AttributeSetInstance_ID, + int C_InvoiceLine_ID, int M_CostElement_ID, + BigDecimal Amt, BigDecimal Qty, + String Description, String trxName) + { + // Delete Unprocessed zero Differences + String sql = "DELETE M_CostDetail " + + "WHERE Processed='N' AND COALESCE(DeltaAmt,0)=0 AND COALESCE(DeltaQty,0)=0" + + " AND C_InvoiceLine_ID=" + C_InvoiceLine_ID + + " AND C_AcctSchema_ID =" + as.getC_AcctSchema_ID() + + " AND M_AttributeSetInstance_ID=" + M_AttributeSetInstance_ID; + int no = DB.executeUpdate(sql, trxName); + if (no != 0) + s_log.config("Deleted #" + no); + MCostDetail cd = get (as.getCtx(), "C_InvoiceLine_ID=?", + C_InvoiceLine_ID, M_AttributeSetInstance_ID, as.getC_AcctSchema_ID(), trxName); + // + if (cd == null) // createNew + { + cd = new MCostDetail (as, AD_Org_ID, + M_Product_ID, M_AttributeSetInstance_ID, + M_CostElement_ID, + Amt, Qty, Description, trxName); + cd.setC_InvoiceLine_ID (C_InvoiceLine_ID); + } + else + { + // MZ Goodwill + // set deltaAmt=Amt, deltaQty=qty, and set Cost Detail for Amt and Qty + cd.setDeltaAmt(Amt.subtract(cd.getAmt())); + cd.setDeltaQty(Qty.subtract(cd.getQty())); + if (cd.isDelta()) + { + cd.setProcessed(false); + cd.setAmt(Amt); + cd.setQty(Qty); + } + // end MZ + else + return true; // nothing to do + } + boolean ok = cd.save(); + if (ok && !cd.isProcessed()) + { + MClient client = MClient.get(as.getCtx(), as.getAD_Client_ID()); + if (client.isCostImmediate()) + cd.process(); + } + s_log.config("(" + ok + ") " + cd); + return ok; + } // createInvoice + + /** + * Create New Shipment Cost Detail for SO Shipments. + * Called from Doc_MInOut - for SO Shipments + * @param as accounting schema + * @param AD_Org_ID org + * @param M_Product_ID product + * @param M_AttributeSetInstance_ID asi + * @param M_InOutLine_ID shipment + * @param M_CostElement_ID optional cost element for Freight + * @param Amt amt + * @param Qty qty + * @param Description optional description + * @param IsSOTrx sales order + * @param trxName transaction + * @return true if no error + */ + public static boolean createShipment (MAcctSchema as, int AD_Org_ID, + int M_Product_ID, int M_AttributeSetInstance_ID, + int M_InOutLine_ID, int M_CostElement_ID, + BigDecimal Amt, BigDecimal Qty, + String Description, boolean IsSOTrx, String trxName) + { + // Delete Unprocessed zero Differences + String sql = "DELETE M_CostDetail " + + "WHERE Processed='N' AND COALESCE(DeltaAmt,0)=0 AND COALESCE(DeltaQty,0)=0" + + " AND M_InOutLine_ID=" + M_InOutLine_ID + + " AND C_AcctSchema_ID =" + as.getC_AcctSchema_ID() + + " AND M_AttributeSetInstance_ID=" + M_AttributeSetInstance_ID; + int no = DB.executeUpdate(sql, trxName); + if (no != 0) + s_log.config("Deleted #" + no); + MCostDetail cd = get (as.getCtx(), "M_InOutLine_ID=?", + M_InOutLine_ID, M_AttributeSetInstance_ID, as.getC_AcctSchema_ID(), trxName); + // + if (cd == null) // createNew + { + cd = new MCostDetail (as, AD_Org_ID, + M_Product_ID, M_AttributeSetInstance_ID, + M_CostElement_ID, + Amt, Qty, Description, trxName); + cd.setM_InOutLine_ID(M_InOutLine_ID); + cd.setIsSOTrx(IsSOTrx); + } + else + { + // MZ Goodwill + // set deltaAmt=Amt, deltaQty=qty, and set Cost Detail for Amt and Qty + cd.setDeltaAmt(Amt.subtract(cd.getAmt())); + cd.setDeltaQty(Qty.subtract(cd.getQty())); + if (cd.isDelta()) + { + cd.setProcessed(false); + cd.setAmt(Amt); + cd.setQty(Qty); + } + // end MZ + else + return true; // nothing to do + } + boolean ok = cd.save(); + if (ok && !cd.isProcessed()) + { + MClient client = MClient.get(as.getCtx(), as.getAD_Client_ID()); + if (client.isCostImmediate()) + cd.process(); + } + s_log.config("(" + ok + ") " + cd); + return ok; + } // createShipment + + /** + * Create New Order Cost Detail for Physical Inventory. + * Called from Doc_Inventory + * @param as accounting schema + * @param AD_Org_ID org + * @param M_Product_ID product + * @param M_AttributeSetInstance_ID asi + * @param M_InventoryLine_ID order + * @param M_CostElement_ID optional cost element + * @param Amt amt total amount + * @param Qty qty + * @param Description optional description + * @param trxName transaction + * @return true if no error + */ + public static boolean createInventory (MAcctSchema as, int AD_Org_ID, + int M_Product_ID, int M_AttributeSetInstance_ID, + int M_InventoryLine_ID, int M_CostElement_ID, + BigDecimal Amt, BigDecimal Qty, + String Description, String trxName) + { + // Delete Unprocessed zero Differences + String sql = "DELETE M_CostDetail " + + "WHERE Processed='N' AND COALESCE(DeltaAmt,0)=0 AND COALESCE(DeltaQty,0)=0" + + " AND M_InventoryLine_ID=" + M_InventoryLine_ID + + " AND C_AcctSchema_ID =" + as.getC_AcctSchema_ID() + + " AND M_AttributeSetInstance_ID=" + M_AttributeSetInstance_ID; + int no = DB.executeUpdate(sql, trxName); + if (no != 0) + s_log.config("Deleted #" + no); + MCostDetail cd = get (as.getCtx(), "M_InventoryLine_ID=?", + M_InventoryLine_ID, M_AttributeSetInstance_ID, as.getC_AcctSchema_ID(), trxName); + // + if (cd == null) // createNew + { + cd = new MCostDetail (as, AD_Org_ID, + M_Product_ID, M_AttributeSetInstance_ID, + M_CostElement_ID, + Amt, Qty, Description, trxName); + cd.setM_InventoryLine_ID(M_InventoryLine_ID); + } + else + { + // MZ Goodwill + // set deltaAmt=Amt, deltaQty=qty, and set Cost Detail for Amt and Qty + cd.setDeltaAmt(Amt.subtract(cd.getAmt())); + cd.setDeltaQty(Qty.subtract(cd.getQty())); + if (cd.isDelta()) + { + cd.setProcessed(false); + cd.setAmt(Amt); + cd.setQty(Qty); + } + // end MZ + else + return true; // nothing to do + } + boolean ok = cd.save(); + if (ok && !cd.isProcessed()) + { + MClient client = MClient.get(as.getCtx(), as.getAD_Client_ID()); + if (client.isCostImmediate()) + cd.process(); + } + s_log.config("(" + ok + ") " + cd); + return ok; + } // createInventory + + /** + * Create New Order Cost Detail for Movements. + * Called from Doc_Movement + * @param as accounting schema + * @param AD_Org_ID org + * @param M_Product_ID product + * @param M_AttributeSetInstance_ID asi + * @param M_MovementLine_ID movement + * @param M_CostElement_ID optional cost element for Freight + * @param Amt amt total amount + * @param Qty qty + * @param from if true the from (reduction) + * @param Description optional description + * @param trxName transaction + * @return true if no error + */ + public static boolean createMovement (MAcctSchema as, int AD_Org_ID, + int M_Product_ID, int M_AttributeSetInstance_ID, + int M_MovementLine_ID, int M_CostElement_ID, + BigDecimal Amt, BigDecimal Qty, boolean from, + String Description, String trxName) + { + // Delete Unprocessed zero Differences + String sql = "DELETE M_CostDetail " + + "WHERE Processed='N' AND COALESCE(DeltaAmt,0)=0 AND COALESCE(DeltaQty,0)=0" + + " AND M_MovementLine_ID=" + M_MovementLine_ID + + " AND IsSOTrx=" + (from ? "'Y'" : "'N'") + + " AND C_AcctSchema_ID =" + as.getC_AcctSchema_ID() + + " AND M_AttributeSetInstance_ID=" + M_AttributeSetInstance_ID; + int no = DB.executeUpdate(sql, trxName); + if (no != 0) + s_log.config("Deleted #" + no); + MCostDetail cd = get (as.getCtx(), "M_MovementLine_ID=? AND IsSOTrx=" + + (from ? "'Y'" : "'N'"), + M_MovementLine_ID, M_AttributeSetInstance_ID, as.getC_AcctSchema_ID(), trxName); + // + if (cd == null) // createNew + { + cd = new MCostDetail (as, AD_Org_ID, + M_Product_ID, M_AttributeSetInstance_ID, + M_CostElement_ID, + Amt, Qty, Description, trxName); + cd.setM_MovementLine_ID (M_MovementLine_ID); + cd.setIsSOTrx(from); + } + else + { + // MZ Goodwill + // set deltaAmt=Amt, deltaQty=qty, and set Cost Detail for Amt and Qty + cd.setDeltaAmt(Amt.subtract(cd.getAmt())); + cd.setDeltaQty(Qty.subtract(cd.getQty())); + if (cd.isDelta()) + { + cd.setProcessed(false); + cd.setAmt(Amt); + cd.setQty(Qty); + } + // end MZ + else + return true; // nothing to do + } + boolean ok = cd.save(); + if (ok && !cd.isProcessed()) + { + MClient client = MClient.get(as.getCtx(), as.getAD_Client_ID()); + if (client.isCostImmediate()) + cd.process(); + } + s_log.config("(" + ok + ") " + cd); + return ok; + } // createMovement + + /** + * Create New Order Cost Detail for Production. + * Called from Doc_Production + * @param as accounting schema + * @param AD_Org_ID org + * @param M_Product_ID product + * @param M_AttributeSetInstance_ID asi + * @param M_ProductionLine_ID production line + * @param M_CostElement_ID optional cost element + * @param Amt amt total amount + * @param Qty qty + * @param Description optional description + * @param trxName transaction + * @return true if no error + */ + public static boolean createProduction (MAcctSchema as, int AD_Org_ID, + int M_Product_ID, int M_AttributeSetInstance_ID, + int M_ProductionLine_ID, int M_CostElement_ID, + BigDecimal Amt, BigDecimal Qty, + String Description, String trxName) + { + // Delete Unprocessed zero Differences + String sql = "DELETE M_CostDetail " + + "WHERE Processed='N' AND COALESCE(DeltaAmt,0)=0 AND COALESCE(DeltaQty,0)=0" + + " AND M_ProductionLine_ID=" + M_ProductionLine_ID + + " AND C_AcctSchema_ID =" + as.getC_AcctSchema_ID() + + " AND M_AttributeSetInstance_ID=" + M_AttributeSetInstance_ID; + int no = DB.executeUpdate(sql, trxName); + if (no != 0) + s_log.config("Deleted #" + no); + MCostDetail cd = get (as.getCtx(), "M_ProductionLine_ID=?", + M_ProductionLine_ID, M_AttributeSetInstance_ID, as.getC_AcctSchema_ID(), trxName); + // + if (cd == null) // createNew + { + cd = new MCostDetail (as, AD_Org_ID, + M_Product_ID, M_AttributeSetInstance_ID, + M_CostElement_ID, + Amt, Qty, Description, trxName); + cd.setM_ProductionLine_ID(M_ProductionLine_ID); + } + else + { + // MZ Goodwill + // set deltaAmt=Amt, deltaQty=qty, and set Cost Detail for Amt and Qty + cd.setDeltaAmt(Amt.subtract(cd.getAmt())); + cd.setDeltaQty(Qty.subtract(cd.getQty())); + if (cd.isDelta()) + { + cd.setProcessed(false); + cd.setAmt(Amt); + cd.setQty(Qty); + } + // end MZ + else + return true; // nothing to do + } + boolean ok = cd.save(); + if (ok && !cd.isProcessed()) + { + MClient client = MClient.get(as.getCtx(), as.getAD_Client_ID()); + if (client.isCostImmediate()) + cd.process(); + } + s_log.config("(" + ok + ") " + cd); + return ok; + } // createProduction + + + /************************************************************************** + * Get Cost Detail + * @param ctx context + * @param whereClause where clause + * @param ID 1st parameter + * @param M_AttributeSetInstance_ID ASI + * @param trxName trx + * @return cost detail + */ + public static MCostDetail get (Properties ctx, String whereClause, + int ID, int M_AttributeSetInstance_ID, int C_AcctSchema_ID, String trxName) + { + String sql = "SELECT * FROM M_CostDetail WHERE " + whereClause + + " AND M_AttributeSetInstance_ID=?" + + " AND C_AcctSchema_ID=?"; + MCostDetail retValue = null; + PreparedStatement pstmt = null; + try + { + pstmt = DB.prepareStatement (sql, null); + pstmt.setInt (1, ID); + pstmt.setInt (2, M_AttributeSetInstance_ID); + pstmt.setInt (3, C_AcctSchema_ID); + ResultSet rs = pstmt.executeQuery (); + if (rs.next ()) + retValue = new MCostDetail (ctx, rs, trxName); + rs.close (); + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + s_log.log (Level.SEVERE, sql + " - " + ID, e); + } + try + { + if (pstmt != null) + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + pstmt = null; + } + return retValue; + } // get + + /** + * Process Cost Details for product + * @param product product + * @param trxName transaction + * @return true if no error + */ + public static boolean processProduct (MProduct product, String trxName) + { + String sql = "SELECT * FROM M_CostDetail " + + "WHERE M_Product_ID=?" + + " AND Processed='N' " + + "ORDER BY C_AcctSchema_ID, M_CostElement_ID, AD_Org_ID, M_AttributeSetInstance_ID, Created"; + int counterOK = 0; + int counterError = 0; + PreparedStatement pstmt = null; + try + { + pstmt = DB.prepareStatement (sql, trxName); + pstmt.setInt (1, product.getM_Product_ID()); + ResultSet rs = pstmt.executeQuery (); + while (rs.next ()) + { + MCostDetail cd = new MCostDetail(product.getCtx(), rs, trxName); + if (cd.process()) // saves + counterOK++; + else + counterError++; + } + rs.close (); + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + s_log.log (Level.SEVERE, sql, e); + counterError++; + } + + try + { + if (pstmt != null) + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + pstmt = null; + } + s_log.config("OK=" + counterOK + ", Errors=" + counterError); + return counterError == 0; + } // processProduct + + /** Logger */ + private static CLogger s_log = CLogger.getCLogger (MCostDetail.class); + + + /************************************************************************** + * Standard Constructor + * @param ctx context + * @param M_CostDetail_ID id + * @param trxName trx + */ + public MCostDetail (Properties ctx, int M_CostDetail_ID, String trxName) + { + super (ctx, M_CostDetail_ID, trxName); + if (M_CostDetail_ID == 0) + { + // setC_AcctSchema_ID (0); + // setM_Product_ID (0); + setM_AttributeSetInstance_ID (0); + // setC_OrderLine_ID (0); + // setM_InOutLine_ID(0); + // setC_InvoiceLine_ID (0); + setProcessed (false); + setAmt (Env.ZERO); + setQty (Env.ZERO); + setIsSOTrx (false); + setDeltaAmt (Env.ZERO); + setDeltaQty (Env.ZERO); + } + } // MCostDetail + + /** + * Load Constructor + * @param ctx context + * @param rs result set + * @param trxName trx + */ + public MCostDetail (Properties ctx, ResultSet rs, String trxName) + { + super (ctx, rs, trxName); + } // MCostDetail + + /** + * New Constructor + * @param as accounting schema + * @param AD_Org_ID org + * @param M_Product_ID product + * @param M_AttributeSetInstance_ID asi + * @param M_CostElement_ID optional cost element for Freight + * @param Amt amt + * @param Qty qty + * @param Description optional description + * @param trxName transaction + */ + public MCostDetail (MAcctSchema as, int AD_Org_ID, + int M_Product_ID, int M_AttributeSetInstance_ID, + int M_CostElement_ID, BigDecimal Amt, BigDecimal Qty, + String Description, String trxName) + { + this (as.getCtx(), 0, trxName); + setClientOrg(as.getAD_Client_ID(), AD_Org_ID); + setC_AcctSchema_ID (as.getC_AcctSchema_ID()); + setM_Product_ID (M_Product_ID); + setM_AttributeSetInstance_ID (M_AttributeSetInstance_ID); + // + setM_CostElement_ID(M_CostElement_ID); + // + setAmt (Amt); + setQty (Qty); + setDescription(Description); + } // MCostDetail + + /** + * Set Amt + * @param Amt amt + */ + public void setAmt (BigDecimal Amt) + { + if (isProcessed()) + throw new IllegalStateException("Cannot change Amt - processed"); + if (Amt == null) + super.setAmt (Env.ZERO); + else + super.setAmt (Amt); + } // setAmt + + /** + * Set Qty + * @param Qty qty + */ + public void setQty (BigDecimal Qty) + { + if (isProcessed()) + throw new IllegalStateException("Cannot change Qty - processed"); + if (Qty == null) + super.setQty (Env.ZERO); + else + super.setQty (Qty); + } // setQty + + /** + * Is Order + * @return true if order line + */ + public boolean isOrder() + { + return getC_OrderLine_ID() != 0; + } // isOrder + + /** + * Is Invoice + * @return true if invoice line + */ + public boolean isInvoice() + { + return getC_InvoiceLine_ID() != 0; + } // isInvoice + + /** + * Is Shipment + * @return true if sales order shipment + */ + public boolean isShipment() + { + return isSOTrx() && getM_InOutLine_ID() != 0; + } // isShipment + + /** + * Is this a Delta Record (previously processed)? + * @return true if delta is not null + */ + public boolean isDelta() + { + return !(getDeltaAmt().signum() == 0 + && getDeltaQty().signum() == 0); + } // isDelta + + + /** + * After Save + * @param newRecord new + * @param success success + * @return true + */ + protected boolean afterSave (boolean newRecord, boolean success) + { + return true; + } // afterSave + + /** + * Before Delete + * @return false if processed + */ + protected boolean beforeDelete () + { + return !isProcessed(); + } // beforeDelete + + + /** + * String Representation + * @return info + */ + public String toString () + { + StringBuffer sb = new StringBuffer ("MCostDetail["); + sb.append (get_ID()); + if (getC_OrderLine_ID() != 0) + sb.append (",C_OrderLine_ID=").append (getC_OrderLine_ID()); + if (getM_InOutLine_ID() != 0) + sb.append (",M_InOutLine_ID=").append (getM_InOutLine_ID()); + if (getC_InvoiceLine_ID() != 0) + sb.append (",C_InvoiceLine_ID=").append (getC_InvoiceLine_ID()); + if (getC_ProjectIssue_ID() != 0) + sb.append (",C_ProjectIssue_ID=").append (getC_ProjectIssue_ID()); + if (getM_MovementLine_ID() != 0) + sb.append (",M_MovementLine_ID=").append (getM_MovementLine_ID()); + if (getM_InventoryLine_ID() != 0) + sb.append (",M_InventoryLine_ID=").append (getM_InventoryLine_ID()); + if (getM_ProductionLine_ID() != 0) + sb.append (",M_ProductionLine_ID=").append (getM_ProductionLine_ID()); + sb.append(",Amt=").append(getAmt()) + .append(",Qty=").append(getQty()); + if (isDelta()) + sb.append(",DeltaAmt=").append(getDeltaAmt()) + .append(",DeltaQty=").append(getDeltaQty()); + sb.append ("]"); + return sb.toString (); + } // toString + + + /************************************************************************** + * Process Cost Detail Record. + * The record is saved if processed. + * @return true if processed + */ + public synchronized boolean process() + { + if (isProcessed()) + { + log.info("Already processed"); + return true; + } + boolean ok = false; + + // get costing level for product + MAcctSchema as = MAcctSchema.get(getCtx(), getC_AcctSchema_ID()); + MProduct product = MProduct.get(getCtx(), getM_Product_ID()); + String CostingLevel = product.getCostingLevel(as); + // Org Element + int Org_ID = getAD_Org_ID(); + int M_ASI_ID = getM_AttributeSetInstance_ID(); + if (MAcctSchema.COSTINGLEVEL_Client.equals(CostingLevel)) + { + Org_ID = 0; + M_ASI_ID = 0; + } + else if (MAcctSchema.COSTINGLEVEL_Organization.equals(CostingLevel)) + M_ASI_ID = 0; + else if (MAcctSchema.COSTINGLEVEL_BatchLot.equals(CostingLevel)) + Org_ID = 0; + + // Create Material Cost elements + if (getM_CostElement_ID() == 0) + { + MCostElement[] ces = MCostElement.getCostingMethods(this); + for (int i = 0; i < ces.length; i++) + { + MCostElement ce = ces[i]; + ok = process (as, product, ce, Org_ID, M_ASI_ID); + if (!ok) + break; + } + } // Material Cost elements + else + { + MCostElement ce = MCostElement.get(getCtx(), getM_CostElement_ID()); + ok = process (as, product, ce, Org_ID, M_ASI_ID); + } + + // Save it + if (ok) + { + setDeltaAmt(null); + setDeltaQty(null); + setProcessed(true); + ok = save(); + } + log.info(ok + " - " + toString()); + return ok; + } // process + + /** + * Process cost detail for cost record + * @param as accounting schema + * @param product product + * @param ce cost element + * @param Org_ID org - corrected for costing level + * @param M_ASI_ID - asi corrected for costing level + * @return true if cost ok + */ + private boolean process (MAcctSchema as, MProduct product, MCostElement ce, + int Org_ID, int M_ASI_ID) + { + MCost cost = MCost.get(product, M_ASI_ID, as, + Org_ID, ce.getM_CostElement_ID(), get_TrxName()); + // if (cost == null) + // cost = new MCost(product, M_ASI_ID, + // as, Org_ID, ce.getM_CostElement_ID()); + + // MZ Goodwill + // used deltaQty and deltaAmt if exist + BigDecimal qty = Env.ZERO; + BigDecimal amt = Env.ZERO; + if (isDelta()) + { + qty = getDeltaQty(); + amt = getDeltaAmt(); + } + else + { + qty = getQty(); + amt = getAmt(); + } + // end MZ + + int precision = as.getCostingPrecision(); + BigDecimal price = amt; + if (qty.signum() != 0) + price = amt.divide(qty, precision, BigDecimal.ROUND_HALF_UP); + + /** All Costing Methods + if (ce.isAverageInvoice()) + else if (ce.isAveragePO()) + else if (ce.isFifo()) + else if (ce.isLifo()) + else if (ce.isLastInvoice()) + else if (ce.isLastPOPrice()) + else if (ce.isStandardCosting()) + else if (ce.isUserDefined()) + else if (!ce.isCostingMethod()) + **/ + + // *** Purchase Order Detail Record *** + if (getC_OrderLine_ID() != 0) + { + boolean isReturnTrx = qty.signum() < 0; + + if (ce.isAveragePO()) + { + cost.setWeightedAverage(amt, qty); + log.finer("PO - AveragePO - " + cost); + } + else if (ce.isLastPOPrice()) + { + if(!isReturnTrx) + { + if (qty.signum() != 0) + cost.setCurrentCostPrice(price); + else + { + BigDecimal cCosts = cost.getCurrentCostPrice().add(amt); + cost.setCurrentCostPrice(cCosts); + } + } + cost.add(amt, qty); + log.finer("PO - LastPO - " + cost); + } + else if (ce.isUserDefined()) + { + // Interface + log.finer("PO - UserDef - " + cost); + } + else if (!ce.isCostingMethod()) + { + log.finer("PO - " + ce + " - " + cost); + } + // else + // log.warning("PO - " + ce + " - " + cost); + } + + // *** AP Invoice Detail Record *** + else if (getC_InvoiceLine_ID() != 0) + { + boolean isReturnTrx = qty.signum() < 0; + + if (ce.isAverageInvoice()) + { + cost.setWeightedAverage(amt, qty); + log.finer("Inv - AverageInv - " + cost); + } + else if (ce.isFifo() + || ce.isLifo()) + { + // Real ASI - costing level Org + MCostQueue cq = MCostQueue.get(product, getM_AttributeSetInstance_ID(), + as, Org_ID, ce.getM_CostElement_ID(), get_TrxName()); + cq.setCosts(amt, qty, precision); + cq.save(); + // Get Costs - costing level Org/ASI + MCostQueue[] cQueue = MCostQueue.getQueue(product, M_ASI_ID, + as, Org_ID, ce, get_TrxName()); + if (cQueue != null && cQueue.length > 0) + cost.setCurrentCostPrice(cQueue[0].getCurrentCostPrice()); + cost.add(amt, qty); + log.finer("Inv - FiFo/LiFo - " + cost); + } + else if (ce.isLastInvoice()) + { + if (!isReturnTrx) + { + if (qty.signum() != 0) + cost.setCurrentCostPrice(price); + else + { + BigDecimal cCosts = cost.getCurrentCostPrice().add(amt); + cost.setCurrentCostPrice(cCosts); + } + } + cost.add(amt, qty); + log.finer("Inv - LastInv - " + cost); + } + else if (ce.isStandardCosting()) + { + // Update cost record only if newly created. + // Elsewhere we risk to set the CurrentCostPrice to an undesired price. + if (cost.is_new() + && cost.getCurrentCostPrice().signum() == 0 + && cost.getCurrentCostPriceLL().signum() == 0) + { + cost.setCurrentCostPrice(price); + // seed initial price + if (cost.getCurrentCostPrice().signum() == 0) + { + cost.setCurrentCostPrice(MCost.getSeedCosts(product, M_ASI_ID, + as, Org_ID, ce.getCostingMethod(), getC_OrderLine_ID())); + log.finest("Inv - Standard - CurrentCostPrice(seed)="+cost.getCurrentCostPrice()+", price="+price); + } + } + cost.add(amt, qty); + log.finer("Inv - Standard - " + cost); + } + else if (ce.isUserDefined()) + { + // Interface + cost.add(amt, qty); + log.finer("Inv - UserDef - " + cost); + } + else if (!ce.isCostingMethod()) // Cost Adjustments + { + // AZ Goodwill + //get costing method for product + String costingMethod = product.getCostingMethod(as); + if (MAcctSchema.COSTINGMETHOD_AveragePO.equals(costingMethod) || + MAcctSchema.COSTINGMETHOD_AverageInvoice.equals(costingMethod)) + { + /** Problem with Landed Costs: certain cost element may not occur in every purchases, + * causing the average calculation of that cost element wrongly took the current qty. + * + * Solution: + * Make sure the current qty is reflecting the actual qty in storage + */ + String sql = "SELECT COALESCE(SUM(QtyOnHand),0) FROM M_Storage" + + " WHERE AD_Client_ID=" + cost.getAD_Client_ID() + + " AND M_Product_ID=" + cost.getM_Product_ID(); + //Costing Level + String CostingLevel = product.getCostingLevel(as); + if (MAcctSchema.COSTINGLEVEL_Organization.equals(CostingLevel)) + sql += " AND AD_Org_ID=" + cost.getAD_Org_ID(); + else if (MAcctSchema.COSTINGLEVEL_BatchLot.equals(CostingLevel)) + sql += " AND M_AttributeSetInstance_ID=" + M_ASI_ID; + // + BigDecimal qtyOnhand = DB.getSQLValueBD(get_TrxName(), sql); + if (qtyOnhand.signum() != 0) + { + BigDecimal oldSum = cost.getCurrentCostPrice().multiply(cost.getCurrentQty()); + BigDecimal sumAmt = oldSum.add(amt); // amt is total already + BigDecimal costs = sumAmt.divide(qtyOnhand, precision, BigDecimal.ROUND_HALF_UP); + cost.setCurrentCostPrice(costs); + } + cost.setCumulatedAmt(cost.getCumulatedAmt().add(amt)); + cost.setCumulatedQty(cost.getCumulatedQty().add(qty)); + cost.setCurrentQty(qtyOnhand); + } + else //original logic from Compiere + { + BigDecimal cCosts = cost.getCurrentCostPrice().add(amt); + cost.setCurrentCostPrice(cCosts); + cost.add(amt, qty); + } + // end AZ + log.finer("Inv - Landed Costs - " + cost); + } + // else + // log.warning("Inv - " + ce + " - " + cost); + } + + // *** Qty Adjustment Detail Record *** + else if (getM_InOutLine_ID() != 0 // AR Shipment Detail Record + || getM_MovementLine_ID() != 0 + || getM_InventoryLine_ID() != 0 + || getM_ProductionLine_ID() != 0 + || getC_ProjectIssue_ID() != 0 + || getPP_Cost_Collector_ID() != 0) + { + boolean addition = qty.signum() > 0; + // + if (ce.isAverageInvoice()) + { + if (addition) + cost.setWeightedAverage(amt, qty); + else + cost.setCurrentQty(cost.getCurrentQty().add(qty)); + log.finer("QtyAdjust - AverageInv - " + cost); + } + else if (ce.isAveragePO()) + { + if (addition) + cost.setWeightedAverage(amt, qty); + else + cost.setCurrentQty(cost.getCurrentQty().add(qty)); + log.finer("QtyAdjust - AveragePO - " + cost); + } + else if (ce.isFifo() || ce.isLifo()) + { + if (addition) + { + // Real ASI - costing level Org + MCostQueue cq = MCostQueue.get(product, getM_AttributeSetInstance_ID(), + as, Org_ID, ce.getM_CostElement_ID(), get_TrxName()); + cq.setCosts(amt, qty, precision); + cq.save(); + } + else + { + // Adjust Queue - costing level Org/ASI + MCostQueue.adjustQty(product, M_ASI_ID, + as, Org_ID, ce, qty.negate(), get_TrxName()); + } + // Get Costs - costing level Org/ASI + MCostQueue[] cQueue = MCostQueue.getQueue(product, M_ASI_ID, + as, Org_ID, ce, get_TrxName()); + if (cQueue != null && cQueue.length > 0) + cost.setCurrentCostPrice(cQueue[0].getCurrentCostPrice()); + cost.setCurrentQty(cost.getCurrentQty().add(qty)); + log.finer("QtyAdjust - FiFo/Lifo - " + cost); + } + else if (ce.isLastInvoice()) + { + cost.setCurrentQty(cost.getCurrentQty().add(qty)); + log.finer("QtyAdjust - LastInv - " + cost); + } + else if (ce.isLastPOPrice()) + { + cost.setCurrentQty(cost.getCurrentQty().add(qty)); + log.finer("QtyAdjust - LastPO - " + cost); + } + else if (ce.isStandardCosting()) + { + if (addition) + { + cost.add(amt, qty); + // Initial + if (cost.getCurrentCostPrice().signum() == 0 + && cost.getCurrentCostPriceLL().signum() == 0 + && cost.is_new()) + { + cost.setCurrentCostPrice(price); + log.finest("QtyAdjust - Standard - CurrentCostPrice="+price); + } + } + else + { + cost.setCurrentQty(cost.getCurrentQty().add(qty)); + } + log.finer("QtyAdjust - Standard - " + cost); + } + else if (ce.isUserDefined()) + { + // Interface + if (addition) + cost.add(amt, qty); + else + cost.setCurrentQty(cost.getCurrentQty().add(qty)); + log.finer("QtyAdjust - UserDef - " + cost); + } + else if (!ce.isCostingMethod()) + { + // Should not happen + log.finer("QtyAdjust - ?none? - " + cost); + } + else + log.warning("QtyAdjust - " + ce + " - " + cost); + + //AZ Goodwill + //Also update Landed Costs to reflect the actual qty in storage + String costingMethod = ce.getCostingMethod(); + if (MAcctSchema.COSTINGMETHOD_AveragePO.equals(costingMethod) || + MAcctSchema.COSTINGMETHOD_AverageInvoice.equals(costingMethod)) + { + MCostElement[] lce = MCostElement.getNonCostingMethods(this); + if (lce.length > 0) + { + String sql = "SELECT COALESCE(SUM(QtyOnHand),0) FROM M_Storage" + + " WHERE AD_Client_ID=" + cost.getAD_Client_ID() + + " AND M_Product_ID=" + cost.getM_Product_ID(); + //Costing Level + String CostingLevel = product.getCostingLevel(as); + if (MAcctSchema.COSTINGLEVEL_Organization.equals(CostingLevel)) + sql += " AND AD_Org_ID=" + cost.getAD_Org_ID(); + else if (MAcctSchema.COSTINGLEVEL_BatchLot.equals(CostingLevel)) + sql += " AND M_AttributeSetInstance_ID=" + M_ASI_ID; + // + BigDecimal qtyOnhand = DB.getSQLValueBD(get_TrxName(), sql); + for (int i = 0 ; i < lce.length ; i++) + { + MCost lCost = MCost.get(getCtx(), cost.getAD_Client_ID(), cost.getAD_Org_ID(), + cost.getM_Product_ID(), cost.getM_CostType_ID(), cost.getC_AcctSchema_ID(), + lce[i].getM_CostElement_ID(), cost.getM_AttributeSetInstance_ID(), get_TrxName()); + if (lCost != null) + { + if (qtyOnhand.signum() != 0) + { + // new average cost + BigDecimal oldSum = lCost.getCurrentCostPrice().multiply(lCost.getCurrentQty()); + BigDecimal costs = oldSum.divide(qtyOnhand, precision, BigDecimal.ROUND_HALF_UP); + lCost.setCurrentCostPrice(costs); + } + lCost.setCurrentQty(qtyOnhand); + if (!lCost.save()) + { + log.warning("Update Landed Costs (Qty) fail: " + lce + " - " + lCost); + return false; + } + } + } + }//end-if + } + //end AZ + } + else // unknown or no id + { + log.warning("Unknown Type: " + toString()); + return false; + } + return cost.save(); + } // process + + // Elaine 2008/6/20 + protected boolean afterDelete (boolean success) + { + if(success) + { + // recalculate MCost + boolean ok = false; + // get costing level for product + MAcctSchema as = new MAcctSchema (getCtx(), getC_AcctSchema_ID(), null); + MProduct product = MProduct.get(getCtx(), getM_Product_ID()); + String CostingLevel = product.getCostingLevel(as); + // Org Element + int Org_ID = getAD_Org_ID(); + int M_ASI_ID = getM_AttributeSetInstance_ID(); + if (MAcctSchema.COSTINGLEVEL_Client.equals(CostingLevel)) + { + Org_ID = 0; + M_ASI_ID = 0; + } + else if (MAcctSchema.COSTINGLEVEL_Organization.equals(CostingLevel)) + M_ASI_ID = 0; + else if (MAcctSchema.COSTINGLEVEL_BatchLot.equals(CostingLevel)) + Org_ID = 0; + + // Create Material Cost elements + if (getM_CostElement_ID() == 0) + { + MCostElement[] ces = MCostElement.getCostingMethods(this); + for (int i = 0; i < ces.length; i++) + { + MCostElement ce = ces[i]; + ok = process (as, product, ce, Org_ID, M_ASI_ID); + if (!ok) + break; + } + } // Material Cost elements + else + { + MCostElement ce = MCostElement.get(getCtx(), getM_CostElement_ID()); + ok = process (as, product, ce, Org_ID, M_ASI_ID); + } + + return ok; + } + + return super.afterDelete(success); + } + // + +} // MCostDetail