From 90033127696fa935e418776b76b048d38bfc895f Mon Sep 17 00:00:00 2001 From: vpj-cd Date: Fri, 30 Jan 2009 02:04:42 +0000 Subject: [PATCH] Issuing of stock should ignore storage with onhand <= 0 - ID: 2545020 http://sourceforge.net/tracker2/index.php?func=detail&aid=2545020&group_id=176962&atid=879332 Merge change from 8234 revision http://adempiere.svn.sourceforge.net/viewvc/adempiere?view=rev&revision=8234 [ 2545020 ] Issuing of stock should ignore storage with onhand <= 0 - Only process storage with qtyOnHand > 0 when fulfilling for customer shipment, inventory movement and physical inventory. - Remove remarked code. - Fixed javadoc in MStorage, restore the original behaviour of getAllASI. Refactor getWarehouse method as the primary method use to retrieve storage records for customer shipment, inventory movement and physical inventory transaction. - Fixed MStorage.getWarehouse to behave as stated in the javadoc, i.e always respect the locator priority. - Remove the linePerASI logic in InOutGenerate so that the actual allocation of storage only happens in MInOut. Ensure proper reset of storage cache. --- base/src/org/compiere/model/MInOut.java | 66 +- base/src/org/compiere/model/MInventory.java | 1939 ++++++++--------- base/src/org/compiere/model/MMovement.java | 76 +- base/src/org/compiere/model/MStorage.java | 98 +- .../org/compiere/process/InOutGenerate.java | 1372 ++++++------ 5 files changed, 1684 insertions(+), 1867 deletions(-) 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