diff --git a/base/src/org/compiere/model/MInOut.java b/base/src/org/compiere/model/MInOut.java index 0a98c9e6d3..264f74fa57 100644 --- a/base/src/org/compiere/model/MInOut.java +++ b/base/src/org/compiere/model/MInOut.java @@ -1654,7 +1654,6 @@ public class MInOut extends X_M_InOut implements DocAction boolean needSave = false; - BigDecimal qtyASI = Env.ZERO ; MProduct product = line.getProduct(); @@ -1694,65 +1693,12 @@ public class MInOut extends X_M_InOut implements DocAction else if(getMovementType().compareTo(MInOut.MOVEMENTTYPE_VendorReturns) == 0 || getMovementType().compareTo(MInOut.MOVEMENTTYPE_CustomerShipment) == 0) { String MMPolicy = product.getMMPolicy(); - MStorage[] storages = MStorage.getAllWithASI(getCtx(), - line.getM_Product_ID(), line.getM_Locator_ID(), - MClient.MMPOLICY_FiFo.equals(MMPolicy), get_TrxName()); + Timestamp minGuaranteeDate = getMovementDate(); + MStorage[] storages = MStorage.getWarehouse(getCtx(), getM_Warehouse_ID(), line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), + minGuaranteeDate, MClient.MMPOLICY_FiFo.equals(MMPolicy), true, line.getM_Locator_ID(), get_TrxName()); BigDecimal qtyToDeliver = line.getMovementQty(); - /*for (int ii = 0; ii < storages.length; ii++) - { - MStorage storage = storages[ii]; - if (ii == 0) - { - if (storage.getQtyOnHand().compareTo(qtyToDeliver) >= 0) - { - line.setM_AttributeSetInstance_ID(storage.getM_AttributeSetInstance_ID()); - needSave = true; - log.config("Direct - " + line); - qtyToDeliver = Env.ZERO; - } - else - { - log.config("Split - " + line); - MInOutLineMA ma = new MInOutLineMA (line, - storage.getM_AttributeSetInstance_ID(), - storage.getQtyOnHand()); - if (!ma.save()) - ; - qtyToDeliver = qtyToDeliver.subtract(storage.getQtyOnHand()); - log.fine("#" + ii + ": " + ma + ", QtyToDeliver=" + qtyToDeliver); - } - } - else // create addl material allocation - { - MInOutLineMA ma = new MInOutLineMA (line, - storage.getM_AttributeSetInstance_ID(), - qtyToDeliver); - if (storage.getQtyOnHand().compareTo(qtyToDeliver) >= 0) - qtyToDeliver = Env.ZERO; - else - { - ma.setMovementQty(storage.getQtyOnHand()); - qtyToDeliver = qtyToDeliver.subtract(storage.getQtyOnHand()); - } - if (!ma.save()) - ; - log.fine("#" + ii + ": " + ma + ", QtyToDeliver=" + qtyToDeliver); - } - if (qtyToDeliver.signum() == 0) - break; - } // for all storages - */ - for (MStorage storage: storages) { - //consume ASI Zero - if (storage.getM_AttributeSetInstance_ID() == 0) - { - qtyASI = qtyASI.add(storage.getQtyOnHand()); - qtyToDeliver = qtyToDeliver.subtract(storage.getQtyOnHand()); - continue; - } - if (storage.getQtyOnHand().compareTo(qtyToDeliver) >= 0) { MInOutLineMA ma = new MInOutLineMA (line, @@ -1779,11 +1725,11 @@ public class MInOut extends X_M_InOut implements DocAction } // No AttributeSetInstance found for remainder - if (qtyToDeliver.signum() != 0 || qtyASI.signum() != 0) + if (qtyToDeliver.signum() != 0) { - MInOutLineMA ma = new MInOutLineMA (line, 0, qtyToDeliver.add(qtyASI)); + MInOutLineMA ma = new MInOutLineMA (line, 0, qtyToDeliver); if (!ma.save()) - ; + throw new IllegalStateException("Error try create ASI Reservation"); log.fine("##: " + ma); } } // outgoing Trx diff --git a/base/src/org/compiere/model/MInventory.java b/base/src/org/compiere/model/MInventory.java index 7bc335ba1f..b2ec362981 100644 --- a/base/src/org/compiere/model/MInventory.java +++ b/base/src/org/compiere/model/MInventory.java @@ -1,1012 +1,929 @@ -/****************************************************************************** - * 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; +/****************************************************************************** + * 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.io.File; +import java.math.BigDecimal; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Properties; +import java.util.logging.Level; + +import org.compiere.process.DocAction; +import org.compiere.process.DocumentEngine; +import org.compiere.util.CCache; +import org.compiere.util.DB; +import org.compiere.util.Env; +import org.compiere.util.Msg; + +/** + * Physical Inventory Model + * + * @author Jorg Janke + * @version $Id: MInventory.java,v 1.3 2006/07/30 00:51:05 jjanke Exp $ + * @author victor.perez@e-evolution.com, e-Evolution + *
  • FR [ 1948157 ] Is necessary the reference for document reverse + * @author Armen Rizal, Goodwill Consulting + *
  • BF [ 1745154 ] Cost in Reversing Material Related Docs + * @see http://sourceforge.net/tracker/?func=detail&atid=879335&aid=1948157&group_id=176962 + */ +public class MInventory extends X_M_Inventory implements DocAction +{ + /** + * Get Inventory from Cache + * @param ctx context + * @param M_Inventory_ID id + * @return MInventory + */ + public static MInventory get (Properties ctx, int M_Inventory_ID) + { + Integer key = new Integer (M_Inventory_ID); + MInventory retValue = (MInventory) s_cache.get (key); + if (retValue != null) + return retValue; + retValue = new MInventory (ctx, M_Inventory_ID, null); + if (retValue.get_ID () != 0) + s_cache.put (key, retValue); + return retValue; + } // get + + /** Cache */ + private static CCache s_cache = new CCache("M_Inventory", 5, 5); + + + /** + * Standard Constructor + * @param ctx context + * @param M_Inventory_ID id + * @param trxName transaction + */ + public MInventory (Properties ctx, int M_Inventory_ID, String trxName) + { + super (ctx, M_Inventory_ID, trxName); + if (M_Inventory_ID == 0) + { + // setName (null); + // setM_Warehouse_ID (0); // FK + setMovementDate (new Timestamp(System.currentTimeMillis())); + setDocAction (DOCACTION_Complete); // CO + setDocStatus (DOCSTATUS_Drafted); // DR + setIsApproved (false); + setMovementDate (new Timestamp(System.currentTimeMillis())); // @#Date@ + setPosted (false); + setProcessed (false); + } + } // MInventory + + /** + * Load Constructor + * @param ctx context + * @param rs result set + * @param trxName transaction + */ + public MInventory (Properties ctx, ResultSet rs, String trxName) + { + super(ctx, rs, trxName); + } // MInventory + + /** + * Warehouse Constructor + * @param wh warehouse + */ + public MInventory (MWarehouse wh) + { + this (wh.getCtx(), 0, wh.get_TrxName()); + setClientOrg(wh); + setM_Warehouse_ID(wh.getM_Warehouse_ID()); + } // MInventory + + + /** Lines */ + private MInventoryLine[] m_lines = null; + + /** + * Get Lines + * @param requery requery + * @return array of lines + */ + public MInventoryLine[] getLines (boolean requery) + { + if (m_lines != null && !requery) { + set_TrxName(m_lines, get_TrxName()); + return m_lines; + } + // + ArrayList list = new ArrayList(); + String sql = "SELECT * FROM M_InventoryLine WHERE M_Inventory_ID=? ORDER BY Line"; + PreparedStatement pstmt = null; + ResultSet rs = null; + try + { + pstmt = DB.prepareStatement (sql, get_TrxName()); + pstmt.setInt (1, getM_Inventory_ID()); + rs = pstmt.executeQuery (); + while (rs.next ()) + list.add (new MInventoryLine (getCtx(), rs, get_TrxName())); + } + catch (Exception e) + { + log.log(Level.SEVERE, sql, e); + } + finally + { + DB.close(rs, pstmt); + rs = null; pstmt = null; + } + + m_lines = new MInventoryLine[list.size ()]; + list.toArray (m_lines); + return m_lines; + } // getLines + + /** + * Add to Description + * @param description text + */ + public void addDescription (String description) + { + String desc = getDescription(); + if (desc == null) + setDescription(description); + else + setDescription(desc + " | " + description); + } // addDescription + + /** + * Overwrite Client/Org - from Import. + * @param AD_Client_ID client + * @param AD_Org_ID org + */ + public void setClientOrg (int AD_Client_ID, int AD_Org_ID) + { + super.setClientOrg(AD_Client_ID, AD_Org_ID); + } // setClientOrg + + /** + * String Representation + * @return info + */ + public String toString () + { + StringBuffer sb = new StringBuffer ("MInventory["); + sb.append (get_ID()) + .append ("-").append (getDocumentNo()) + .append (",M_Warehouse_ID=").append(getM_Warehouse_ID()) + .append ("]"); + return sb.toString (); + } // toString + + /** + * Get Document Info + * @return document info (untranslated) + */ + public String getDocumentInfo() + { + MDocType dt = MDocType.get(getCtx(), getC_DocType_ID()); + return dt.getName() + " " + getDocumentNo(); + } // getDocumentInfo + + /** + * Create PDF + * @return File or null + */ + public File createPDF () + { + try + { + File temp = File.createTempFile(get_TableName()+get_ID()+"_", ".pdf"); + return createPDF (temp); + } + catch (Exception e) + { + log.severe("Could not create PDF - " + e.getMessage()); + } + return null; + } // getPDF + + /** + * Create PDF file + * @param file output file + * @return file if success + */ + public File createPDF (File file) + { + // ReportEngine re = ReportEngine.get (getCtx(), ReportEngine.INVOICE, getC_Invoice_ID()); + // if (re == null) + return null; + // return re.getPDF(file); + } // createPDF + + + /** + * Before Save + * @param newRecord new + * @return true + */ + protected boolean beforeSave (boolean newRecord) + { + if (getC_DocType_ID() == 0) + { + MDocType types[] = MDocType.getOfDocBaseType(getCtx(), MDocType.DOCBASETYPE_MaterialPhysicalInventory); + if (types.length > 0) // get first + setC_DocType_ID(types[0].getC_DocType_ID()); + else + { + log.saveError("Error", Msg.parseTranslation(getCtx(), "@NotFound@ @C_DocType_ID@")); + return false; + } + } + return true; + } // beforeSave + + + /** + * Set Processed. + * Propergate to Lines/Taxes + * @param processed processed + */ + public void setProcessed (boolean processed) + { + super.setProcessed (processed); + if (get_ID() == 0) + return; + String sql = "UPDATE M_InventoryLine SET Processed='" + + (processed ? "Y" : "N") + + "' WHERE M_Inventory_ID=" + getM_Inventory_ID(); + int noLine = DB.executeUpdate(sql, get_TrxName()); + m_lines = null; + log.fine("Processed=" + processed + " - Lines=" + noLine); + } // setProcessed + + + /************************************************************************** + * Process document + * @param processAction document action + * @return true if performed + */ + public boolean processIt (String processAction) + { + m_processMsg = null; + DocumentEngine engine = new DocumentEngine (this, getDocStatus()); + return engine.processIt (processAction, getDocAction()); + } // processIt + + /** Process Message */ + private String m_processMsg = null; + /** Just Prepared Flag */ + private boolean m_justPrepared = false; + + /** + * Unlock Document. + * @return true if success + */ + public boolean unlockIt() + { + log.info(toString()); + setProcessing(false); + return true; + } // unlockIt + + /** + * Invalidate Document + * @return true if success + */ + public boolean invalidateIt() + { + log.info(toString()); + setDocAction(DOCACTION_Prepare); + return true; + } // invalidateIt + + /** + * Prepare Document + * @return new status (In Progress or Invalid) + */ + public String prepareIt() + { + log.info(toString()); + m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_BEFORE_PREPARE); + if (m_processMsg != null) + return DocAction.STATUS_Invalid; + + // Std Period open? + if (!MPeriod.isOpen(getCtx(), getMovementDate(), MDocType.DOCBASETYPE_MaterialPhysicalInventory)) + { + m_processMsg = "@PeriodClosed@"; + return DocAction.STATUS_Invalid; + } + MInventoryLine[] lines = getLines(false); + if (lines.length == 0) + { + m_processMsg = "@NoLines@"; + return DocAction.STATUS_Invalid; + } + + // TODO: Add up Amounts + // setApprovalAmt(); + + m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_AFTER_PREPARE); + if (m_processMsg != null) + return DocAction.STATUS_Invalid; + + m_justPrepared = true; + if (!DOCACTION_Complete.equals(getDocAction())) + setDocAction(DOCACTION_Complete); + return DocAction.STATUS_InProgress; + } // prepareIt + + /** + * Approve Document + * @return true if success + */ + public boolean approveIt() + { + log.info(toString()); + setIsApproved(true); + return true; + } // approveIt + + /** + * Reject Approval + * @return true if success + */ + public boolean rejectIt() + { + log.info(toString()); + setIsApproved(false); + return true; + } // rejectIt + + /** + * Complete Document + * @return new status (Complete, In Progress, Invalid, Waiting ..) + */ + public String completeIt() + { + // Re-Check + if (!m_justPrepared) + { + String status = prepareIt(); + if (!DocAction.STATUS_InProgress.equals(status)) + return status; + } + + m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_BEFORE_COMPLETE); + if (m_processMsg != null) + return DocAction.STATUS_Invalid; + + // Implicit Approval + if (!isApproved()) + approveIt(); + log.info(toString()); + + MInventoryLine[] lines = getLines(false); + for (MInventoryLine line : lines) + { + if (!line.isActive()) + continue; + + MProduct product = line.getProduct(); + + //Get Quantity to Inventory Inernal Use + BigDecimal qtyDiff = line.getQtyInternalUse().negate(); + //If Quantity to Inventory Internal Use = Zero Then is Physical Inventory Else is Inventory Internal Use + if (qtyDiff.signum() == 0) + qtyDiff = line.getQtyCount().subtract(line.getQtyBook()); + + //Ignore the Material Policy when is Reverse Correction + if(!isReversal()) + checkMaterialPolicy(line, qtyDiff); + + // Stock Movement - Counterpart MOrder.reserveStock + if (product != null + && product.isStocked() ) + { + log.fine("Material Transaction"); + MTransaction mtrx = null; + + //If AttributeSetInstance = Zero then create new AttributeSetInstance use Inventory Line MA else use current AttributeSetInstance + if (line.getM_AttributeSetInstance_ID() == 0 || qtyDiff.compareTo(Env.ZERO) == 0) + { + MInventoryLineMA mas[] = MInventoryLineMA.get(getCtx(), + line.getM_InventoryLine_ID(), get_TrxName()); + + for (int j = 0; j < mas.length; j++) + { + MInventoryLineMA ma = mas[j]; + BigDecimal QtyMA = ma.getMovementQty(); + BigDecimal QtyNew = QtyMA.add(qtyDiff); + log.fine("Diff=" + qtyDiff + + " - Instance OnHand=" + QtyMA + "->" + QtyNew); + + if (!MStorage.add(getCtx(), getM_Warehouse_ID(), + line.getM_Locator_ID(), + line.getM_Product_ID(), + ma.getM_AttributeSetInstance_ID(), 0, + QtyMA.negate(), Env.ZERO, Env.ZERO, get_TrxName())) + { + m_processMsg = "Cannot correct Inventory (MA)"; + return DocAction.STATUS_Invalid; + } + + // Only Update Date Last Inventory if is a Physical Inventory + if(line.getQtyInternalUse().compareTo(Env.ZERO) == 0) + { + MStorage storage = MStorage.get(getCtx(), line.getM_Locator_ID(), + line.getM_Product_ID(), ma.getM_AttributeSetInstance_ID(), get_TrxName()); + storage.setDateLastInventory(getMovementDate()); + if (!storage.save(get_TrxName())) + { + m_processMsg = "Storage not updated(2)"; + return DocAction.STATUS_Invalid; + } + } + + String m_MovementType =null; + if(QtyMA.negate().compareTo(Env.ZERO) > 0 ) + m_MovementType = MTransaction.MOVEMENTTYPE_InventoryIn; + else + m_MovementType = MTransaction.MOVEMENTTYPE_InventoryOut; + // Transaction + mtrx = new MTransaction (getCtx(), line.getAD_Org_ID(), m_MovementType, + line.getM_Locator_ID(), line.getM_Product_ID(), ma.getM_AttributeSetInstance_ID(), + QtyMA.negate(), getMovementDate(), get_TrxName()); + mtrx.setM_InventoryLine_ID(line.getM_InventoryLine_ID()); + if (!mtrx.save()) + { + m_processMsg = "Transaction not inserted(2)"; + return DocAction.STATUS_Invalid; + } + qtyDiff = QtyNew; + } + } + + //sLine.getM_AttributeSetInstance_ID() != 0 + // Fallback + if (mtrx == null) + { + //Fallback: Update Storage - see also VMatch.createMatchRecord + if (!MStorage.add(getCtx(), getM_Warehouse_ID(), + line.getM_Locator_ID(), + line.getM_Product_ID(), + line.getM_AttributeSetInstance_ID(), 0, + qtyDiff, Env.ZERO, Env.ZERO, get_TrxName())) + { + m_processMsg = "Cannot correct Inventory (MA)"; + return DocAction.STATUS_Invalid; + } + + // Only Update Date Last Inventory if is a Physical Inventory + if(line.getQtyInternalUse().compareTo(Env.ZERO) == 0) + { + MStorage storage = MStorage.get(getCtx(), line.getM_Locator_ID(), + line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), get_TrxName()); + + storage.setDateLastInventory(getMovementDate()); + if (!storage.save(get_TrxName())) + { + m_processMsg = "Storage not updated(2)"; + return DocAction.STATUS_Invalid; + } + } + + String m_MovementType =null; + if(qtyDiff.compareTo(Env.ZERO) > 0 ) + m_MovementType = MTransaction.MOVEMENTTYPE_InventoryIn; + else + m_MovementType = MTransaction.MOVEMENTTYPE_InventoryOut; + // Transaction + mtrx = new MTransaction (getCtx(), line.getAD_Org_ID(), m_MovementType, + line.getM_Locator_ID(), line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), + qtyDiff, getMovementDate(), get_TrxName()); + mtrx.setM_InventoryLine_ID(line.getM_InventoryLine_ID()); + if (!mtrx.save()) + { + m_processMsg = "Transaction not inserted(2)"; + return DocAction.STATUS_Invalid; + } + } // Fallback + } // stock movement + + + } // for all lines + + // User Validation + String valid = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_AFTER_COMPLETE); + if (valid != null) + { + m_processMsg = valid; + return DocAction.STATUS_Invalid; + } + + // Set the definite document number after completed (if needed) + setDefiniteDocumentNo(); + + // + setProcessed(true); + setDocAction(DOCACTION_Close); + return DocAction.STATUS_Completed; + } // completeIt + + /** + * Set the definite document number after completed + */ + private void setDefiniteDocumentNo() { + MDocType dt = MDocType.get(getCtx(), getC_DocType_ID()); + if (dt.isOverwriteDateOnComplete()) { + setMovementDate(new Timestamp (System.currentTimeMillis())); + } + if (dt.isOverwriteSeqOnComplete()) { + String value = DB.getDocumentNo(getC_DocType_ID(), get_TrxName(), true, this); + if (value != null) + setDocumentNo(value); + } + } + + /** + * Check Material Policy. + * (NOT USED) + * Sets line ASI + */ + private void checkMaterialPolicy(MInventoryLine line, BigDecimal qtyDiff) + { + int no = MInventoryLineMA.deleteInventoryMA(line.getM_InventoryLine_ID(), get_TrxName()); + if (no > 0) + log.config("Delete old #" + no); + + + // Check Line + boolean needSave = false; + // Attribute Set Instance + if (line.getM_AttributeSetInstance_ID() == 0) + { + MProduct product = MProduct.get(getCtx(), line.getM_Product_ID()); + if (qtyDiff.signum() > 0) // Incoming Trx + { + MAttributeSetInstance asi = new MAttributeSetInstance(getCtx(), 0, get_TrxName()); + asi.setClientOrg(getAD_Client_ID(), 0); + asi.setM_AttributeSet_ID(product.getM_AttributeSet_ID()); + if (!asi.save()) + { + throw new IllegalStateException("Error try create ASI Reservation"); + } + if (asi.save()) + { + line.setM_AttributeSetInstance_ID(asi.getM_AttributeSetInstance_ID()); + needSave = true; + } + } + else // Outgoing Trx + { + String MMPolicy = product.getMMPolicy(); + MStorage[] storages = MStorage.getWarehouse(getCtx(), getM_Warehouse_ID(), line.getM_Product_ID(), 0, + null, MClient.MMPOLICY_FiFo.equals(MMPolicy), true, line.getM_Locator_ID(), get_TrxName()); + BigDecimal qtyToDeliver = qtyDiff.negate(); + + for (MStorage storage: storages) + { + if (storage.getQtyOnHand().compareTo(qtyToDeliver) >= 0) + { + MInventoryLineMA ma = new MInventoryLineMA (line, + storage.getM_AttributeSetInstance_ID(), + qtyToDeliver); + if (!ma.save()) + { + throw new IllegalStateException("Error try create ASI Reservation"); + } + qtyToDeliver = Env.ZERO; + log.fine( ma + ", QtyToDeliver=" + qtyToDeliver); + //return; + } + else + { + MInventoryLineMA ma = new MInventoryLineMA (line, + storage.getM_AttributeSetInstance_ID(), + storage.getQtyOnHand()); + if (!ma.save()) + { + throw new IllegalStateException("Error try create ASI Reservation"); + } + qtyToDeliver = qtyToDeliver.subtract(storage.getQtyOnHand()); + log.fine( ma + ", QtyToDeliver=" + qtyToDeliver); + } + } + + // No AttributeSetInstance found for remainder + if (qtyToDeliver.signum() != 0) + { + MInventoryLineMA ma = new MInventoryLineMA (line, 0 , qtyToDeliver); + + if (!ma.save()) + throw new IllegalStateException("Error try create ASI Reservation"); + log.fine("##: " + ma); + } + } // outgoing Trx + + if (needSave && !line.save()) + log.severe("NOT saved " + line); + } // for all lines + + } // checkMaterialPolicy + + /** + * Void Document. + * @return false + */ + public boolean voidIt() + { + log.info(toString()); + // Before Void + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_VOID); + if (m_processMsg != null) + return false; + + if (DOCSTATUS_Closed.equals(getDocStatus()) + || DOCSTATUS_Reversed.equals(getDocStatus()) + || DOCSTATUS_Voided.equals(getDocStatus())) + { + m_processMsg = "Document Closed: " + getDocStatus(); + return false; + } + + // Not Processed + if (DOCSTATUS_Drafted.equals(getDocStatus()) + || DOCSTATUS_Invalid.equals(getDocStatus()) + || DOCSTATUS_InProgress.equals(getDocStatus()) + || DOCSTATUS_Approved.equals(getDocStatus()) + || DOCSTATUS_NotApproved.equals(getDocStatus()) ) + { + // Set lines to 0 + MInventoryLine[] lines = getLines(false); + for (int i = 0; i < lines.length; i++) + { + MInventoryLine line = lines[i]; + BigDecimal oldCount = line.getQtyCount(); + BigDecimal oldInternal = line.getQtyInternalUse(); + if (oldCount.compareTo(line.getQtyBook()) != 0 + || oldInternal.signum() != 0) + { + line.setQtyInternalUse(Env.ZERO); + line.setQtyCount(line.getQtyBook()); + line.addDescription("Void (" + oldCount + "/" + oldInternal + ")"); + line.save(get_TrxName()); + } + } + } + else + { + return reverseCorrectIt(); + } + + // After Void + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_VOID); + if (m_processMsg != null) + return false; + setProcessed(true); + setDocAction(DOCACTION_None); + return true; + } // voidIt + + /** + * Close Document. + * @return true if success + */ + public boolean closeIt() + { + log.info(toString()); + // Before Close + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_CLOSE); + if (m_processMsg != null) + return false; + // After Close + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_CLOSE); + if (m_processMsg != null) + return false; + + setDocAction(DOCACTION_None); + return true; + } // closeIt + + /** + * Reverse Correction + * @return false + */ + public boolean reverseCorrectIt() + { + log.info(toString()); + // Before reverseCorrect + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_REVERSECORRECT); + if (m_processMsg != null) + return false; + + MDocType dt = MDocType.get(getCtx(), getC_DocType_ID()); + if (!MPeriod.isOpen(getCtx(), getMovementDate(), dt.getDocBaseType())) + { + m_processMsg = "@PeriodClosed@"; + return false; + } + + // Deep Copy + MInventory reversal = new MInventory(getCtx(), 0, get_TrxName()); + copyValues(this, reversal, getAD_Client_ID(), getAD_Org_ID()); + reversal.setDocStatus(DOCSTATUS_Drafted); + reversal.setDocAction(DOCACTION_Complete); + reversal.setIsApproved (false); + reversal.setPosted(false); + reversal.setProcessed(false); + reversal.addDescription("{->" + getDocumentNo() + ")"); + if (!reversal.save()) + { + m_processMsg = "Could not create Inventory Reversal"; + return false; + } + reversal.setReversal(true); + + // Reverse Line Qty + MInventoryLine[] oLines = getLines(true); + for (int i = 0; i < oLines.length; i++) + { + MInventoryLine oLine = oLines[i]; + MInventoryLine rLine = new MInventoryLine(getCtx(), 0, get_TrxName()); + copyValues(oLine, rLine, oLine.getAD_Client_ID(), oLine.getAD_Org_ID()); + rLine.setM_Inventory_ID(reversal.getM_Inventory_ID()); + rLine.setParent(reversal); + rLine.setQtyBook (oLine.getQtyCount()); // switch + rLine.setQtyCount (oLine.getQtyBook()); + rLine.setQtyInternalUse (oLine.getQtyInternalUse().negate()); + + if (!rLine.save()) + { + m_processMsg = "Could not create Inventory Reversal Line"; + return false; + } + + //We need to copy MA + if (rLine.getM_AttributeSetInstance_ID() == 0) + { + MInventoryLineMA mas[] = MInventoryLineMA.get(getCtx(), + oLines[i].getM_InventoryLine_ID(), get_TrxName()); + for (int j = 0; j < mas.length; j++) + { + MInventoryLineMA ma = new MInventoryLineMA (rLine, + mas[j].getM_AttributeSetInstance_ID(), + mas[j].getMovementQty().negate()); + if (!ma.save()) + throw new IllegalStateException("Error try create ASI Reservation"); + } + } + } + // + if (!reversal.processIt(DocAction.ACTION_Complete)) + { + m_processMsg = "Reversal ERROR: " + reversal.getProcessMsg(); + return false; + } + reversal.closeIt(); + reversal.setDocStatus(DOCSTATUS_Reversed); + reversal.setDocAction(DOCACTION_None); + reversal.save(); + m_processMsg = reversal.getDocumentNo(); + + // Update Reversed (this) + addDescription("(" + reversal.getDocumentNo() + "<-)"); + // After reverseCorrect + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REVERSECORRECT); + if (m_processMsg != null) + return false; + setProcessed(true); + setDocStatus(DOCSTATUS_Reversed); // may come from void + setDocAction(DOCACTION_None); + + return true; + } // reverseCorrectIt + + /** + * Reverse Accrual + * @return false + */ + public boolean reverseAccrualIt() + { + log.info(toString()); + // Before reverseAccrual + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_REVERSEACCRUAL); + if (m_processMsg != null) + return false; + + // After reverseAccrual + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REVERSEACCRUAL); + if (m_processMsg != null) + return false; + + return false; + } // reverseAccrualIt + + /** + * Re-activate + * @return false + */ + public boolean reActivateIt() + { + log.info(toString()); + // Before reActivate + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_REACTIVATE); + if (m_processMsg != null) + return false; + + // After reActivate + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REACTIVATE); + if (m_processMsg != null) + return false; + + return false; + } // reActivateIt + + + /************************************************************************* + * Get Summary + * @return Summary of Document + */ + public String getSummary() + { + StringBuffer sb = new StringBuffer(); + sb.append(getDocumentNo()); + // : Total Lines = 123.00 (#1) + sb.append(": ") + .append(Msg.translate(getCtx(),"ApprovalAmt")).append("=").append(getApprovalAmt()) + .append(" (#").append(getLines(false).length).append(")"); + // - Description + if (getDescription() != null && getDescription().length() > 0) + sb.append(" - ").append(getDescription()); + return sb.toString(); + } // getSummary + + /** + * Get Process Message + * @return clear text error message + */ + public String getProcessMsg() + { + return m_processMsg; + } // getProcessMsg + + /** + * Get Document Owner (Responsible) + * @return AD_User_ID + */ + public int getDoc_User_ID() + { + return getUpdatedBy(); + } // getDoc_User_ID + + /** + * Get Document Currency + * @return C_Currency_ID + */ + public int getC_Currency_ID() + { + // MPriceList pl = MPriceList.get(getCtx(), getM_PriceList_ID()); + // return pl.getC_Currency_ID(); + return 0; + } // getC_Currency_ID + + /** Reversal Flag */ + private boolean m_reversal = false; + + /** + * Set Reversal + * @param reversal reversal + */ + private void setReversal(boolean reversal) + { + m_reversal = reversal; + } // setReversal + /** + * Is Reversal + * @return reversal + */ + private boolean isReversal() + { + return m_reversal; + } // isReversal + +} // MInventory -import java.io.File; -import java.math.BigDecimal; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.Timestamp; -import java.util.ArrayList; -import java.util.Properties; -import java.util.logging.Level; - -import org.compiere.process.DocAction; -import org.compiere.process.DocumentEngine; -import org.compiere.report.MReportTree; -import org.compiere.util.CCache; -import org.compiere.util.DB; -import org.compiere.util.Env; -import org.compiere.util.Msg; - -/** - * Physical Inventory Model - * - * @author Jorg Janke - * @version $Id: MInventory.java,v 1.3 2006/07/30 00:51:05 jjanke Exp $ - * @author victor.perez@e-evolution.com, e-Evolution http://www.e-evolution.com - *
  • FR [ 1948157 ] Is necessary the reference for document reverse - *
  • FR [ 2520591 ] Support multiples calendar for Org - * @see http://sourceforge.net/tracker2/?func=detail&atid=879335&aid=2520591&group_id=176962 - * @author Armen Rizal, Goodwill Consulting - *
  • BF [ 1745154 ] Cost in Reversing Material Related Docs - * @see http://sourceforge.net/tracker/?func=detail&atid=879335&aid=1948157&group_id=176962 - */ -public class MInventory extends X_M_Inventory implements DocAction -{ - /** - * Get Inventory from Cache - * @param ctx context - * @param M_Inventory_ID id - * @return MInventory - */ - public static MInventory get (Properties ctx, int M_Inventory_ID) - { - Integer key = new Integer (M_Inventory_ID); - MInventory retValue = (MInventory) s_cache.get (key); - if (retValue != null) - return retValue; - retValue = new MInventory (ctx, M_Inventory_ID, null); - if (retValue.get_ID () != 0) - s_cache.put (key, retValue); - return retValue; - } // get - - /** Cache */ - private static CCache s_cache = new CCache("M_Inventory", 5, 5); - - - /** - * Standard Constructor - * @param ctx context - * @param M_Inventory_ID id - * @param trxName transaction - */ - public MInventory (Properties ctx, int M_Inventory_ID, String trxName) - { - super (ctx, M_Inventory_ID, trxName); - if (M_Inventory_ID == 0) - { - // setName (null); - // setM_Warehouse_ID (0); // FK - setMovementDate (new Timestamp(System.currentTimeMillis())); - setDocAction (DOCACTION_Complete); // CO - setDocStatus (DOCSTATUS_Drafted); // DR - setIsApproved (false); - setMovementDate (new Timestamp(System.currentTimeMillis())); // @#Date@ - setPosted (false); - setProcessed (false); - } - } // MInventory - - /** - * Load Constructor - * @param ctx context - * @param rs result set - * @param trxName transaction - */ - public MInventory (Properties ctx, ResultSet rs, String trxName) - { - super(ctx, rs, trxName); - } // MInventory - - /** - * Warehouse Constructor - * @param wh warehouse - */ - public MInventory (MWarehouse wh) - { - this (wh.getCtx(), 0, wh.get_TrxName()); - setClientOrg(wh); - setM_Warehouse_ID(wh.getM_Warehouse_ID()); - } // MInventory - - - /** Lines */ - private MInventoryLine[] m_lines = null; - - /** - * Get Lines - * @param requery requery - * @return array of lines - */ - public MInventoryLine[] getLines (boolean requery) - { - if (m_lines != null && !requery) { - set_TrxName(m_lines, get_TrxName()); - return m_lines; - } - // - ArrayList list = new ArrayList(); - String sql = "SELECT * FROM M_InventoryLine WHERE M_Inventory_ID=? ORDER BY Line"; - PreparedStatement pstmt = null; - ResultSet rs = null; - try - { - pstmt = DB.prepareStatement (sql, get_TrxName()); - pstmt.setInt (1, getM_Inventory_ID()); - rs = pstmt.executeQuery (); - while (rs.next ()) - list.add (new MInventoryLine (getCtx(), rs, get_TrxName())); - } - catch (Exception e) - { - log.log(Level.SEVERE, sql, e); - } - finally - { - DB.close(rs, pstmt); - rs = null; pstmt = null; - } - - m_lines = new MInventoryLine[list.size ()]; - list.toArray (m_lines); - return m_lines; - } // getLines - - /** - * Add to Description - * @param description text - */ - public void addDescription (String description) - { - String desc = getDescription(); - if (desc == null) - setDescription(description); - else - setDescription(desc + " | " + description); - } // addDescription - - /** - * Overwrite Client/Org - from Import. - * @param AD_Client_ID client - * @param AD_Org_ID org - */ - public void setClientOrg (int AD_Client_ID, int AD_Org_ID) - { - super.setClientOrg(AD_Client_ID, AD_Org_ID); - } // setClientOrg - - /** - * String Representation - * @return info - */ - public String toString () - { - StringBuffer sb = new StringBuffer ("MInventory["); - sb.append (get_ID()) - .append ("-").append (getDocumentNo()) - .append (",M_Warehouse_ID=").append(getM_Warehouse_ID()) - .append ("]"); - return sb.toString (); - } // toString - - /** - * Get Document Info - * @return document info (untranslated) - */ - public String getDocumentInfo() - { - MDocType dt = MDocType.get(getCtx(), getC_DocType_ID()); - return dt.getName() + " " + getDocumentNo(); - } // getDocumentInfo - - /** - * Create PDF - * @return File or null - */ - public File createPDF () - { - try - { - File temp = File.createTempFile(get_TableName()+get_ID()+"_", ".pdf"); - return createPDF (temp); - } - catch (Exception e) - { - log.severe("Could not create PDF - " + e.getMessage()); - } - return null; - } // getPDF - - /** - * Create PDF file - * @param file output file - * @return file if success - */ - public File createPDF (File file) - { - // ReportEngine re = ReportEngine.get (getCtx(), ReportEngine.INVOICE, getC_Invoice_ID()); - // if (re == null) - return null; - // return re.getPDF(file); - } // createPDF - - - /** - * Before Save - * @param newRecord new - * @return true - */ - protected boolean beforeSave (boolean newRecord) - { - if (getC_DocType_ID() == 0) - { - MDocType types[] = MDocType.getOfDocBaseType(getCtx(), MDocType.DOCBASETYPE_MaterialPhysicalInventory); - if (types.length > 0) // get first - setC_DocType_ID(types[0].getC_DocType_ID()); - else - { - log.saveError("Error", Msg.parseTranslation(getCtx(), "@NotFound@ @C_DocType_ID@")); - return false; - } - } - return true; - } // beforeSave - - - /** - * Set Processed. - * Propergate to Lines/Taxes - * @param processed processed - */ - public void setProcessed (boolean processed) - { - super.setProcessed (processed); - if (get_ID() == 0) - return; - String sql = "UPDATE M_InventoryLine SET Processed='" - + (processed ? "Y" : "N") - + "' WHERE M_Inventory_ID=" + getM_Inventory_ID(); - int noLine = DB.executeUpdate(sql, get_TrxName()); - m_lines = null; - log.fine("Processed=" + processed + " - Lines=" + noLine); - } // setProcessed - - - /************************************************************************** - * Process document - * @param processAction document action - * @return true if performed - */ - public boolean processIt (String processAction) - { - m_processMsg = null; - DocumentEngine engine = new DocumentEngine (this, getDocStatus()); - return engine.processIt (processAction, getDocAction()); - } // processIt - - /** Process Message */ - private String m_processMsg = null; - /** Just Prepared Flag */ - private boolean m_justPrepared = false; - - /** - * Unlock Document. - * @return true if success - */ - public boolean unlockIt() - { - log.info(toString()); - setProcessing(false); - return true; - } // unlockIt - - /** - * Invalidate Document - * @return true if success - */ - public boolean invalidateIt() - { - log.info(toString()); - setDocAction(DOCACTION_Prepare); - return true; - } // invalidateIt - - /** - * Prepare Document - * @return new status (In Progress or Invalid) - */ - public String prepareIt() - { - log.info(toString()); - m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_BEFORE_PREPARE); - if (m_processMsg != null) - return DocAction.STATUS_Invalid; - - // Std Period open? - if (!MPeriod.isOpen(getCtx(), getMovementDate(), MDocType.DOCBASETYPE_MaterialPhysicalInventory, getAD_Org_ID())) - { - m_processMsg = "@PeriodClosed@"; - return DocAction.STATUS_Invalid; - } - MInventoryLine[] lines = getLines(false); - if (lines.length == 0) - { - m_processMsg = "@NoLines@"; - return DocAction.STATUS_Invalid; - } - - // TODO: Add up Amounts - // setApprovalAmt(); - - m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_AFTER_PREPARE); - if (m_processMsg != null) - return DocAction.STATUS_Invalid; - - m_justPrepared = true; - if (!DOCACTION_Complete.equals(getDocAction())) - setDocAction(DOCACTION_Complete); - return DocAction.STATUS_InProgress; - } // prepareIt - - /** - * Approve Document - * @return true if success - */ - public boolean approveIt() - { - log.info(toString()); - setIsApproved(true); - return true; - } // approveIt - - /** - * Reject Approval - * @return true if success - */ - public boolean rejectIt() - { - log.info(toString()); - setIsApproved(false); - return true; - } // rejectIt - - /** - * Complete Document - * @return new status (Complete, In Progress, Invalid, Waiting ..) - */ - public String completeIt() - { - // Re-Check - if (!m_justPrepared) - { - String status = prepareIt(); - if (!DocAction.STATUS_InProgress.equals(status)) - return status; - } - - m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_BEFORE_COMPLETE); - if (m_processMsg != null) - return DocAction.STATUS_Invalid; - - // Implicit Approval - if (!isApproved()) - approveIt(); - log.info(toString()); - - MInventoryLine[] lines = getLines(false); - for (MInventoryLine line : lines) - { - if (!line.isActive()) - continue; - - MProduct product = line.getProduct(); - - //Get Quantity to Inventory Inernal Use - BigDecimal qtyDiff = line.getQtyInternalUse().negate(); - //If Quantity to Inventory Internal Use = Zero Then is Physical Inventory Else is Inventory Internal Use - if (qtyDiff.signum() == 0) - qtyDiff = line.getQtyCount().subtract(line.getQtyBook()); - - //Ignore the Material Policy when is Reverse Correction - if(!isReversal()) - checkMaterialPolicy(line, qtyDiff); - - // Stock Movement - Counterpart MOrder.reserveStock - if (product != null - && product.isStocked() ) - { - log.fine("Material Transaction"); - MTransaction mtrx = null; - - //If AttributeSetInstance = Zero then create new AttributeSetInstance use Inventory Line MA else use current AttributeSetInstance - if (line.getM_AttributeSetInstance_ID() == 0 || qtyDiff.compareTo(Env.ZERO) == 0) - { - MInventoryLineMA mas[] = MInventoryLineMA.get(getCtx(), - line.getM_InventoryLine_ID(), get_TrxName()); - - for (int j = 0; j < mas.length; j++) - { - MInventoryLineMA ma = mas[j]; - BigDecimal QtyMA = ma.getMovementQty(); - BigDecimal QtyNew = QtyMA.add(qtyDiff); - log.fine("Diff=" + qtyDiff - + " - Instance OnHand=" + QtyMA + "->" + QtyNew); - - if (!MStorage.add(getCtx(), getM_Warehouse_ID(), - line.getM_Locator_ID(), - line.getM_Product_ID(), - ma.getM_AttributeSetInstance_ID(), 0, - QtyMA.negate(), Env.ZERO, Env.ZERO, get_TrxName())) - { - m_processMsg = "Cannot correct Inventory (MA)"; - return DocAction.STATUS_Invalid; - } - - // Only Update Date Last Inventory if is a Physical Inventory - if(line.getQtyInternalUse().compareTo(Env.ZERO) == 0) - { - MStorage storage = MStorage.get(getCtx(), line.getM_Locator_ID(), - line.getM_Product_ID(), ma.getM_AttributeSetInstance_ID(), get_TrxName()); - storage.setDateLastInventory(getMovementDate()); - if (!storage.save(get_TrxName())) - { - m_processMsg = "Storage not updated(2)"; - return DocAction.STATUS_Invalid; - } - } - - String m_MovementType =null; - if(QtyMA.negate().compareTo(Env.ZERO) > 0 ) - m_MovementType = MTransaction.MOVEMENTTYPE_InventoryIn; - else - m_MovementType = MTransaction.MOVEMENTTYPE_InventoryOut; - // Transaction - mtrx = new MTransaction (getCtx(), line.getAD_Org_ID(), m_MovementType, - line.getM_Locator_ID(), line.getM_Product_ID(), ma.getM_AttributeSetInstance_ID(), - QtyMA.negate(), getMovementDate(), get_TrxName()); - - mtrx.setM_InventoryLine_ID(line.getM_InventoryLine_ID()); - if (!mtrx.save()) - { - m_processMsg = "Transaction not inserted(2)"; - return DocAction.STATUS_Invalid; - } - if(QtyMA.signum() != 0) - { - String err = createCostDetail(line, ma.getM_AttributeSetInstance_ID() , QtyMA.negate()); - if(err != null && err.length() > 0) return err; - } - - qtyDiff = QtyNew; - - } - } - - //sLine.getM_AttributeSetInstance_ID() != 0 - // Fallback - if (mtrx == null) - { - //Fallback: Update Storage - see also VMatch.createMatchRecord - if (!MStorage.add(getCtx(), getM_Warehouse_ID(), - line.getM_Locator_ID(), - line.getM_Product_ID(), - line.getM_AttributeSetInstance_ID(), 0, - qtyDiff, Env.ZERO, Env.ZERO, get_TrxName())) - { - m_processMsg = "Cannot correct Inventory (MA)"; - return DocAction.STATUS_Invalid; - } - - // Only Update Date Last Inventory if is a Physical Inventory - if(line.getQtyInternalUse().compareTo(Env.ZERO) == 0) - { - MStorage storage = MStorage.get(getCtx(), line.getM_Locator_ID(), - line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), get_TrxName()); - - storage.setDateLastInventory(getMovementDate()); - if (!storage.save(get_TrxName())) - { - m_processMsg = "Storage not updated(2)"; - return DocAction.STATUS_Invalid; - } - } - - String m_MovementType =null; - if(qtyDiff.compareTo(Env.ZERO) > 0 ) - m_MovementType = MTransaction.MOVEMENTTYPE_InventoryIn; - else - m_MovementType = MTransaction.MOVEMENTTYPE_InventoryOut; - // Transaction - mtrx = new MTransaction (getCtx(), line.getAD_Org_ID(), m_MovementType, - line.getM_Locator_ID(), line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), - qtyDiff, getMovementDate(), get_TrxName()); - mtrx.setM_InventoryLine_ID(line.getM_InventoryLine_ID()); - if (!mtrx.save()) - { - m_processMsg = "Transaction not inserted(2)"; - return DocAction.STATUS_Invalid; - } - - if(qtyDiff.signum() != 0) - { - String err = createCostDetail(line, line.getM_AttributeSetInstance_ID(), qtyDiff); - if(err != null && err.length() > 0) return err; - } - } // Fallback - } // stock movement - - } // for all lines - - // User Validation - String valid = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_AFTER_COMPLETE); - if (valid != null) - { - m_processMsg = valid; - return DocAction.STATUS_Invalid; - } - - // Set the definite document number after completed (if needed) - setDefiniteDocumentNo(); - - // - setProcessed(true); - setDocAction(DOCACTION_Close); - return DocAction.STATUS_Completed; - } // completeIt - - /** - * Set the definite document number after completed - */ - private void setDefiniteDocumentNo() { - MDocType dt = MDocType.get(getCtx(), getC_DocType_ID()); - if (dt.isOverwriteDateOnComplete()) { - setMovementDate(new Timestamp (System.currentTimeMillis())); - } - if (dt.isOverwriteSeqOnComplete()) { - String value = DB.getDocumentNo(getC_DocType_ID(), get_TrxName(), true, this); - if (value != null) - setDocumentNo(value); - } - } - - /** - * Check Material Policy. - * (NOT USED) - * Sets line ASI - */ - private void checkMaterialPolicy(MInventoryLine line, BigDecimal qtyDiff) - { - int no = MInventoryLineMA.deleteInventoryMA(line.getM_InventoryLine_ID(), get_TrxName()); - if (no > 0) - log.config("Delete old #" + no); - - - // Check Line - boolean needSave = false; - BigDecimal qtyASI = Env.ZERO ; - // Attribute Set Instance - if (line.getM_AttributeSetInstance_ID() == 0) - { - MProduct product = MProduct.get(getCtx(), line.getM_Product_ID()); - if (qtyDiff.signum() > 0) // Incoming Trx - { - MAttributeSetInstance asi = new MAttributeSetInstance(getCtx(), 0, get_TrxName()); - asi.setClientOrg(getAD_Client_ID(), 0); - asi.setM_AttributeSet_ID(product.getM_AttributeSet_ID()); - if (!asi.save()) - { - throw new IllegalStateException("Error try create ASI Reservation"); - } - if (asi.save()) - { - line.setM_AttributeSetInstance_ID(asi.getM_AttributeSetInstance_ID()); - needSave = true; - } - } - else // Outgoing Trx - { - String MMPolicy = product.getMMPolicy(); - MStorage[] storages = MStorage.getAllWithASI(getCtx(), - line.getM_Product_ID(), line.getM_Locator_ID(), - MClient.MMPOLICY_FiFo.equals(MMPolicy), get_TrxName()); - BigDecimal qtyToDeliver = qtyDiff.negate(); - - for (MStorage storage: storages) - { - //cosume ASI Zero - if (storage.getM_AttributeSetInstance_ID() == 0) - { - qtyASI = qtyASI.add(storage.getQtyOnHand()); - qtyToDeliver = qtyToDeliver.subtract(storage.getQtyOnHand()); - continue; - } - - if (storage.getQtyOnHand().compareTo(qtyToDeliver) >= 0) - { - MInventoryLineMA ma = new MInventoryLineMA (line, - storage.getM_AttributeSetInstance_ID(), - qtyToDeliver); - if (!ma.save()) - { - throw new IllegalStateException("Error try create ASI Reservation"); - } - qtyToDeliver = Env.ZERO; - log.fine( ma + ", QtyToDeliver=" + qtyToDeliver); - //return; - } - else - { - MInventoryLineMA ma = new MInventoryLineMA (line, - storage.getM_AttributeSetInstance_ID(), - storage.getQtyOnHand()); - if (!ma.save()) - { - throw new IllegalStateException("Error try create ASI Reservation"); - } - qtyToDeliver = qtyToDeliver.subtract(storage.getQtyOnHand()); - log.fine( ma + ", QtyToDeliver=" + qtyToDeliver); - } - } - - // No AttributeSetInstance found for remainder - if (qtyToDeliver.signum() != 0 || qtyASI.signum() != 0) - { - MInventoryLineMA ma = new MInventoryLineMA (line, 0 , qtyToDeliver.add(qtyASI)); - - if (!ma.save()) - ; - log.fine("##: " + ma); - } - } // outgoing Trx - - if (needSave && !line.save()) - log.severe("NOT saved " + line); - } // for all lines - - } // checkMaterialPolicy - - /** - * Void Document. - * @return false - */ - public boolean voidIt() - { - log.info(toString()); - // Before Void - m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_VOID); - if (m_processMsg != null) - return false; - - if (DOCSTATUS_Closed.equals(getDocStatus()) - || DOCSTATUS_Reversed.equals(getDocStatus()) - || DOCSTATUS_Voided.equals(getDocStatus())) - { - m_processMsg = "Document Closed: " + getDocStatus(); - return false; - } - - // Not Processed - if (DOCSTATUS_Drafted.equals(getDocStatus()) - || DOCSTATUS_Invalid.equals(getDocStatus()) - || DOCSTATUS_InProgress.equals(getDocStatus()) - || DOCSTATUS_Approved.equals(getDocStatus()) - || DOCSTATUS_NotApproved.equals(getDocStatus()) ) - { - // Set lines to 0 - MInventoryLine[] lines = getLines(false); - for (int i = 0; i < lines.length; i++) - { - MInventoryLine line = lines[i]; - BigDecimal oldCount = line.getQtyCount(); - BigDecimal oldInternal = line.getQtyInternalUse(); - if (oldCount.compareTo(line.getQtyBook()) != 0 - || oldInternal.signum() != 0) - { - line.setQtyInternalUse(Env.ZERO); - line.setQtyCount(line.getQtyBook()); - line.addDescription("Void (" + oldCount + "/" + oldInternal + ")"); - line.save(get_TrxName()); - } - } - } - else - { - return reverseCorrectIt(); - } - - // After Void - m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_VOID); - if (m_processMsg != null) - return false; - setProcessed(true); - setDocAction(DOCACTION_None); - return true; - } // voidIt - - /** - * Close Document. - * @return true if success - */ - public boolean closeIt() - { - log.info(toString()); - // Before Close - m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_CLOSE); - if (m_processMsg != null) - return false; - // After Close - m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_CLOSE); - if (m_processMsg != null) - return false; - - setDocAction(DOCACTION_None); - return true; - } // closeIt - - /** - * Reverse Correction - * @return false - */ - public boolean reverseCorrectIt() - { - log.info(toString()); - // Before reverseCorrect - m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_REVERSECORRECT); - if (m_processMsg != null) - return false; - - MDocType dt = MDocType.get(getCtx(), getC_DocType_ID()); - if (!MPeriod.isOpen(getCtx(), getMovementDate(), dt.getDocBaseType(), getAD_Org_ID())) - { - m_processMsg = "@PeriodClosed@"; - return false; - } - - // Deep Copy - MInventory reversal = new MInventory(getCtx(), 0, get_TrxName()); - copyValues(this, reversal, getAD_Client_ID(), getAD_Org_ID()); - reversal.setDocStatus(DOCSTATUS_Drafted); - reversal.setDocAction(DOCACTION_Complete); - reversal.setIsApproved (false); - reversal.setPosted(false); - reversal.setProcessed(false); - reversal.addDescription("{->" + getDocumentNo() + ")"); - //FR1948157 - reversal.setReversal_ID(getM_Inventory_ID()); - if (!reversal.save()) - { - m_processMsg = "Could not create Inventory Reversal"; - return false; - } - reversal.setReversal(true); - - // Reverse Line Qty - MInventoryLine[] oLines = getLines(true); - for (int i = 0; i < oLines.length; i++) - { - MInventoryLine oLine = oLines[i]; - MInventoryLine rLine = new MInventoryLine(getCtx(), 0, get_TrxName()); - copyValues(oLine, rLine, oLine.getAD_Client_ID(), oLine.getAD_Org_ID()); - rLine.setM_Inventory_ID(reversal.getM_Inventory_ID()); - rLine.setParent(reversal); - //AZ Goodwill - // store original (voided/reversed) document line - rLine.setReversalLine_ID(oLine.getM_InventoryLine_ID()); - // - rLine.setQtyBook (oLine.getQtyCount()); // switch - rLine.setQtyCount (oLine.getQtyBook()); - rLine.setQtyInternalUse (oLine.getQtyInternalUse().negate()); - - if (!rLine.save()) - { - m_processMsg = "Could not create Inventory Reversal Line"; - return false; - } - - //We need to copy MA - if (rLine.getM_AttributeSetInstance_ID() == 0) - { - MInventoryLineMA mas[] = MInventoryLineMA.get(getCtx(), - oLines[i].getM_InventoryLine_ID(), get_TrxName()); - for (int j = 0; j < mas.length; j++) - { - MInventoryLineMA ma = new MInventoryLineMA (rLine, - mas[j].getM_AttributeSetInstance_ID(), - mas[j].getMovementQty().negate()); - if (!ma.save()) - ; - } - } - } - // - if (!reversal.processIt(DocAction.ACTION_Complete)) - { - m_processMsg = "Reversal ERROR: " + reversal.getProcessMsg(); - return false; - } - reversal.closeIt(); - reversal.setDocStatus(DOCSTATUS_Reversed); - reversal.setDocAction(DOCACTION_None); - reversal.save(); - m_processMsg = reversal.getDocumentNo(); - - // Update Reversed (this) - addDescription("(" + reversal.getDocumentNo() + "<-)"); - // After reverseCorrect - m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REVERSECORRECT); - if (m_processMsg != null) - return false; - setProcessed(true); - //FR1948157 - setReversal_ID(reversal.getM_Inventory_ID()); - setDocStatus(DOCSTATUS_Reversed); // may come from void - setDocAction(DOCACTION_None); - - return true; - } // reverseCorrectIt - - /** - * Reverse Accrual - * @return false - */ - public boolean reverseAccrualIt() - { - log.info(toString()); - // Before reverseAccrual - m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_REVERSEACCRUAL); - if (m_processMsg != null) - return false; - - // After reverseAccrual - m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REVERSEACCRUAL); - if (m_processMsg != null) - return false; - - return false; - } // reverseAccrualIt - - /** - * Re-activate - * @return false - */ - public boolean reActivateIt() - { - log.info(toString()); - // Before reActivate - m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_REACTIVATE); - if (m_processMsg != null) - return false; - - // After reActivate - m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REACTIVATE); - if (m_processMsg != null) - return false; - - return false; - } // reActivateIt - - - /************************************************************************* - * Get Summary - * @return Summary of Document - */ - public String getSummary() - { - StringBuffer sb = new StringBuffer(); - sb.append(getDocumentNo()); - // : Total Lines = 123.00 (#1) - sb.append(": ") - .append(Msg.translate(getCtx(),"ApprovalAmt")).append("=").append(getApprovalAmt()) - .append(" (#").append(getLines(false).length).append(")"); - // - Description - if (getDescription() != null && getDescription().length() > 0) - sb.append(" - ").append(getDescription()); - return sb.toString(); - } // getSummary - - /** - * Get Process Message - * @return clear text error message - */ - public String getProcessMsg() - { - return m_processMsg; - } // getProcessMsg - - /** - * Get Document Owner (Responsible) - * @return AD_User_ID - */ - public int getDoc_User_ID() - { - return getUpdatedBy(); - } // getDoc_User_ID - - /** - * Get Document Currency - * @return C_Currency_ID - */ - public int getC_Currency_ID() - { - // MPriceList pl = MPriceList.get(getCtx(), getM_PriceList_ID()); - // return pl.getC_Currency_ID(); - return 0; - } // getC_Currency_ID - - /** Reversal Flag */ - private boolean m_reversal = false; - - /** - * Set Reversal - * @param reversal reversal - */ - private void setReversal(boolean reversal) - { - m_reversal = reversal; - } // setReversal - /** - * Is Reversal - * @return reversal - */ - private boolean isReversal() - { - return m_reversal; - } // isReversal - - /** - * Create Cost Detail - * @param line - * @param Qty - * @return - */ - private String createCostDetail(MInventoryLine line,int M_AttributeSetInstance_ID, BigDecimal qty) - { - // Get Account Schemas to create MCostDetail - MAcctSchema[] acctschemas = MAcctSchema.getClientAcctSchema(getCtx(), getAD_Client_ID()); - for(int asn = 0; asn < acctschemas.length; asn++) - { - MAcctSchema as = acctschemas[asn]; - - boolean skip = false; - if (as.getAD_OrgOnly_ID() != 0) - { - if (as.getOnlyOrgs() == null) - as.setOnlyOrgs(MReportTree.getChildIDs(getCtx(), - 0, MAcctSchemaElement.ELEMENTTYPE_Organization, - as.getAD_OrgOnly_ID())); - - // Header Level Org - skip = as.isSkipOrg(getAD_Org_ID()); - // Line Level Org - skip = as.isSkipOrg(line.getAD_Org_ID()); - } - if (skip) - continue; - - ProductCost pc = new ProductCost (Env.getCtx(), - line.getM_Product_ID(), M_AttributeSetInstance_ID, line.get_TrxName()); - pc.setQty(qty); - BigDecimal costs = pc.getProductCosts(as, line.getAD_Org_ID(), as.getCostingMethod(), - 0,false); - if (costs == null || costs.signum() == 0) - { - return "No Costs for " + line.getProduct().getName(); - } - - // Set Total Amount and Total Quantity from Inventory - MCostDetail.createInventory(as, line.getAD_Org_ID(), - line.getM_Product_ID(), M_AttributeSetInstance_ID, - line.getM_InventoryLine_ID(), 0, // no cost element - costs.multiply(qty), qty, - line.getDescription(), line.get_TrxName()); - } - - return ""; - } -} // MInventory diff --git a/base/src/org/compiere/model/MMovement.java b/base/src/org/compiere/model/MMovement.java index 32bb132fab..10acd74d4b 100644 --- a/base/src/org/compiere/model/MMovement.java +++ b/base/src/org/compiere/model/MMovement.java @@ -658,81 +658,19 @@ public class MMovement extends X_M_Movement implements DocAction //{ // MMovementLine line = lines[i]; boolean needSave = false; - BigDecimal qtyASI = Env.ZERO ; // Attribute Set Instance if (line.getM_AttributeSetInstance_ID() == 0) { MProduct product = MProduct.get(getCtx(), line.getM_Product_ID()); String MMPolicy = product.getMMPolicy(); - MStorage[] storages = MStorage.getAllWithASI(getCtx(), - line.getM_Product_ID(), line.getM_Locator_ID(), - MClient.MMPOLICY_FiFo.equals(MMPolicy), get_TrxName()); + MStorage[] storages = MStorage.getWarehouse(getCtx(), 0, line.getM_Product_ID(), 0, + null, MClient.MMPOLICY_FiFo.equals(MMPolicy), true, line.getM_Locator_ID(), get_TrxName()); + BigDecimal qtyToDeliver = line.getMovementQty(); - - /*for (int ii = 0; ii < storages.length; ii++) - { - MStorage storage = storages[ii]; - if (ii == 0) - { - if (storage.getQtyOnHand().compareTo(qtyToDeliver) >= 0) - { - line.setM_AttributeSetInstance_ID(storage.getM_AttributeSetInstance_ID()); - needSave = true; - log.config("Direct - " + line); - qtyToDeliver = Env.ZERO; - } - else - { - log.config("Split - " + line); - MMovementLineMA ma = new MMovementLineMA (line, - storage.getM_AttributeSetInstance_ID(), - storage.getQtyOnHand()); - if (!ma.save()) - ; - qtyToDeliver = qtyToDeliver.subtract(storage.getQtyOnHand()); - log.fine("#" + ii + ": " + ma + ", QtyToDeliver=" + qtyToDeliver); - } - } - else // create addl material allocation - { - MMovementLineMA ma = new MMovementLineMA (line, - storage.getM_AttributeSetInstance_ID(), - qtyToDeliver); - if (storage.getQtyOnHand().compareTo(qtyToDeliver) >= 0) - qtyToDeliver = Env.ZERO; - else - { - ma.setMovementQty(storage.getQtyOnHand()); - qtyToDeliver = qtyToDeliver.subtract(storage.getQtyOnHand()); - } - if (!ma.save()) - ; - log.fine("#" + ii + ": " + ma + ", QtyToDeliver=" + qtyToDeliver); - } - if (qtyToDeliver.signum() == 0) - break; - } // for all storages - - // No AttributeSetInstance found for remainder - if (qtyToDeliver.signum() != 0) - { - MMovementLineMA ma = new MMovementLineMA (line, - 0, qtyToDeliver); - if (!ma.save()) - ; - log.fine("##: " + ma); - }*/ + for (MStorage storage: storages) { - //consume ASI Zero - if (storage.getM_AttributeSetInstance_ID() == 0) - { - qtyASI = qtyASI.add(storage.getQtyOnHand()); - qtyToDeliver = qtyToDeliver.subtract(storage.getQtyOnHand()); - continue; - } - if (storage.getQtyOnHand().compareTo(qtyToDeliver) >= 0) { MMovementLineMA ma = new MMovementLineMA (line, @@ -761,12 +699,12 @@ public class MMovement extends X_M_Movement implements DocAction } // No AttributeSetInstance found for remainder - if (qtyToDeliver.signum() != 0 || qtyASI.signum() != 0) + if (qtyToDeliver.signum() != 0) { - MMovementLineMA ma = new MMovementLineMA (line, 0 , qtyToDeliver.add(qtyASI)); + MMovementLineMA ma = new MMovementLineMA (line, 0 , qtyToDeliver); if (!ma.save()) - ; + throw new IllegalStateException("Error try create ASI Reservation"); log.fine("##: " + ma); } } // attributeSetInstance diff --git a/base/src/org/compiere/model/MStorage.java b/base/src/org/compiere/model/MStorage.java index 23589a8c45..13e12bd254 100644 --- a/base/src/org/compiere/model/MStorage.java +++ b/base/src/org/compiere/model/MStorage.java @@ -78,7 +78,6 @@ public class MStorage extends X_M_Storage DB.close(rs, pstmt); rs = null; pstmt = null; } - pstmt = null; if (retValue == null) s_log.fine("Not Found - M_Locator_ID=" + M_Locator_ID + ", M_Product_ID=" + M_Product_ID + ", M_AttributeSetInstance_ID=" + M_AttributeSetInstance_ID); @@ -89,7 +88,7 @@ public class MStorage extends X_M_Storage } // get /** - * Get all Storages for Product with ASI and QtyOnHand > 0 + * Get all Storages for Product with ASI and QtyOnHand <> 0 * @param ctx context * @param M_Product_ID product * @param M_Locator_ID locator @@ -103,13 +102,9 @@ public class MStorage extends X_M_Storage ArrayList list = new ArrayList(); String sql = "SELECT * FROM M_Storage " + "WHERE M_Product_ID=? AND M_Locator_ID=?" - // Remove for management rightly FIFO/LIFO now you can consume a layer with ASI ID = zero and Qty onhand in negative - // + " AND M_AttributeSetInstance_ID > 0" - // + " AND QtyOnHand > 0 " - + " AND QtyOnHand <> 0 " + + " AND M_AttributeSetInstance_ID > 0 " + + " AND QtyOnHand <> 0 " + "ORDER BY M_AttributeSetInstance_ID"; - if (!FiFo) - sql += " DESC"; PreparedStatement pstmt = null; ResultSet rs = null; try @@ -130,14 +125,13 @@ public class MStorage extends X_M_Storage DB.close(rs, pstmt); rs = null; pstmt = null; } - pstmt = null; MStorage[] retValue = new MStorage[list.size()]; list.toArray(retValue); return retValue; } // getAllWithASI /** - * Get all Storages for Product + * Get all Storages for Product where QtyOnHand <> 0 * @param ctx context * @param M_Product_ID product * @param M_Locator_ID locator @@ -172,7 +166,6 @@ public class MStorage extends X_M_Storage DB.close(rs, pstmt); rs = null; pstmt = null; } - pstmt = null; MStorage[] retValue = new MStorage[list.size()]; list.toArray(retValue); return retValue; @@ -227,23 +220,41 @@ public class MStorage extends X_M_Storage * @param FiFo first in-first-out * @param trxName transaction * @return existing - ordered by location priority (desc) and/or guarantee date + * + * @deprecated */ public static MStorage[] getWarehouse (Properties ctx, int M_Warehouse_ID, int M_Product_ID, int M_AttributeSetInstance_ID, int M_AttributeSet_ID, boolean allAttributeInstances, Timestamp minGuaranteeDate, boolean FiFo, String trxName) { - if (M_Warehouse_ID == 0 || M_Product_ID == 0) + return getWarehouse(ctx, M_Warehouse_ID, M_Product_ID, M_AttributeSetInstance_ID, + minGuaranteeDate, FiFo, false, 0, trxName); + } + + /** + * Get Storage Info for Warehouse or locator + * @param ctx context + * @param M_Warehouse_ID ignore if M_Locator_ID > 0 + * @param M_Product_ID product + * @param M_AttributeSetInstance_ID instance id, 0 to retrieve all instance + * @param minGuaranteeDate optional minimum guarantee date if all attribute instances + * @param FiFo first in-first-out + * @param positiveOnly if true, only return storage records with qtyOnHand > 0 + * @param M_Locator_ID optional locator id + * @param trxName transaction + * @return existing - ordered by location priority (desc) and/or guarantee date + */ + public static MStorage[] getWarehouse (Properties ctx, int M_Warehouse_ID, + int M_Product_ID, int M_AttributeSetInstance_ID, Timestamp minGuaranteeDate, + boolean FiFo, boolean positiveOnly, int M_Locator_ID, String trxName) + { + if ((M_Warehouse_ID == 0 && M_Locator_ID == 0) || M_Product_ID == 0) return new MStorage[0]; - if (M_AttributeSet_ID == 0) - allAttributeInstances = true; - else - { - MAttributeSet mas = MAttributeSet.get(ctx, M_AttributeSet_ID); - if (!mas.isInstanceAttribute()) - allAttributeInstances = true; - } + boolean allAttributeInstances = false; + if (M_AttributeSetInstance_ID == 0) + allAttributeInstances = true; ArrayList list = new ArrayList(); // Specific Attribute Set Instance @@ -251,11 +262,18 @@ public class MStorage extends X_M_Storage + "s.AD_Client_ID,s.AD_Org_ID,s.IsActive,s.Created,s.CreatedBy,s.Updated,s.UpdatedBy," + "s.QtyOnHand,s.QtyReserved,s.QtyOrdered,s.DateLastInventory " + "FROM M_Storage s" - + " INNER JOIN M_Locator l ON (l.M_Locator_ID=s.M_Locator_ID) " - + "WHERE l.M_Warehouse_ID=?" - + " AND s.M_Product_ID=?" - + " AND COALESCE(s.M_AttributeSetInstance_ID,0)=? " - + "ORDER BY l.PriorityNo DESC, M_AttributeSetInstance_ID"; + + " INNER JOIN M_Locator l ON (l.M_Locator_ID=s.M_Locator_ID) "; + if (M_Locator_ID > 0) + sql += "WHERE l.M_Locator_ID = ?"; + else + sql += "WHERE l.M_Warehouse_ID=?"; + sql += " AND s.M_Product_ID=?" + + " AND COALESCE(s.M_AttributeSetInstance_ID,0)=? "; + if (positiveOnly) + { + sql += " AND s.QtyOnHand > 0 "; + } + sql += "ORDER BY l.PriorityNo DESC, M_AttributeSetInstance_ID"; if (!FiFo) sql += " DESC"; // All Attribute Set Instances @@ -266,19 +284,31 @@ public class MStorage extends X_M_Storage + "s.QtyOnHand,s.QtyReserved,s.QtyOrdered,s.DateLastInventory " + "FROM M_Storage s" + " INNER JOIN M_Locator l ON (l.M_Locator_ID=s.M_Locator_ID)" - + " LEFT OUTER JOIN M_AttributeSetInstance asi ON (s.M_AttributeSetInstance_ID=asi.M_AttributeSetInstance_ID) " - + "WHERE l.M_Warehouse_ID=?" - + " AND s.M_Product_ID=? "; + + " LEFT OUTER JOIN M_AttributeSetInstance asi ON (s.M_AttributeSetInstance_ID=asi.M_AttributeSetInstance_ID) "; + if (M_Locator_ID > 0) + sql += "WHERE l.M_Locator_ID = ?"; + else + sql += "WHERE l.M_Warehouse_ID=?"; + sql += " AND s.M_Product_ID=? "; if (minGuaranteeDate != null) { - sql += "AND (asi.GuaranteeDate IS NULL OR asi.GuaranteeDate>?) " - + "ORDER BY asi.GuaranteeDate, M_AttributeSetInstance_ID"; + sql += "AND (asi.GuaranteeDate IS NULL OR asi.GuaranteeDate>?) "; + if (positiveOnly) + { + sql += " AND s.QtyOnHand > 0 "; + } + sql += "ORDER BY l.PriorityNo DESC, " + + "asi.GuaranteeDate, M_AttributeSetInstance_ID"; if (!FiFo) sql += " DESC"; - sql += ", l.PriorityNo DESC, s.QtyOnHand DESC"; + sql += ", s.QtyOnHand DESC"; } else { + if (positiveOnly) + { + sql += " AND s.QtyOnHand > 0 "; + } sql += "ORDER BY l.PriorityNo DESC, l.M_Locator_ID, s.M_AttributeSetInstance_ID"; if (!FiFo) sql += " DESC"; @@ -290,12 +320,16 @@ public class MStorage extends X_M_Storage try { pstmt = DB.prepareStatement(sql, trxName); - pstmt.setInt(1, M_Warehouse_ID); + pstmt.setInt(1, M_Locator_ID > 0 ? M_Locator_ID : M_Warehouse_ID); pstmt.setInt(2, M_Product_ID); if (!allAttributeInstances) + { pstmt.setInt(3, M_AttributeSetInstance_ID); + } else if (minGuaranteeDate != null) + { pstmt.setTimestamp(3, minGuaranteeDate); + } rs = pstmt.executeQuery(); while (rs.next()) list.add (new MStorage (ctx, rs, trxName)); diff --git a/base/src/org/compiere/process/InOutGenerate.java b/base/src/org/compiere/process/InOutGenerate.java index 8d1b95962b..0e932712c2 100644 --- a/base/src/org/compiere/process/InOutGenerate.java +++ b/base/src/org/compiere/process/InOutGenerate.java @@ -1,696 +1,678 @@ -/****************************************************************************** - * Product: Adempiere ERP & CRM Smart Business Solution * - * Copyright (C) 1999-2006 ComPiere, Inc. All Rights Reserved. * - * This program is free software; you can redistribute it and/or modify it * - * under the terms version 2 of the GNU General Public License as published * - * by the Free Software Foundation. This program is distributed in the hope * - * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied * - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * - * See the GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along * - * with this program; if not, write to the Free Software Foundation, Inc., * - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. * - * For the text or an alternative of this public license, you may reach us * - * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA * - * or via info@compiere.org or http://www.compiere.org/license.html * - *****************************************************************************/ -package org.compiere.process; +/****************************************************************************** + * Product: Adempiere ERP & CRM Smart Business Solution * + * Copyright (C) 1999-2006 ComPiere, Inc. All Rights Reserved. * + * This program is free software; you can redistribute it and/or modify it * + * under the terms version 2 of the GNU General Public License as published * + * by the Free Software Foundation. This program is distributed in the hope * + * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied * + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * + * See the GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License along * + * with this program; if not, write to the Free Software Foundation, Inc., * + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. * + * For the text or an alternative of this public license, you may reach us * + * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA * + * or via info@compiere.org or http://www.compiere.org/license.html * + *****************************************************************************/ +package org.compiere.process; + +import java.math.*; +import java.sql.*; +import java.util.*; +import java.util.logging.*; + +import org.compiere.model.*; +import org.compiere.util.*; + +/** + * Generate Shipments. + * Manual or Automatic + * + * @author Jorg Janke + * @version $Id: InOutGenerate.java,v 1.2 2006/07/30 00:51:01 jjanke Exp $ + */ +public class InOutGenerate extends SvrProcess +{ + /** Manual Selection */ + private boolean p_Selection = false; + /** Warehouse */ + private int p_M_Warehouse_ID = 0; + /** BPartner */ + private int p_C_BPartner_ID = 0; + /** Promise Date */ + private Timestamp p_DatePromised = null; + /** Include Orders w. unconfirmed Shipments */ + private boolean p_IsUnconfirmedInOut = false; + /** DocAction */ + private String p_docAction = DocAction.ACTION_Complete; + /** Consolidate */ + private boolean p_ConsolidateDocument = true; + /** Shipment Date */ + private Timestamp p_DateShipped = null; + + /** The current Shipment */ + private MInOut m_shipment = null; + /** Numner of Shipments */ + private int m_created = 0; + /** Line Number */ + private int m_line = 0; + /** Movement Date */ + private Timestamp m_movementDate = null; + /** Last BP Location */ + private int m_lastC_BPartner_Location_ID = -1; + + /** The Query sql */ + private String m_sql = null; + + + /** Storages temp space */ + private HashMap m_map = new HashMap(); + /** Last Parameter */ + private SParameter m_lastPP = null; + /** Last Storage */ + private MStorage[] m_lastStorages = null; + + + /** + * Prepare - e.g., get Parameters. + */ + protected void prepare() + { + ProcessInfoParameter[] para = getParameter(); + for (int i = 0; i < para.length; i++) + { + String name = para[i].getParameterName(); + if (para[i].getParameter() == null) + ; + else if (name.equals("M_Warehouse_ID")) + p_M_Warehouse_ID = para[i].getParameterAsInt(); + else if (name.equals("C_BPartner_ID")) + p_C_BPartner_ID = para[i].getParameterAsInt(); + else if (name.equals("DatePromised")) + p_DatePromised = (Timestamp)para[i].getParameter(); + else if (name.equals("Selection")) + p_Selection = "Y".equals(para[i].getParameter()); + else if (name.equals("IsUnconfirmedInOut")) + p_IsUnconfirmedInOut = "Y".equals(para[i].getParameter()); + else if (name.equals("ConsolidateDocument")) + p_ConsolidateDocument = "Y".equals(para[i].getParameter()); + else if (name.equals("DocAction")) + p_docAction = (String)para[i].getParameter(); + else if (name.equals("MovementDate")) + p_DateShipped = (Timestamp)para[i].getParameter(); + else + log.log(Level.SEVERE, "Unknown Parameter: " + name); + + // juddm - added ability to specify a shipment date from Generate Shipments + if (p_DateShipped == null) { + m_movementDate = Env.getContextAsDate(getCtx(), "#Date"); + if (m_movementDate == null) + m_movementDate = new Timestamp(System.currentTimeMillis()); + } else + m_movementDate = p_DateShipped; + // DocAction check + if (!DocAction.ACTION_Complete.equals(p_docAction)) + p_docAction = DocAction.ACTION_Prepare; + } + } // prepare + + /** + * Generate Shipments + * @return info + * @throws Exception + */ + protected String doIt () throws Exception + { + log.info("Selection=" + p_Selection + + ", M_Warehouse_ID=" + p_M_Warehouse_ID + + ", C_BPartner_ID=" + p_C_BPartner_ID + + ", Consolidate=" + p_ConsolidateDocument + + ", IsUnconfirmed=" + p_IsUnconfirmedInOut + + ", Movement=" + m_movementDate); + + if (p_M_Warehouse_ID == 0) + throw new AdempiereUserError("@NotFound@ @M_Warehouse_ID@"); + + if (p_Selection) // VInOutGen + { + m_sql = "SELECT C_Order.* FROM C_Order, T_Selection " + + "WHERE C_Order.DocStatus='CO' AND C_Order.IsSOTrx='Y' AND C_Order.AD_Client_ID=? " + + "AND C_Order.C_Order_ID = T_Selection.T_Selection_ID " + + "AND T_Selection.AD_PInstance_ID=? "; + } + else + { + m_sql = "SELECT * FROM C_Order o " + + "WHERE DocStatus='CO' AND IsSOTrx='Y'" + // No Offer,POS + + " AND o.C_DocType_ID IN (SELECT C_DocType_ID FROM C_DocType " + + "WHERE DocBaseType='SOO' AND DocSubTypeSO NOT IN ('ON','OB','WR'))" + + " AND o.IsDropShip='N'" + // No Manual + + " AND o.DeliveryRule<>'M'" + // Open Order Lines with Warehouse + + " AND EXISTS (SELECT * FROM C_OrderLine ol " + + "WHERE ol.M_Warehouse_ID=?"; // #1 + if (p_DatePromised != null) + m_sql += " AND TRUNC(ol.DatePromised)<=?"; // #2 + m_sql += " AND o.C_Order_ID=ol.C_Order_ID AND ol.QtyOrdered<>ol.QtyDelivered)"; + // + if (p_C_BPartner_ID != 0) + m_sql += " AND o.C_BPartner_ID=?"; // #3 + } + m_sql += " ORDER BY M_Warehouse_ID, PriorityRule, M_Shipper_ID, C_BPartner_ID, C_BPartner_Location_ID, C_Order_ID"; + // m_sql += " FOR UPDATE"; + + PreparedStatement pstmt = null; + try + { + pstmt = DB.prepareStatement (m_sql, get_TrxName()); + int index = 1; + if (p_Selection) + { + pstmt.setInt(index++, Env.getAD_Client_ID(getCtx())); + pstmt.setInt(index++, getAD_PInstance_ID()); + } + else + { + pstmt.setInt(index++, p_M_Warehouse_ID); + if (p_DatePromised != null) + pstmt.setTimestamp(index++, p_DatePromised); + if (p_C_BPartner_ID != 0) + pstmt.setInt(index++, p_C_BPartner_ID); + } + } + catch (Exception e) + { + log.log(Level.SEVERE, m_sql, e); + } + return generate(pstmt); + } // doIt + + /** + * Generate Shipments + * @param pstmt Order Query + * @return info + */ + private String generate (PreparedStatement pstmt) + { + try + { + ResultSet rs = pstmt.executeQuery (); + while (rs.next ()) // Order + { + MOrder order = new MOrder (getCtx(), rs, get_TrxName()); + // New Header different Shipper, Shipment Location + if (!p_ConsolidateDocument + || (m_shipment != null + && (m_shipment.getC_BPartner_Location_ID() != order.getC_BPartner_Location_ID() + || m_shipment.getM_Shipper_ID() != order.getM_Shipper_ID() ))) + completeShipment(); + log.fine("check: " + order + " - DeliveryRule=" + order.getDeliveryRule()); + // + Timestamp minGuaranteeDate = m_movementDate; + boolean completeOrder = MOrder.DELIVERYRULE_CompleteOrder.equals(order.getDeliveryRule()); + // OrderLine WHERE + String where = " AND M_Warehouse_ID=" + p_M_Warehouse_ID; + if (p_DatePromised != null) + where += " AND (TRUNC(DatePromised)<=" + DB.TO_DATE(p_DatePromised, true) + + " OR DatePromised IS NULL)"; + // Exclude Auto Delivery if not Force + if (!MOrder.DELIVERYRULE_Force.equals(order.getDeliveryRule())) + where += " AND (C_OrderLine.M_Product_ID IS NULL" + + " OR EXISTS (SELECT * FROM M_Product p " + + "WHERE C_OrderLine.M_Product_ID=p.M_Product_ID" + + " AND IsExcludeAutoDelivery='N'))"; + // Exclude Unconfirmed + if (!p_IsUnconfirmedInOut) + where += " AND NOT EXISTS (SELECT * FROM M_InOutLine iol" + + " INNER JOIN M_InOut io ON (iol.M_InOut_ID=io.M_InOut_ID) " + + "WHERE iol.C_OrderLine_ID=C_OrderLine.C_OrderLine_ID AND io.DocStatus IN ('IP','WC'))"; + // Deadlock Prevention - Order by M_Product_ID + MOrderLine[] lines = order.getLines (where, "ORDER BY C_BPartner_Location_ID, M_Product_ID"); + for (int i = 0; i < lines.length; i++) + { + MOrderLine line = lines[i]; + if (line.getM_Warehouse_ID() != p_M_Warehouse_ID) + continue; + log.fine("check: " + line); + BigDecimal onHand = Env.ZERO; + BigDecimal toDeliver = line.getQtyOrdered() + .subtract(line.getQtyDelivered()); + MProduct product = line.getProduct(); + // Nothing to Deliver + if (product != null && toDeliver.signum() == 0) + continue; + + // or it's a charge - Bug#: 1603966 + if (line.getC_Charge_ID()!=0 && toDeliver.signum() == 0) + continue; + + // Check / adjust for confirmations + BigDecimal unconfirmedShippedQty = Env.ZERO; + if (p_IsUnconfirmedInOut && product != null && toDeliver.signum() != 0) + { + String where2 = "EXISTS (SELECT * FROM M_InOut io WHERE io.M_InOut_ID=M_InOutLine.M_InOut_ID AND io.DocStatus IN ('IP','WC'))"; + MInOutLine[] iols = MInOutLine.getOfOrderLine(getCtx(), + line.getC_OrderLine_ID(), where2, null); + for (int j = 0; j < iols.length; j++) + unconfirmedShippedQty = unconfirmedShippedQty.add(iols[j].getMovementQty()); + String logInfo = "Unconfirmed Qty=" + unconfirmedShippedQty + + " - ToDeliver=" + toDeliver + "->"; + toDeliver = toDeliver.subtract(unconfirmedShippedQty); + logInfo += toDeliver; + if (toDeliver.signum() < 0) + { + toDeliver = Env.ZERO; + logInfo += " (set to 0)"; + } + // Adjust On Hand + onHand = onHand.subtract(unconfirmedShippedQty); + log.fine(logInfo); + } + + // Comments & lines w/o product & services + if ((product == null || !product.isStocked()) + && (line.getQtyOrdered().signum() == 0 // comments + || toDeliver.signum() != 0)) // lines w/o product + { + if (!MOrder.DELIVERYRULE_CompleteOrder.equals(order.getDeliveryRule())) // printed later + createLine (order, line, toDeliver, null, false); + continue; + } + + // Stored Product + String MMPolicy = product.getMMPolicy(); + MStorage[] storages = getStorages(line.getM_Warehouse_ID(), + line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), + minGuaranteeDate, MClient.MMPOLICY_FiFo.equals(MMPolicy)); + + for (int j = 0; j < storages.length; j++) + { + MStorage storage = storages[j]; + onHand = onHand.add(storage.getQtyOnHand()); + } + boolean fullLine = onHand.compareTo(toDeliver) >= 0 + || toDeliver.signum() < 0; + + // Complete Order + if (completeOrder && !fullLine) + { + log.fine("Failed CompleteOrder - OnHand=" + onHand + + " (Unconfirmed=" + unconfirmedShippedQty + + "), ToDeliver=" + toDeliver + " - " + line); + completeOrder = false; + break; + } + // Complete Line + else if (fullLine && MOrder.DELIVERYRULE_CompleteLine.equals(order.getDeliveryRule())) + { + log.fine("CompleteLine - OnHand=" + onHand + + " (Unconfirmed=" + unconfirmedShippedQty + + ", ToDeliver=" + toDeliver + " - " + line); + // + createLine (order, line, toDeliver, storages, false); + } + // Availability + else if (MOrder.DELIVERYRULE_Availability.equals(order.getDeliveryRule()) + && (onHand.signum() > 0 + || toDeliver.signum() < 0)) + { + BigDecimal deliver = toDeliver; + if (deliver.compareTo(onHand) > 0) + deliver = onHand; + log.fine("Available - OnHand=" + onHand + + " (Unconfirmed=" + unconfirmedShippedQty + + "), ToDeliver=" + toDeliver + + ", Delivering=" + deliver + " - " + line); + // + createLine (order, line, deliver, storages, false); + } + // Force + else if (MOrder.DELIVERYRULE_Force.equals(order.getDeliveryRule())) + { + BigDecimal deliver = toDeliver; + log.fine("Force - OnHand=" + onHand + + " (Unconfirmed=" + unconfirmedShippedQty + + "), ToDeliver=" + toDeliver + + ", Delivering=" + deliver + " - " + line); + // + createLine (order, line, deliver, storages, true); + } + // Manual + else if (MOrder.DELIVERYRULE_Manual.equals(order.getDeliveryRule())) + log.fine("Manual - OnHand=" + onHand + + " (Unconfirmed=" + unconfirmedShippedQty + + ") - " + line); + else + log.fine("Failed: " + order.getDeliveryRule() + " - OnHand=" + onHand + + " (Unconfirmed=" + unconfirmedShippedQty + + "), ToDeliver=" + toDeliver + " - " + line); + } // for all order lines + + // Complete Order successful + if (completeOrder && MOrder.DELIVERYRULE_CompleteOrder.equals(order.getDeliveryRule())) + { + for (int i = 0; i < lines.length; i++) + { + MOrderLine line = lines[i]; + if (line.getM_Warehouse_ID() != p_M_Warehouse_ID) + continue; + MProduct product = line.getProduct(); + BigDecimal toDeliver = line.getQtyOrdered().subtract(line.getQtyDelivered()); + // + MStorage[] storages = null; + if (product != null && product.isStocked()) + { + String MMPolicy = product.getMMPolicy(); + storages = getStorages(line.getM_Warehouse_ID(), + line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), + minGuaranteeDate, MClient.MMPOLICY_FiFo.equals(MMPolicy)); + } + // + createLine (order, line, toDeliver, storages, false); + } + } + m_line += 1000; + } // while order + rs.close (); + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + log.log(Level.SEVERE, m_sql, e); + } + try + { + if (pstmt != null) + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + pstmt = null; + } + completeShipment(); + return "@Created@ = " + m_created; + } // generate + + + + /************************************************************************** + * Create Line + * @param order order + * @param orderLine line + * @param qty qty + * @param storages storage info + * @param force force delivery + */ + private void createLine (MOrder order, MOrderLine orderLine, BigDecimal qty, + MStorage[] storages, boolean force) + { + // Complete last Shipment - can have multiple shipments + if (m_lastC_BPartner_Location_ID != orderLine.getC_BPartner_Location_ID() ) + completeShipment(); + m_lastC_BPartner_Location_ID = orderLine.getC_BPartner_Location_ID(); + // Create New Shipment + if (m_shipment == null) + { + m_shipment = new MInOut (order, 0, m_movementDate); + m_shipment.setM_Warehouse_ID(orderLine.getM_Warehouse_ID()); // sets Org too + if (order.getC_BPartner_ID() != orderLine.getC_BPartner_ID()) + m_shipment.setC_BPartner_ID(orderLine.getC_BPartner_ID()); + if (order.getC_BPartner_Location_ID() != orderLine.getC_BPartner_Location_ID()) + m_shipment.setC_BPartner_Location_ID(orderLine.getC_BPartner_Location_ID()); + if (!m_shipment.save()) + throw new IllegalStateException("Could not create Shipment"); + } + // Non Inventory Lines + if (storages == null) + { + MInOutLine line = new MInOutLine (m_shipment); + line.setOrderLine(orderLine, 0, Env.ZERO); + line.setQty(qty); // Correct UOM + if (orderLine.getQtyEntered().compareTo(orderLine.getQtyOrdered()) != 0) + line.setQtyEntered(qty + .multiply(orderLine.getQtyEntered()) + .divide(orderLine.getQtyOrdered(), 12, BigDecimal.ROUND_HALF_UP)); + line.setLine(m_line + orderLine.getLine()); + if (!line.save()) + throw new IllegalStateException("Could not create Shipment Line"); + log.fine(line.toString()); + return; + } + + // Inventory Lines + ArrayList list = new ArrayList(); + BigDecimal toDeliver = qty; + for (int i = 0; i < storages.length; i++) + { + MStorage storage = storages[i]; + + BigDecimal deliver = toDeliver; + // Not enough On Hand + if (deliver.compareTo(storage.getQtyOnHand()) > 0 + && storage.getQtyOnHand().signum() >= 0) // positive storage + { + if (!force // Adjust to OnHand Qty + || (force && i+1 != storages.length)) // if force not on last location + deliver = storage.getQtyOnHand(); + } + if (deliver.signum() == 0) // zero deliver + continue; + int M_Locator_ID = storage.getM_Locator_ID(); + // + MInOutLine line = null; + if (orderLine.getM_AttributeSetInstance_ID() == 0) // find line with Locator + { + for (int ll = 0; ll < list.size(); ll++) + { + MInOutLine test = (MInOutLine)list.get(ll); + if (test.getM_Locator_ID() == M_Locator_ID && test.getM_AttributeSetInstance_ID() == 0) + { + line = test; + break; + } + } + } + if (line == null) // new line + { + line = new MInOutLine (m_shipment); + line.setOrderLine(orderLine, M_Locator_ID, order.isSOTrx() ? deliver : Env.ZERO); + line.setQty(deliver); + list.add(line); + } + else // existing line + line.setQty(line.getMovementQty().add(deliver)); + if (orderLine.getQtyEntered().compareTo(orderLine.getQtyOrdered()) != 0) + line.setQtyEntered(line.getMovementQty().multiply(orderLine.getQtyEntered()) + .divide(orderLine.getQtyOrdered(), 12, BigDecimal.ROUND_HALF_UP)); + line.setLine(m_line + orderLine.getLine()); + + if (!line.save()) + throw new IllegalStateException("Could not create Shipment Line"); + log.fine("ToDeliver=" + qty + "/" + deliver + " - " + line); + toDeliver = toDeliver.subtract(deliver); + // Temp adjustment, actual update happen in MInOut.completeIt + storage.setQtyOnHand(storage.getQtyOnHand().subtract(deliver)); + // + if (toDeliver.signum() == 0) + break; + } + if (toDeliver.signum() != 0 ) + { + if (!force) + { + throw new IllegalStateException("Not All Delivered - Remainder=" + toDeliver); + } + else + { + MInOutLine line = new MInOutLine (m_shipment); + line.setOrderLine(orderLine, 0, order.isSOTrx() ? toDeliver : Env.ZERO); + line.setQty(toDeliver); + if (!line.save()) + throw new IllegalStateException("Could not create Shipment Line"); + + } + } + } // createLine + + + /** + * Get Storages + * @param M_Warehouse_ID + * @param M_Product_ID + * @param M_AttributeSetInstance_ID + * @param minGuaranteeDate + * @param FiFo + * @return storages + */ + private MStorage[] getStorages(int M_Warehouse_ID, + int M_Product_ID, int M_AttributeSetInstance_ID, + Timestamp minGuaranteeDate, boolean FiFo) + { + m_lastPP = new SParameter(M_Warehouse_ID, + M_Product_ID, M_AttributeSetInstance_ID, + minGuaranteeDate, FiFo); + // + m_lastStorages = m_map.get(m_lastPP); + + if (m_lastStorages == null) + { + m_lastStorages = MStorage.getWarehouse(getCtx(), + M_Warehouse_ID, M_Product_ID, M_AttributeSetInstance_ID, + minGuaranteeDate, FiFo, true, 0 , get_TrxName()); + m_map.put(m_lastPP, m_lastStorages); + } + return m_lastStorages; + } // getStorages + + + /** + * Complete Shipment + */ + private void completeShipment() + { + if (m_shipment != null) + { + // Fails if there is a confirmation + if (!m_shipment.processIt(p_docAction)) + log.warning("Failed: " + m_shipment); + m_shipment.save(); + // + addLog(m_shipment.getM_InOut_ID(), m_shipment.getMovementDate(), null, m_shipment.getDocumentNo()); + m_created++; + + //reset storage cache as MInOut.completeIt will update m_storage + m_map = new HashMap(); + m_lastPP = null; + m_lastStorages = null; + } + m_shipment = null; + m_line = 0; + } // completeOrder + + /** + * InOutGenerate Parameter + */ + class SParameter + { + /** + * Parameter + * @param p_Warehouse_ID warehouse + * @param p_Product_ID + * @param p_AttributeSetInstance_ID + * @param p_minGuaranteeDate + * @param p_FiFo + */ + protected SParameter (int p_Warehouse_ID, + int p_Product_ID, int p_AttributeSetInstance_ID, + Timestamp p_minGuaranteeDate, boolean p_FiFo) + { + this.M_Warehouse_ID = p_Warehouse_ID; + this.M_Product_ID = p_Product_ID; + this.M_AttributeSetInstance_ID = p_AttributeSetInstance_ID; + this.minGuaranteeDate = p_minGuaranteeDate; + this.FiFo = p_FiFo; + } + /** Warehouse */ + public int M_Warehouse_ID; + /** Product */ + public int M_Product_ID; + /** ASI */ + public int M_AttributeSetInstance_ID; + /** AS */ + public int M_AttributeSet_ID; + /** All instances */ + public boolean allAttributeInstances; + /** Mon Guarantee Date */ + public Timestamp minGuaranteeDate; + /** FiFo */ + public boolean FiFo; + + /** + * Equals + * @param obj + * @return true if equal + */ + public boolean equals (Object obj) + { + if (obj != null && obj instanceof SParameter) + { + SParameter cmp = (SParameter)obj; + boolean eq = cmp.M_Warehouse_ID == M_Warehouse_ID + && cmp.M_Product_ID == M_Product_ID + && cmp.M_AttributeSetInstance_ID == M_AttributeSetInstance_ID + && cmp.M_AttributeSet_ID == M_AttributeSet_ID + && cmp.allAttributeInstances == allAttributeInstances + && cmp.FiFo == FiFo; + if (eq) + { + if (cmp.minGuaranteeDate == null && minGuaranteeDate == null) + ; + else if (cmp.minGuaranteeDate != null && minGuaranteeDate != null + && cmp.minGuaranteeDate.equals(minGuaranteeDate)) + ; + else + eq = false; + } + return eq; + } + return false; + } // equals + + /** + * hashCode + * @return hash code + */ + public int hashCode () + { + long hash = M_Warehouse_ID + + (M_Product_ID * 2) + + (M_AttributeSetInstance_ID * 3) + + (M_AttributeSet_ID * 4); + + if (allAttributeInstances) + hash *= -1; + if (FiFo); + hash *= -2; + if (hash < 0) + hash = -hash + 7; + while (hash > Integer.MAX_VALUE) + hash -= Integer.MAX_VALUE; + // + if (minGuaranteeDate != null) + { + hash += minGuaranteeDate.hashCode(); + while (hash > Integer.MAX_VALUE) + hash -= Integer.MAX_VALUE; + } + + return (int)hash; + } // hashCode + + } // Parameter + +} // InOutGenerate -import java.math.BigDecimal; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.Timestamp; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.logging.Level; - -import org.compiere.model.MAttributeSet; -import org.compiere.model.MClient; -import org.compiere.model.MInOut; -import org.compiere.model.MInOutLine; -import org.compiere.model.MOrder; -import org.compiere.model.MOrderLine; -import org.compiere.model.MProduct; -import org.compiere.model.MStorage; -import org.compiere.util.AdempiereUserError; -import org.compiere.util.DB; -import org.compiere.util.Env; - -/** - * Generate Shipments. - * Manual or Automatic - * - * @author Jorg Janke - * @version $Id: InOutGenerate.java,v 1.2 2006/07/30 00:51:01 jjanke Exp $ - */ -public class InOutGenerate extends SvrProcess -{ - /** Manual Selection */ - private boolean p_Selection = false; - /** Warehouse */ - private int p_M_Warehouse_ID = 0; - /** BPartner */ - private int p_C_BPartner_ID = 0; - /** Promise Date */ - private Timestamp p_DatePromised = null; - /** Include Orders w. unconfirmed Shipments */ - private boolean p_IsUnconfirmedInOut = false; - /** DocAction */ - private String p_docAction = DocAction.ACTION_Complete; - /** Consolidate */ - private boolean p_ConsolidateDocument = true; - /** Shipment Date */ - private Timestamp p_DateShipped = null; - - /** The current Shipment */ - private MInOut m_shipment = null; - /** Numner of Shipments */ - private int m_created = 0; - /** Line Number */ - private int m_line = 0; - /** Movement Date */ - private Timestamp m_movementDate = null; - /** Last BP Location */ - private int m_lastC_BPartner_Location_ID = -1; - - /** The Query sql */ - private String m_sql = null; - - - /** Storages temp space */ - private HashMap m_map = new HashMap(); - /** Last Parameter */ - private SParameter m_lastPP = null; - /** Last Storage */ - private MStorage[] m_lastStorages = null; - - - /** - * Prepare - e.g., get Parameters. - */ - protected void prepare() - { - ProcessInfoParameter[] para = getParameter(); - for (int i = 0; i < para.length; i++) - { - String name = para[i].getParameterName(); - if (para[i].getParameter() == null) - ; - else if (name.equals("M_Warehouse_ID")) - p_M_Warehouse_ID = para[i].getParameterAsInt(); - else if (name.equals("C_BPartner_ID")) - p_C_BPartner_ID = para[i].getParameterAsInt(); - else if (name.equals("DatePromised")) - p_DatePromised = (Timestamp)para[i].getParameter(); - else if (name.equals("Selection")) - p_Selection = "Y".equals(para[i].getParameter()); - else if (name.equals("IsUnconfirmedInOut")) - p_IsUnconfirmedInOut = "Y".equals(para[i].getParameter()); - else if (name.equals("ConsolidateDocument")) - p_ConsolidateDocument = "Y".equals(para[i].getParameter()); - else if (name.equals("DocAction")) - p_docAction = (String)para[i].getParameter(); - else if (name.equals("MovementDate")) - p_DateShipped = (Timestamp)para[i].getParameter(); - else - log.log(Level.SEVERE, "Unknown Parameter: " + name); - - // juddm - added ability to specify a shipment date from Generate Shipments - if (p_DateShipped == null) { - m_movementDate = Env.getContextAsDate(getCtx(), "#Date"); - if (m_movementDate == null) - m_movementDate = new Timestamp(System.currentTimeMillis()); - } else - m_movementDate = p_DateShipped; - // DocAction check - if (!DocAction.ACTION_Complete.equals(p_docAction)) - p_docAction = DocAction.ACTION_Prepare; - } - } // prepare - - /** - * Generate Shipments - * @return info - * @throws Exception - */ - protected String doIt () throws Exception - { - log.info("Selection=" + p_Selection - + ", M_Warehouse_ID=" + p_M_Warehouse_ID - + ", C_BPartner_ID=" + p_C_BPartner_ID - + ", Consolidate=" + p_ConsolidateDocument - + ", IsUnconfirmed=" + p_IsUnconfirmedInOut - + ", Movement=" + m_movementDate); - - if (p_M_Warehouse_ID == 0) - throw new AdempiereUserError("@NotFound@ @M_Warehouse_ID@"); - - if (p_Selection) // VInOutGen - { - m_sql = "SELECT C_Order.* FROM C_Order, T_Selection " - + "WHERE C_Order.DocStatus='CO' AND C_Order.IsSOTrx='Y' AND C_Order.AD_Client_ID=? " - + "AND C_Order.C_Order_ID = T_Selection.T_Selection_ID " - + "AND T_Selection.AD_PInstance_ID=? "; - } - else - { - m_sql = "SELECT * FROM C_Order o " - + "WHERE DocStatus='CO' AND IsSOTrx='Y'" - // No Offer,POS - + " AND o.C_DocType_ID IN (SELECT C_DocType_ID FROM C_DocType " - + "WHERE DocBaseType='SOO' AND DocSubTypeSO NOT IN ('ON','OB','WR'))" - + " AND o.IsDropShip='N'" - // No Manual - + " AND o.DeliveryRule<>'M'" - // Open Order Lines with Warehouse - + " AND EXISTS (SELECT * FROM C_OrderLine ol " - + "WHERE ol.M_Warehouse_ID=?"; // #1 - if (p_DatePromised != null) - m_sql += " AND TRUNC(ol.DatePromised)<=?"; // #2 - m_sql += " AND o.C_Order_ID=ol.C_Order_ID AND ol.QtyOrdered<>ol.QtyDelivered)"; - // - if (p_C_BPartner_ID != 0) - m_sql += " AND o.C_BPartner_ID=?"; // #3 - } - m_sql += " ORDER BY M_Warehouse_ID, PriorityRule, M_Shipper_ID, C_BPartner_ID, C_BPartner_Location_ID, C_Order_ID"; - // m_sql += " FOR UPDATE"; - - PreparedStatement pstmt = null; - try - { - pstmt = DB.prepareStatement (m_sql, get_TrxName()); - int index = 1; - if (p_Selection) - { - pstmt.setInt(index++, Env.getAD_Client_ID(getCtx())); - pstmt.setInt(index++, getAD_PInstance_ID()); - } - else - { - pstmt.setInt(index++, p_M_Warehouse_ID); - if (p_DatePromised != null) - pstmt.setTimestamp(index++, p_DatePromised); - if (p_C_BPartner_ID != 0) - pstmt.setInt(index++, p_C_BPartner_ID); - } - } - catch (Exception e) - { - log.log(Level.SEVERE, m_sql, e); - } - return generate(pstmt); - } // doIt - - /** - * Generate Shipments - * @param pstmt Order Query - * @return info - */ - private String generate (PreparedStatement pstmt) - { - MClient client = MClient.get(getCtx()); - try - { - ResultSet rs = pstmt.executeQuery (); - while (rs.next ()) // Order - { - MOrder order = new MOrder (getCtx(), rs, get_TrxName()); - // New Header different Shipper, Shipment Location - if (!p_ConsolidateDocument - || (m_shipment != null - && (m_shipment.getC_BPartner_Location_ID() != order.getC_BPartner_Location_ID() - || m_shipment.getM_Shipper_ID() != order.getM_Shipper_ID() ))) - completeShipment(); - log.fine("check: " + order + " - DeliveryRule=" + order.getDeliveryRule()); - // - Timestamp minGuaranteeDate = m_movementDate; - boolean completeOrder = MOrder.DELIVERYRULE_CompleteOrder.equals(order.getDeliveryRule()); - // OrderLine WHERE - String where = " AND M_Warehouse_ID=" + p_M_Warehouse_ID; - if (p_DatePromised != null) - where += " AND (TRUNC(DatePromised)<=" + DB.TO_DATE(p_DatePromised, true) - + " OR DatePromised IS NULL)"; - // Exclude Auto Delivery if not Force - if (!MOrder.DELIVERYRULE_Force.equals(order.getDeliveryRule())) - where += " AND (C_OrderLine.M_Product_ID IS NULL" - + " OR EXISTS (SELECT * FROM M_Product p " - + "WHERE C_OrderLine.M_Product_ID=p.M_Product_ID" - + " AND IsExcludeAutoDelivery='N'))"; - // Exclude Unconfirmed - if (!p_IsUnconfirmedInOut) - where += " AND NOT EXISTS (SELECT * FROM M_InOutLine iol" - + " INNER JOIN M_InOut io ON (iol.M_InOut_ID=io.M_InOut_ID) " - + "WHERE iol.C_OrderLine_ID=C_OrderLine.C_OrderLine_ID AND io.DocStatus IN ('IP','WC'))"; - // Deadlock Prevention - Order by M_Product_ID - MOrderLine[] lines = order.getLines (where, "C_BPartner_Location_ID, M_Product_ID"); - for (int i = 0; i < lines.length; i++) - { - MOrderLine line = lines[i]; - if (line.getM_Warehouse_ID() != p_M_Warehouse_ID) - continue; - log.fine("check: " + line); - BigDecimal onHand = Env.ZERO; - BigDecimal toDeliver = line.getQtyOrdered() - .subtract(line.getQtyDelivered()); - MProduct product = line.getProduct(); - // Nothing to Deliver - if (product != null && toDeliver.signum() == 0) - continue; - - // or it's a charge - Bug#: 1603966 - if (line.getC_Charge_ID()!=0 && toDeliver.signum() == 0) - continue; - - // Check / adjust for confirmations - BigDecimal unconfirmedShippedQty = Env.ZERO; - if (p_IsUnconfirmedInOut && product != null && toDeliver.signum() != 0) - { - String where2 = "EXISTS (SELECT * FROM M_InOut io WHERE io.M_InOut_ID=M_InOutLine.M_InOut_ID AND io.DocStatus IN ('IP','WC'))"; - MInOutLine[] iols = MInOutLine.getOfOrderLine(getCtx(), - line.getC_OrderLine_ID(), where2, null); - for (int j = 0; j < iols.length; j++) - unconfirmedShippedQty = unconfirmedShippedQty.add(iols[j].getMovementQty()); - String logInfo = "Unconfirmed Qty=" + unconfirmedShippedQty - + " - ToDeliver=" + toDeliver + "->"; - toDeliver = toDeliver.subtract(unconfirmedShippedQty); - logInfo += toDeliver; - if (toDeliver.signum() < 0) - { - toDeliver = Env.ZERO; - logInfo += " (set to 0)"; - } - // Adjust On Hand - onHand = onHand.subtract(unconfirmedShippedQty); - log.fine(logInfo); - } - - // Comments & lines w/o product & services - if ((product == null || !product.isStocked()) - && (line.getQtyOrdered().signum() == 0 // comments - || toDeliver.signum() != 0)) // lines w/o product - { - if (!MOrder.DELIVERYRULE_CompleteOrder.equals(order.getDeliveryRule())) // printed later - createLine (order, line, toDeliver, null, false); - continue; - } - - // Stored Product - String MMPolicy = product.getMMPolicy(); - MStorage[] storages = getStorages(line.getM_Warehouse_ID(), - line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), - product.getM_AttributeSet_ID(), - line.getM_AttributeSetInstance_ID()==0, minGuaranteeDate, - MClient.MMPOLICY_FiFo.equals(MMPolicy)); - - for (int j = 0; j < storages.length; j++) - { - MStorage storage = storages[j]; - onHand = onHand.add(storage.getQtyOnHand()); - } - boolean fullLine = onHand.compareTo(toDeliver) >= 0 - || toDeliver.signum() < 0; - - // Complete Order - if (completeOrder && !fullLine) - { - log.fine("Failed CompleteOrder - OnHand=" + onHand - + " (Unconfirmed=" + unconfirmedShippedQty - + "), ToDeliver=" + toDeliver + " - " + line); - completeOrder = false; - break; - } - // Complete Line - else if (fullLine && MOrder.DELIVERYRULE_CompleteLine.equals(order.getDeliveryRule())) - { - log.fine("CompleteLine - OnHand=" + onHand - + " (Unconfirmed=" + unconfirmedShippedQty - + ", ToDeliver=" + toDeliver + " - " + line); - // - createLine (order, line, toDeliver, storages, false); - } - // Availability - else if (MOrder.DELIVERYRULE_Availability.equals(order.getDeliveryRule()) - && (onHand.signum() > 0 - || toDeliver.signum() < 0)) - { - BigDecimal deliver = toDeliver; - if (deliver.compareTo(onHand) > 0) - deliver = onHand; - log.fine("Available - OnHand=" + onHand - + " (Unconfirmed=" + unconfirmedShippedQty - + "), ToDeliver=" + toDeliver - + ", Delivering=" + deliver + " - " + line); - // - createLine (order, line, deliver, storages, false); - } - // Force - else if (MOrder.DELIVERYRULE_Force.equals(order.getDeliveryRule())) - { - BigDecimal deliver = toDeliver; - log.fine("Force - OnHand=" + onHand - + " (Unconfirmed=" + unconfirmedShippedQty - + "), ToDeliver=" + toDeliver - + ", Delivering=" + deliver + " - " + line); - // - createLine (order, line, deliver, storages, true); - } - // Manual - else if (MOrder.DELIVERYRULE_Manual.equals(order.getDeliveryRule())) - log.fine("Manual - OnHand=" + onHand - + " (Unconfirmed=" + unconfirmedShippedQty - + ") - " + line); - else - log.fine("Failed: " + order.getDeliveryRule() + " - OnHand=" + onHand - + " (Unconfirmed=" + unconfirmedShippedQty - + "), ToDeliver=" + toDeliver + " - " + line); - } // for all order lines - - // Complete Order successful - if (completeOrder && MOrder.DELIVERYRULE_CompleteOrder.equals(order.getDeliveryRule())) - { - for (int i = 0; i < lines.length; i++) - { - MOrderLine line = lines[i]; - if (line.getM_Warehouse_ID() != p_M_Warehouse_ID) - continue; - MProduct product = line.getProduct(); - BigDecimal toDeliver = line.getQtyOrdered().subtract(line.getQtyDelivered()); - // - MStorage[] storages = null; - if (product != null && product.isStocked()) - { - String MMPolicy = product.getMMPolicy(); - storages = getStorages(line.getM_Warehouse_ID(), - line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), - product.getM_AttributeSet_ID(), - line.getM_AttributeSetInstance_ID()==0, minGuaranteeDate, - MClient.MMPOLICY_FiFo.equals(MMPolicy)); - } - // - createLine (order, line, toDeliver, storages, false); - } - } - m_line += 1000; - } // while order - rs.close (); - pstmt.close (); - pstmt = null; - } - catch (Exception e) - { - log.log(Level.SEVERE, m_sql, e); - } - try - { - if (pstmt != null) - pstmt.close (); - pstmt = null; - } - catch (Exception e) - { - pstmt = null; - } - completeShipment(); - return "@Created@ = " + m_created; - } // generate - - - - /************************************************************************** - * Create Line - * @param order order - * @param orderLine line - * @param qty qty - * @param storages storage info - * @param force force delivery - */ - private void createLine (MOrder order, MOrderLine orderLine, BigDecimal qty, - MStorage[] storages, boolean force) - { - // Complete last Shipment - can have multiple shipments - if (m_lastC_BPartner_Location_ID != orderLine.getC_BPartner_Location_ID() ) - completeShipment(); - m_lastC_BPartner_Location_ID = orderLine.getC_BPartner_Location_ID(); - // Create New Shipment - if (m_shipment == null) - { - m_shipment = new MInOut (order, 0, m_movementDate); - m_shipment.setM_Warehouse_ID(orderLine.getM_Warehouse_ID()); // sets Org too - if (order.getC_BPartner_ID() != orderLine.getC_BPartner_ID()) - m_shipment.setC_BPartner_ID(orderLine.getC_BPartner_ID()); - if (order.getC_BPartner_Location_ID() != orderLine.getC_BPartner_Location_ID()) - m_shipment.setC_BPartner_Location_ID(orderLine.getC_BPartner_Location_ID()); - if (!m_shipment.save()) - throw new IllegalStateException("Could not create Shipment"); - } - // Non Inventory Lines - if (storages == null) - { - MInOutLine line = new MInOutLine (m_shipment); - line.setOrderLine(orderLine, 0, Env.ZERO); - line.setQty(qty); // Correct UOM - if (orderLine.getQtyEntered().compareTo(orderLine.getQtyOrdered()) != 0) - line.setQtyEntered(qty - .multiply(orderLine.getQtyEntered()) - .divide(orderLine.getQtyOrdered(), 12, BigDecimal.ROUND_HALF_UP)); - line.setLine(m_line + orderLine.getLine()); - if (!line.save()) - throw new IllegalStateException("Could not create Shipment Line"); - log.fine(line.toString()); - return; - } - - // Product - MProduct product = orderLine.getProduct(); - boolean linePerASI = false; - if (product.getM_AttributeSet_ID() != 0) - { - MAttributeSet mas = MAttributeSet.get(getCtx(), product.getM_AttributeSet_ID()); - linePerASI = mas.isInstanceAttribute(); - } - - // Inventory Lines - ArrayList list = new ArrayList(); - BigDecimal toDeliver = qty; - for (int i = 0; i < storages.length; i++) - { - MStorage storage = storages[i]; - BigDecimal deliver = toDeliver; - // Not enough On Hand - if (deliver.compareTo(storage.getQtyOnHand()) > 0 - && storage.getQtyOnHand().signum() >= 0) // positive storage - { - if (!force // Adjust to OnHand Qty - || (force && i+1 != storages.length)) // if force not on last location - deliver = storage.getQtyOnHand(); - } - if (deliver.signum() == 0) // zero deliver - continue; - int M_Locator_ID = storage.getM_Locator_ID(); - // - MInOutLine line = null; - if (!linePerASI) // find line with Locator - { - for (int ll = 0; ll < list.size(); ll++) - { - MInOutLine test = (MInOutLine)list.get(ll); - if (test.getM_Locator_ID() == M_Locator_ID) - { - line = test; - break; - } - } - } - if (line == null) // new line - { - line = new MInOutLine (m_shipment); - line.setOrderLine(orderLine, M_Locator_ID, order.isSOTrx() ? deliver : Env.ZERO); - line.setQty(deliver); - list.add(line); - } - else // existing line - line.setQty(line.getMovementQty().add(deliver)); - if (orderLine.getQtyEntered().compareTo(orderLine.getQtyOrdered()) != 0) - line.setQtyEntered(line.getMovementQty().multiply(orderLine.getQtyEntered()) - .divide(orderLine.getQtyOrdered(), 12, BigDecimal.ROUND_HALF_UP)); - line.setLine(m_line + orderLine.getLine()); - if (linePerASI) - line.setM_AttributeSetInstance_ID(storage.getM_AttributeSetInstance_ID()); - if (!line.save()) - throw new IllegalStateException("Could not create Shipment Line"); - log.fine("ToDeliver=" + qty + "/" + deliver + " - " + line); - toDeliver = toDeliver.subtract(deliver); - // Temp adjustment - storage.setQtyOnHand(storage.getQtyOnHand().subtract(deliver)); - // - if (toDeliver.signum() == 0) - break; - } - if (toDeliver.signum() != 0) - throw new IllegalStateException("Not All Delivered - Remainder=" + toDeliver); - } // createLine - - - /** - * Get Storages - * @param M_Warehouse_ID - * @param M_Product_ID - * @param M_AttributeSetInstance_ID - * @param M_AttributeSet_ID - * @param allAttributeInstances - * @param minGuaranteeDate - * @param FiFo - * @return storages - */ - private MStorage[] getStorages(int M_Warehouse_ID, - int M_Product_ID, int M_AttributeSetInstance_ID, int M_AttributeSet_ID, - boolean allAttributeInstances, Timestamp minGuaranteeDate, - boolean FiFo) - { - m_lastPP = new SParameter(M_Warehouse_ID, - M_Product_ID, M_AttributeSetInstance_ID, M_AttributeSet_ID, - allAttributeInstances, minGuaranteeDate, FiFo); - // - m_lastStorages = m_map.get(m_lastPP); - - if (m_lastStorages == null) - { - m_lastStorages = MStorage.getWarehouse(getCtx(), - M_Warehouse_ID, M_Product_ID, M_AttributeSetInstance_ID, - M_AttributeSet_ID, allAttributeInstances, minGuaranteeDate, - FiFo, get_TrxName()); - m_map.put(m_lastPP, m_lastStorages); - } - return m_lastStorages; - } // getStorages - - - /** - * Complete Shipment - */ - private void completeShipment() - { - if (m_shipment != null) - { - // Fails if there is a confirmation - if (!m_shipment.processIt(p_docAction)) - log.warning("Failed: " + m_shipment); - m_shipment.save(); - // - addLog(m_shipment.getM_InOut_ID(), m_shipment.getMovementDate(), null, m_shipment.getDocumentNo()); - m_created++; - m_map = new HashMap(); - if (m_lastPP != null && m_lastStorages != null) - m_map.put(m_lastPP, m_lastStorages); - } - m_shipment = null; - m_line = 0; - } // completeOrder - - /** - * InOutGenerate Parameter - */ - class SParameter - { - /** - * Parameter - * @param p_Warehouse_ID warehouse - * @param p_Product_ID - * @param p_AttributeSetInstance_ID - * @param p_AttributeSet_ID - * @param p_allAttributeInstances - * @param p_minGuaranteeDate - * @param p_FiFo - */ - protected SParameter (int p_Warehouse_ID, - int p_Product_ID, int p_AttributeSetInstance_ID, int p_AttributeSet_ID, - boolean p_allAttributeInstances, Timestamp p_minGuaranteeDate, - boolean p_FiFo) - { - this.M_Warehouse_ID = p_Warehouse_ID; - this.M_Product_ID = p_Product_ID; - this.M_AttributeSetInstance_ID = p_AttributeSetInstance_ID; - this.M_AttributeSet_ID = p_AttributeSet_ID; - this.allAttributeInstances = p_allAttributeInstances; - this.minGuaranteeDate = p_minGuaranteeDate; - this.FiFo = p_FiFo; - } - /** Warehouse */ - public int M_Warehouse_ID; - /** Product */ - public int M_Product_ID; - /** ASI */ - public int M_AttributeSetInstance_ID; - /** AS */ - public int M_AttributeSet_ID; - /** All instances */ - public boolean allAttributeInstances; - /** Mon Guarantee Date */ - public Timestamp minGuaranteeDate; - /** FiFo */ - public boolean FiFo; - - /** - * Equals - * @param obj - * @return true if equal - */ - public boolean equals (Object obj) - { - if (obj != null && obj instanceof SParameter) - { - SParameter cmp = (SParameter)obj; - boolean eq = cmp.M_Warehouse_ID == M_Warehouse_ID - && cmp.M_Product_ID == M_Product_ID - && cmp.M_AttributeSetInstance_ID == M_AttributeSetInstance_ID - && cmp.M_AttributeSet_ID == M_AttributeSet_ID - && cmp.allAttributeInstances == allAttributeInstances - && cmp.FiFo == FiFo; - if (eq) - { - if (cmp.minGuaranteeDate == null && minGuaranteeDate == null) - ; - else if (cmp.minGuaranteeDate != null && minGuaranteeDate != null - && cmp.minGuaranteeDate.equals(minGuaranteeDate)) - ; - else - eq = false; - } - return eq; - } - return false; - } // equals - - /** - * hashCode - * @return hash code - */ - public int hashCode () - { - long hash = M_Warehouse_ID - + (M_Product_ID * 2) - + (M_AttributeSetInstance_ID * 3) - + (M_AttributeSet_ID * 4); - - if (allAttributeInstances) - hash *= -1; - if (FiFo); - hash *= -2; - if (hash < 0) - hash = -hash + 7; - while (hash > Integer.MAX_VALUE) - hash -= Integer.MAX_VALUE; - // - if (minGuaranteeDate != null) - { - hash += minGuaranteeDate.hashCode(); - while (hash > Integer.MAX_VALUE) - hash -= Integer.MAX_VALUE; - } - - return (int)hash; - } // hashCode - - } // Parameter - -} // InOutGenerate