/****************************************************************************** * Product: iDempiere ERP & CRM Smart Business Solution * * Copyright (C) 1999-2012 iDempiere 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. * *****************************************************************************/ package org.compiere.model; import java.math.BigDecimal; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import java.util.Properties; import java.util.logging.Level; import org.adempiere.util.IReservationTracer; import org.compiere.util.CLogger; import org.compiere.util.DB; import org.compiere.util.Env; /** * Inventory reservation storage model */ public class MStorageReservation extends X_M_StorageReservation { /** * generated serial id */ private static final long serialVersionUID = 8179093165315835613L; /** * Get Reservation Storage * @param ctx context * @param M_Warehouse_ID warehouse * @param M_Product_ID product * @param M_AttributeSetInstance_ID instance * @param isSOTrx * @param trxName transaction * @return existing MStorageReservation or null */ public static MStorageReservation get (Properties ctx, int M_Warehouse_ID, int M_Product_ID, int M_AttributeSetInstance_ID, boolean isSOTrx, String trxName) { MStorageReservation retValue = null; String sql = "SELECT * FROM M_StorageReservation " + "WHERE M_Warehouse_ID=? AND M_Product_ID=? AND IsSOTrx=? AND "; if (M_AttributeSetInstance_ID == 0) sql += "(M_AttributeSetInstance_ID=? OR M_AttributeSetInstance_ID IS NULL)"; else sql += "M_AttributeSetInstance_ID=?"; PreparedStatement pstmt = null; ResultSet rs = null; try { pstmt = DB.prepareStatement (sql, trxName); pstmt.setInt (1, M_Warehouse_ID); pstmt.setInt (2, M_Product_ID); pstmt.setString (3, isSOTrx ? "Y" : "N"); pstmt.setInt (4, M_AttributeSetInstance_ID); rs = pstmt.executeQuery (); if (rs.next ()) retValue = new MStorageReservation (ctx, rs, trxName); } catch (SQLException ex) { s_log.log(Level.SEVERE, sql, ex); } finally { DB.close(rs, pstmt); rs = null; pstmt = null; } if (retValue == null) { if (s_log.isLoggable(Level.FINE)) s_log.fine("Not Found - M_Warehouse_ID=" + M_Warehouse_ID + ", M_Product_ID=" + M_Product_ID + ", M_AttributeSetInstance_ID=" + M_AttributeSetInstance_ID + ", IsSOTrx=" + isSOTrx); } else { if (s_log.isLoggable(Level.FINE)) s_log.fine("M_Warehouse_ID=" + M_Warehouse_ID + ", M_Product_ID=" + M_Product_ID + ", M_AttributeSetInstance_ID=" + M_AttributeSetInstance_ID + ", IsSOTrx=" + isSOTrx); } return retValue; } // get private static CLogger s_log = CLogger.getCLogger(MStorageReservation.class); /** * UUID based Constructor * @param ctx Context * @param M_StorageReservation_UU UUID key * @param trxName Transaction */ public MStorageReservation(Properties ctx, String M_StorageReservation_UU, String trxName) { super(ctx, M_StorageReservation_UU, trxName); } /** * @param ctx * @param M_StorageReservation_ID * @param trxName */ public MStorageReservation(Properties ctx, int M_StorageReservation_ID, String trxName) { super(ctx, M_StorageReservation_ID, trxName); } /** * @param ctx * @param rs * @param trxName */ public MStorageReservation(Properties ctx, ResultSet rs, String trxName) { super(ctx, rs, trxName); } /** * NEW MStorageReservation Constructor * @param warehouse (parent) warehouse * @param M_Product_ID product * @param M_AttributeSetInstance_ID attribute * @param isSOTrx */ private MStorageReservation (MWarehouse warehouse, int M_Product_ID, int M_AttributeSetInstance_ID, boolean isSOTrx) { this (warehouse.getCtx(), 0, warehouse.get_TrxName()); setClientOrg(warehouse); setM_Warehouse_ID(warehouse.getM_Warehouse_ID()); setM_Product_ID (M_Product_ID); setM_AttributeSetInstance_ID (M_AttributeSetInstance_ID); setIsSOTrx(isSOTrx); setQty(Env.ZERO); } // MStorageReservation /** * Get Reservation Storage for Product on specified Warehouse * @param ctx * @param m_Warehouse_ID * @param m_Product_ID * @param i not use * @param trxName * @return array of MStorageReservation */ public static MStorageReservation[] get(Properties ctx, int m_Warehouse_ID, int m_Product_ID, int i, String trxName) { String sqlWhere = "M_Product_ID=? AND M_Warehouse_ID=?"; List list = new Query(ctx, MStorageReservation.Table_Name, sqlWhere, trxName) .setParameters(m_Product_ID, m_Warehouse_ID) .list(); MStorageReservation[] retValue = new MStorageReservation[list.size()]; list.toArray(retValue); return retValue; } /** * Get Reservation Storage for Product across warehouses * @param ctx context * @param M_Product_ID product * @param trxName transaction * @return array of MStorageReservation */ public static MStorageReservation[] getOfProduct (Properties ctx, int M_Product_ID, String trxName) { String sqlWhere = "M_Product_ID=?"; List list = new Query(ctx, MStorageReservation.Table_Name, sqlWhere, trxName) .setParameters(M_Product_ID) .list(); MStorageReservation[] retValue = new MStorageReservation[list.size()]; list.toArray(retValue); return retValue; } // getOfProduct /** * Get Quantity Reserved/Ordered of Warehouse * @param M_Product_ID * @param M_Warehouse_ID * @param M_AttributeSetInstance_ID M_AttributeSetInstance_ID filter if > 0 * @param isSOTrx - true to get reserved, false to get ordered * @param trxName * @return quantity reserved/ordered */ public static BigDecimal getQty(int M_Product_ID, int M_Warehouse_ID, int M_AttributeSetInstance_ID, boolean isSOTrx, String trxName) { ArrayList params = new ArrayList(); StringBuilder sql = new StringBuilder(); sql.append(" SELECT SUM(Qty) FROM M_StorageReservation sr") .append(" WHERE sr.M_Product_ID=? AND sr.M_Warehouse_ID=?") .append(" AND sr.IsSOTrx=?"); params.add(M_Product_ID); params.add(M_Warehouse_ID); params.add(isSOTrx ? "Y" : "N"); // With ASI if (M_AttributeSetInstance_ID != 0) { sql.append(" AND M_AttributeSetInstance_ID=?"); params.add(M_AttributeSetInstance_ID); } BigDecimal qty = DB.getSQLValueBDEx(trxName, sql.toString(), params); if (qty==null) qty = Env.ZERO; return qty; } /** * Get Available Qty (On Hand - Reserved) * @param M_Warehouse_ID wh * @param M_Product_ID product * @param M_AttributeSetInstance_ID masi * @param trxName transaction * @return qty available (QtyOnHand-QtyReserved) */ public static BigDecimal getQtyAvailable (int M_Warehouse_ID, int M_Product_ID, int M_AttributeSetInstance_ID, String trxName) { BigDecimal qtyOnHand = MStorageOnHand.getQtyOnHandForReservation(M_Product_ID, M_Warehouse_ID, M_AttributeSetInstance_ID, trxName); BigDecimal qtyReserved = MStorageReservation.getQty(M_Product_ID, M_Warehouse_ID, M_AttributeSetInstance_ID, true, trxName); BigDecimal retValue = qtyOnHand.subtract(qtyReserved); return retValue; } /** * @param ctx * @param M_Warehouse_ID * @param M_Product_ID * @param M_AttributeSetInstance_ID * @param diffQty * @param isSOTrx * @param trxName * @return true if ok * @deprecated */ @Deprecated public static boolean add (Properties ctx, int M_Warehouse_ID, int M_Product_ID, int M_AttributeSetInstance_ID, BigDecimal diffQty, boolean isSOTrx, String trxName) { return add(ctx, M_Warehouse_ID, M_Product_ID, M_AttributeSetInstance_ID, diffQty, isSOTrx, trxName, (IReservationTracer)null); } /** * Add diffQty to quantity reserved/ordered * @param ctx * @param M_Warehouse_ID * @param M_Product_ID * @param M_AttributeSetInstance_ID * @param diffQty difference to add to quantity reserved/ordered * @param isSOTrx true for quantity reserved, false for quantity ordered * @param trxName * @param tracer * @return true if ok */ public static boolean add (Properties ctx, int M_Warehouse_ID, int M_Product_ID, int M_AttributeSetInstance_ID, BigDecimal diffQty, boolean isSOTrx, String trxName, IReservationTracer tracer){ if (diffQty == null || diffQty.signum() == 0) return true; /* Do NOT use FIFO ASI for reservation */ MProduct prd = MProduct.get(ctx, M_Product_ID); if (prd.getM_AttributeSet_ID() == 0 || ! prd.getM_AttributeSet().isInstanceAttribute()) { // Product doesn't manage attribute set, always reserved with 0 M_AttributeSetInstance_ID = 0; } // Get Storage MStorageReservation storage = getCreate (ctx, M_Warehouse_ID, M_Product_ID, M_AttributeSetInstance_ID, isSOTrx, trxName); DB.getDatabase().forUpdate(storage, 120); // Verify if (storage.getM_Warehouse_ID() != M_Warehouse_ID && storage.getM_Product_ID() != M_Product_ID && storage.getM_AttributeSetInstance_ID() != M_AttributeSetInstance_ID) { s_log.severe ("No Storage found - M_Warehouse_ID=" + M_Warehouse_ID + ",M_Product_ID=" + M_Product_ID + ",ASI=" + M_AttributeSetInstance_ID); return false; } storage.addQty(diffQty, tracer); if (s_log.isLoggable(Level.FINE)) { StringBuilder diffText = new StringBuilder("(Qty=").append(diffQty).append(") -> ").append(storage.toString()); s_log.fine(diffText.toString()); } return true; } /** * Add quantity on hand directly - not using cached value - solving IDEMPIERE-2629 * @param addition * @deprecated */ @Deprecated public void addQty(BigDecimal addition) { addQty(addition, null); } /** * Add addition to Qty of this record using direct SQL - not using cached value - solving IDEMPIERE-2629 * @param addition */ public void addQty(BigDecimal addition, IReservationTracer tracer) { final String sql = "UPDATE M_StorageReservation SET Qty=Qty+?, Updated=getDate(), UpdatedBy=? " + "WHERE M_Product_ID=? AND M_Warehouse_ID=? AND M_AttributeSetInstance_ID=? AND IsSOTrx=?"; DB.executeUpdateEx(sql, new Object[] {addition, Env.getAD_User_ID(Env.getCtx()), getM_Product_ID(), getM_Warehouse_ID(), getM_AttributeSetInstance_ID(), isSOTrx()}, get_TrxName()); load(get_TrxName()); if (tracer != null) { BigDecimal oldQty = getQty().subtract(addition); tracer.trace(oldQty, addition); } } /** * Update Reservation Storage * Called from MProjectIssue * @param ctx context * @param M_Warehouse_ID warehouse * @param M_Product_ID product * @param M_AttributeSetInstance_ID AS Instance * @param reservationAttributeSetInstance_ID reservation AS Instance * @param diffQty add * @param isSOTrx * @param trxName transaction * @return true if updated */ @Deprecated public static boolean add (Properties ctx, int M_Warehouse_ID, int M_Product_ID, int M_AttributeSetInstance_ID, int reservationAttributeSetInstance_ID, BigDecimal diffQty, boolean isSOTrx, String trxName) { return add(ctx, M_Warehouse_ID, M_Product_ID, reservationAttributeSetInstance_ID, diffQty, isSOTrx, trxName); } // add /** * Create or Get Reservation Storage * @param ctx context * @param M_Warehouse_ID * @param M_Product_ID product * @param M_AttributeSetInstance_ID instance * @param isSOTrx true for quantity reserved, false for quantity ordered * @param trxName transaction * @return existing or new MStorageReservation */ public static MStorageReservation getCreate (Properties ctx, int M_Warehouse_ID, int M_Product_ID, int M_AttributeSetInstance_ID, boolean isSOTrx, String trxName) { if (M_Warehouse_ID == 0) throw new IllegalArgumentException("M_Warehouse_ID=0"); if (M_Product_ID == 0) throw new IllegalArgumentException("M_Product_ID=0"); MStorageReservation retValue = get(ctx, M_Warehouse_ID, M_Product_ID, M_AttributeSetInstance_ID, isSOTrx, trxName); if (retValue != null) return retValue; // Insert row based on warehouse MWarehouse warehouse = new MWarehouse (ctx, M_Warehouse_ID, trxName); if (warehouse.get_ID() != M_Warehouse_ID) throw new IllegalArgumentException("Not found M_Warehouse_ID=" + M_Warehouse_ID); // retValue = new MStorageReservation (warehouse, M_Product_ID, M_AttributeSetInstance_ID, isSOTrx); retValue.saveEx(trxName); if (s_log.isLoggable(Level.FINE)) s_log.fine("New " + retValue); return retValue; } // getCreate /** * String Representation * @return info */ @Override public String toString() { StringBuilder sb = new StringBuilder("MStorageReservation[") .append("M_Warehouse_ID=").append(getM_Warehouse_ID()) .append(",M_Product_ID=").append(getM_Product_ID()) .append(",M_AttributeSetInstance_ID=").append(getM_AttributeSetInstance_ID()) .append(",IsSOTrx=").append(isSOTrx()) .append(": Qty=").append(getQty()) .append("]"); return sb.toString(); } // toString }