From e3961c7d613b02a148a537b18cf19d5cebd9bfcd Mon Sep 17 00:00:00 2001 From: Heng Sin Low Date: Fri, 15 Sep 2017 11:15:10 +0800 Subject: [PATCH] IDEMPIERE-3488 Improvement to Error Message for Negative Inventory Disallow exception --- .../oracle/201709151000_IDEMPIERE-3488.sql | 11 + .../201709151000_IDEMPIERE-3488.sql | 8 + .../NegativeInventoryDisallowedException.java | 88 +++ .../src/org/compiere/model/MInOut.java | 610 +++++++++--------- .../src/org/compiere/model/MInventory.java | 298 +++++---- .../src/org/compiere/model/MMovement.java | 276 ++++---- .../src/org/compiere/model/MProjectIssue.java | 37 +- .../org/compiere/model/MStorageOnHand.java | 12 +- .../src/org/eevolution/model/MDDOrder.java | 62 +- 9 files changed, 795 insertions(+), 607 deletions(-) create mode 100644 migration/i4.1/oracle/201709151000_IDEMPIERE-3488.sql create mode 100644 migration/i4.1/postgresql/201709151000_IDEMPIERE-3488.sql create mode 100644 org.adempiere.base/src/org/adempiere/exceptions/NegativeInventoryDisallowedException.java diff --git a/migration/i4.1/oracle/201709151000_IDEMPIERE-3488.sql b/migration/i4.1/oracle/201709151000_IDEMPIERE-3488.sql new file mode 100644 index 0000000000..06b9126c82 --- /dev/null +++ b/migration/i4.1/oracle/201709151000_IDEMPIERE-3488.sql @@ -0,0 +1,11 @@ +SET SQLBLANKLINES ON +SET DEFINE OFF + +-- IDEMPIERE-3488 Improvement to Error Message for Negative Inventory Disallow exception +-- Sep 14, 2017 6:28:55 PM GMT+08:00 +INSERT INTO AD_Message (MsgType,MsgText,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Message_ID,Value,EntityType,AD_Message_UU) VALUES ('E','The {0} warehouse does not allow negative inventory for Product = {1}, ASI = {2}, Locator = {3} (Shortage of {4})',0,0,'Y',TO_DATE('2017-09-14 18:28:54','YYYY-MM-DD HH24:MI:SS'),100,TO_DATE('2017-09-14 18:28:54','YYYY-MM-DD HH24:MI:SS'),100,200431,'NegativeInventoryDisallowedInfo','D','1a686715-09f5-4437-9885-882719423bd1') +; + +SELECT register_migration_script('201709151000_IDEMPIERE-3488.sql') FROM dual +; + diff --git a/migration/i4.1/postgresql/201709151000_IDEMPIERE-3488.sql b/migration/i4.1/postgresql/201709151000_IDEMPIERE-3488.sql new file mode 100644 index 0000000000..d585048143 --- /dev/null +++ b/migration/i4.1/postgresql/201709151000_IDEMPIERE-3488.sql @@ -0,0 +1,8 @@ +-- IDEMPIERE-3488 Improvement to Error Message for Negative Inventory Disallow exception +-- Sep 14, 2017 6:28:55 PM GMT+08:00 +INSERT INTO AD_Message (MsgType,MsgText,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Message_ID,Value,EntityType,AD_Message_UU) VALUES ('E','The {0} warehouse does not allow negative inventory for Product = {1}, ASI = {2}, Locator = {3} (Shortage of {4})',0,0,'Y',TO_TIMESTAMP('2017-09-14 18:28:54','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2017-09-14 18:28:54','YYYY-MM-DD HH24:MI:SS'),100,200431,'NegativeInventoryDisallowedInfo','D','1a686715-09f5-4437-9885-882719423bd1') +; + +SELECT register_migration_script('201709151000_IDEMPIERE-3488.sql') FROM dual +; + diff --git a/org.adempiere.base/src/org/adempiere/exceptions/NegativeInventoryDisallowedException.java b/org.adempiere.base/src/org/adempiere/exceptions/NegativeInventoryDisallowedException.java new file mode 100644 index 0000000000..da49059d85 --- /dev/null +++ b/org.adempiere.base/src/org/adempiere/exceptions/NegativeInventoryDisallowedException.java @@ -0,0 +1,88 @@ +/******************************************************************************* + * Copyright (C) 2017 Trek Global Inc. * + * Copyright (C) 2017 Low Heng Sin * + * This program is free software; you can redistribute it and/or modify it * + * under the terms version 2 of the GNU General Public License as published * + * by the Free Software Foundation. This program is distributed in the hope * + * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied * + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * + * See the GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License along * + * with this program; if not, write to the Free Software Foundation, Inc., * + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. * + *******************************************************************************/ +package org.adempiere.exceptions; + +import java.math.BigDecimal; +import java.util.Properties; + +import org.compiere.model.MAttributeSetInstance; +import org.compiere.model.MLocator; +import org.compiere.model.MProduct; +import org.compiere.model.MSysConfig; +import org.compiere.model.MWarehouse; +import org.compiere.util.Env; +import org.compiere.util.Msg; + +/** + * + * @author hengsin + * + */ +public class NegativeInventoryDisallowedException extends AdempiereException +{ + /** + * + */ + private static final long serialVersionUID = 253224414462489886L; + + private int M_Warehouse_ID; + private int M_Product_ID; + private int M_AttributeSetInstance_ID; + private int M_Locator_ID; + private BigDecimal QtyOnHand; + private BigDecimal MovementQty; + + public NegativeInventoryDisallowedException(Properties ctx, int M_Warehouse_ID, int M_Product_ID, int M_AttributeSetInstance_ID, int M_Locator_ID, + BigDecimal QtyOnHand, BigDecimal MovementQty) + { + super(Msg.getMsg(ctx, "NegativeInventoryDisallowedInfo", new Object[] { + MWarehouse.get(ctx, M_Warehouse_ID).getName(), + MProduct.get(ctx, M_Product_ID).getValue() + MSysConfig.getValue(MSysConfig.IDENTIFIER_SEPARATOR, "_", Env.getAD_Client_ID(ctx)) + MProduct.get(ctx, M_Product_ID).getName(), + M_AttributeSetInstance_ID > 0 ? MAttributeSetInstance.get(ctx, M_AttributeSetInstance_ID, M_Product_ID).getDescription() : "0", + M_Locator_ID > 0 ? MLocator.get(ctx, M_Locator_ID).getValue() : "0", MovementQty.subtract(QtyOnHand) + + })); + + this.M_Warehouse_ID = M_Warehouse_ID; + this.M_Product_ID = M_Product_ID; + this.M_AttributeSetInstance_ID = M_AttributeSetInstance_ID; + this.M_Locator_ID = M_Locator_ID; + this.QtyOnHand = QtyOnHand; + this.MovementQty = MovementQty; + } + + public int getM_Warehouse_ID() { + return M_Warehouse_ID; + } + + public int getM_Product_ID() { + return M_Product_ID; + } + + public int getM_AttributeSetInstance_ID() { + return M_AttributeSetInstance_ID; + } + + public int getM_Locator_ID() { + return M_Locator_ID; + } + + public BigDecimal getQtyOnHand() { + return QtyOnHand; + } + + public BigDecimal getMovementQty() { + return MovementQty; + } +} diff --git a/org.adempiere.base/src/org/compiere/model/MInOut.java b/org.adempiere.base/src/org/compiere/model/MInOut.java index d8c0627596..35768187e1 100644 --- a/org.adempiere.base/src/org/compiere/model/MInOut.java +++ b/org.adempiere.base/src/org/compiere/model/MInOut.java @@ -26,6 +26,7 @@ import java.util.Properties; import java.util.logging.Level; import org.adempiere.exceptions.AdempiereException; +import org.adempiere.exceptions.NegativeInventoryDisallowedException; import org.adempiere.exceptions.PeriodClosedException; import org.compiere.print.MPrintFormat; import org.compiere.print.ReportEngine; @@ -1286,6 +1287,7 @@ public class MInOut extends X_M_InOut implements DocAction if (log.isLoggable(Level.INFO)) log.info(toString()); StringBuilder info = new StringBuilder(); + StringBuilder errors = new StringBuilder(); // For all lines MInOutLine[] lines = getLines(false); for (int lineIndex = 0; lineIndex < lines.length; lineIndex++) @@ -1293,353 +1295,326 @@ public class MInOut extends X_M_InOut implements DocAction MInOutLine sLine = lines[lineIndex]; MProduct product = sLine.getProduct(); - // Qty & Type - String MovementType = getMovementType(); - BigDecimal Qty = sLine.getMovementQty(); - if (MovementType.charAt(1) == '-') // C- Customer Shipment - V- Vendor Return - Qty = Qty.negate(); - - // Update Order Line - MOrderLine oLine = null; - if (sLine.getC_OrderLine_ID() != 0) + try { - oLine = new MOrderLine (getCtx(), sLine.getC_OrderLine_ID(), get_TrxName()); - if (log.isLoggable(Level.FINE)) log.fine("OrderLine - Reserved=" + oLine.getQtyReserved() - + ", Delivered=" + oLine.getQtyDelivered()); - } - - - // Load RMA Line - MRMALine rmaLine = null; - - if (sLine.getM_RMALine_ID() != 0) - { - rmaLine = new MRMALine(getCtx(), sLine.getM_RMALine_ID(), get_TrxName()); - } - - if (log.isLoggable(Level.INFO)) log.info("Line=" + sLine.getLine() + " - Qty=" + sLine.getMovementQty()); - - // Stock Movement - Counterpart MOrder.reserveStock - if (product != null - && product.isStocked() ) - { - //Ignore the Material Policy when is Reverse Correction - if(!isReversal()) + // Qty & Type + String MovementType = getMovementType(); + BigDecimal Qty = sLine.getMovementQty(); + if (MovementType.charAt(1) == '-') // C- Customer Shipment - V- Vendor Return + Qty = Qty.negate(); + + // Update Order Line + MOrderLine oLine = null; + if (sLine.getC_OrderLine_ID() != 0) { - BigDecimal movementQty = sLine.getMovementQty(); - BigDecimal qtyOnLineMA = MInOutLineMA.getManualQty(sLine.getM_InOutLine_ID(), get_TrxName()); - - if ( (movementQty.signum() != 0 && qtyOnLineMA.signum() != 0 && movementQty.signum() != qtyOnLineMA.signum()) // must have same sign - || (qtyOnLineMA.abs().compareTo(movementQty.abs())>0)) { // compare absolute values - // More then line qty on attribute tab for line 10 - m_processMsg = "@Over_Qty_On_Attribute_Tab@ " + sLine.getLine(); - return DOCSTATUS_Invalid; - } - - checkMaterialPolicy(sLine,movementQty.subtract(qtyOnLineMA)); + oLine = new MOrderLine (getCtx(), sLine.getC_OrderLine_ID(), get_TrxName()); + if (log.isLoggable(Level.FINE)) log.fine("OrderLine - Reserved=" + oLine.getQtyReserved() + + ", Delivered=" + oLine.getQtyDelivered()); } - - log.fine("Material Transaction"); - MTransaction mtrx = null; - - // - BigDecimal overReceipt = BigDecimal.ZERO; - if (!isReversal()) + + + // Load RMA Line + MRMALine rmaLine = null; + + if (sLine.getM_RMALine_ID() != 0) + { + rmaLine = new MRMALine(getCtx(), sLine.getM_RMALine_ID(), get_TrxName()); + } + + if (log.isLoggable(Level.INFO)) log.info("Line=" + sLine.getLine() + " - Qty=" + sLine.getMovementQty()); + + // Stock Movement - Counterpart MOrder.reserveStock + if (product != null + && product.isStocked() ) { - if (oLine != null) + //Ignore the Material Policy when is Reverse Correction + if(!isReversal()) { - BigDecimal toDelivered = oLine.getQtyOrdered() - .subtract(oLine.getQtyDelivered()); - if (toDelivered.signum() < 0) // IDEMPIERE-2889 - toDelivered = Env.ZERO; - if (sLine.getMovementQty().compareTo(toDelivered) > 0) - overReceipt = sLine.getMovementQty().subtract( - toDelivered); - if (overReceipt.signum() != 0) - { - sLine.setQtyOverReceipt(overReceipt); - sLine.saveEx(); + BigDecimal movementQty = sLine.getMovementQty(); + BigDecimal qtyOnLineMA = MInOutLineMA.getManualQty(sLine.getM_InOutLine_ID(), get_TrxName()); + + if ( (movementQty.signum() != 0 && qtyOnLineMA.signum() != 0 && movementQty.signum() != qtyOnLineMA.signum()) // must have same sign + || (qtyOnLineMA.abs().compareTo(movementQty.abs())>0)) { // compare absolute values + // More then line qty on attribute tab for line 10 + m_processMsg = "@Over_Qty_On_Attribute_Tab@ " + sLine.getLine(); + return DOCSTATUS_Invalid; } + + checkMaterialPolicy(sLine,movementQty.subtract(qtyOnLineMA)); } - } - else - { - overReceipt = sLine.getQtyOverReceipt(); - } - BigDecimal orderedQtyToUpdate = sLine.getMovementQty().subtract(overReceipt); - // - if (sLine.getM_AttributeSetInstance_ID() == 0) - { - MInOutLineMA mas[] = MInOutLineMA.get(getCtx(), - sLine.getM_InOutLine_ID(), get_TrxName()); - for (int j = 0; j < mas.length; j++) + + log.fine("Material Transaction"); + MTransaction mtrx = null; + + // + BigDecimal overReceipt = BigDecimal.ZERO; + if (!isReversal()) { - MInOutLineMA ma = mas[j]; - BigDecimal QtyMA = ma.getMovementQty(); - if (MovementType.charAt(1) == '-') // C- Customer Shipment - V- Vendor Return - QtyMA = QtyMA.negate(); - - // Update Storage - see also VMatch.createMatchRecord + if (oLine != null) + { + BigDecimal toDelivered = oLine.getQtyOrdered() + .subtract(oLine.getQtyDelivered()); + if (toDelivered.signum() < 0) // IDEMPIERE-2889 + toDelivered = Env.ZERO; + if (sLine.getMovementQty().compareTo(toDelivered) > 0) + overReceipt = sLine.getMovementQty().subtract( + toDelivered); + if (overReceipt.signum() != 0) + { + sLine.setQtyOverReceipt(overReceipt); + sLine.saveEx(); + } + } + } + else + { + overReceipt = sLine.getQtyOverReceipt(); + } + BigDecimal orderedQtyToUpdate = sLine.getMovementQty().subtract(overReceipt); + // + if (sLine.getM_AttributeSetInstance_ID() == 0) + { + MInOutLineMA mas[] = MInOutLineMA.get(getCtx(), + sLine.getM_InOutLine_ID(), get_TrxName()); + for (int j = 0; j < mas.length; j++) + { + MInOutLineMA ma = mas[j]; + BigDecimal QtyMA = ma.getMovementQty(); + if (MovementType.charAt(1) == '-') // C- Customer Shipment - V- Vendor Return + QtyMA = QtyMA.negate(); + + // Update Storage - see also VMatch.createMatchRecord + if (!MStorageOnHand.add(getCtx(), getM_Warehouse_ID(), + sLine.getM_Locator_ID(), + sLine.getM_Product_ID(), + ma.getM_AttributeSetInstance_ID(), + QtyMA,ma.getDateMaterialPolicy(), + get_TrxName())) + { + String lastError = CLogger.retrieveErrorString(""); + m_processMsg = "Cannot correct Inventory OnHand (MA) [" + product.getValue() + "] - " + lastError; + return DocAction.STATUS_Invalid; + } + + // Create Transaction + mtrx = new MTransaction (getCtx(), sLine.getAD_Org_ID(), + MovementType, sLine.getM_Locator_ID(), + sLine.getM_Product_ID(), ma.getM_AttributeSetInstance_ID(), + QtyMA, getMovementDate(), get_TrxName()); + mtrx.setM_InOutLine_ID(sLine.getM_InOutLine_ID()); + if (!mtrx.save()) + { + m_processMsg = "Could not create Material Transaction (MA) [" + product.getValue() + "]"; + return DocAction.STATUS_Invalid; + } + } + + if (oLine!=null && mtrx!=null && oLine.getQtyOrdered().signum() > 0) + { + if (sLine.getC_OrderLine_ID() != 0) + { + if (!MStorageReservation.add(getCtx(), oLine.getM_Warehouse_ID(), + sLine.getM_Product_ID(), + oLine.getM_AttributeSetInstance_ID(), + orderedQtyToUpdate.negate(), + isSOTrx(), + get_TrxName())) + { + String lastError = CLogger.retrieveErrorString(""); + m_processMsg = "Cannot correct Inventory " + (isSOTrx()? "Reserved" : "Ordered") + " (MA) - [" + product.getValue() + "] - " + lastError; + return DocAction.STATUS_Invalid; + } + } + } + + } + // sLine.getM_AttributeSetInstance_ID() != 0 + if (mtrx == null) + { + Timestamp dateMPolicy= null; + MStorageOnHand[] storages = MStorageOnHand.getWarehouse(getCtx(), 0, + sLine.getM_Product_ID(), sLine.getM_AttributeSetInstance_ID(), null, + MClient.MMPOLICY_FiFo.equals(product.getMMPolicy()), false, + sLine.getM_Locator_ID(), get_TrxName()); + for (MStorageOnHand storage : storages) { + if (storage.getQtyOnHand().compareTo(sLine.getMovementQty()) >= 0) { + dateMPolicy = storage.getDateMaterialPolicy(); + break; + } + } + + if (dateMPolicy == null && storages.length > 0) + dateMPolicy = storages[0].getDateMaterialPolicy(); + + if(dateMPolicy==null) + dateMPolicy = getMovementDate(); + + // Fallback: Update Storage - see also VMatch.createMatchRecord if (!MStorageOnHand.add(getCtx(), getM_Warehouse_ID(), sLine.getM_Locator_ID(), sLine.getM_Product_ID(), - ma.getM_AttributeSetInstance_ID(), - QtyMA,ma.getDateMaterialPolicy(), - get_TrxName())) + sLine.getM_AttributeSetInstance_ID(), + Qty,dateMPolicy,get_TrxName())) { String lastError = CLogger.retrieveErrorString(""); - m_processMsg = "Cannot correct Inventory OnHand (MA) [" + product.getValue() + "] - " + lastError; - return DocAction.STATUS_Invalid; - } - - // Create Transaction - mtrx = new MTransaction (getCtx(), sLine.getAD_Org_ID(), - MovementType, sLine.getM_Locator_ID(), - sLine.getM_Product_ID(), ma.getM_AttributeSetInstance_ID(), - QtyMA, getMovementDate(), get_TrxName()); - mtrx.setM_InOutLine_ID(sLine.getM_InOutLine_ID()); - if (!mtrx.save()) - { - m_processMsg = "Could not create Material Transaction (MA) [" + product.getValue() + "]"; + m_processMsg = "Cannot correct Inventory OnHand [" + product.getValue() + "] - " + lastError; return DocAction.STATUS_Invalid; } - } - - if (oLine!=null && mtrx!=null && oLine.getQtyOrdered().signum() > 0) - { - if (sLine.getC_OrderLine_ID() != 0) + if (oLine!=null && oLine.getQtyOrdered().signum() > 0) { if (!MStorageReservation.add(getCtx(), oLine.getM_Warehouse_ID(), sLine.getM_Product_ID(), oLine.getM_AttributeSetInstance_ID(), - orderedQtyToUpdate.negate(), - isSOTrx(), - get_TrxName())) + orderedQtyToUpdate.negate(), isSOTrx(), get_TrxName())) { - String lastError = CLogger.retrieveErrorString(""); - m_processMsg = "Cannot correct Inventory " + (isSOTrx()? "Reserved" : "Ordered") + " (MA) - [" + product.getValue() + "] - " + lastError; + m_processMsg = "Cannot correct Inventory Reserved " + (isSOTrx()? "Reserved [" :"Ordered [") + product.getValue() + "]"; return DocAction.STATUS_Invalid; } } - } - - } - // sLine.getM_AttributeSetInstance_ID() != 0 - if (mtrx == null) - { - Timestamp dateMPolicy= null; - MStorageOnHand[] storages = MStorageOnHand.getWarehouse(getCtx(), 0, - sLine.getM_Product_ID(), sLine.getM_AttributeSetInstance_ID(), null, - MClient.MMPOLICY_FiFo.equals(product.getMMPolicy()), false, - sLine.getM_Locator_ID(), get_TrxName()); - for (MStorageOnHand storage : storages) { - if (storage.getQtyOnHand().compareTo(sLine.getMovementQty()) >= 0) { - dateMPolicy = storage.getDateMaterialPolicy(); - break; - } - } - - if (dateMPolicy == null && storages.length > 0) - dateMPolicy = storages[0].getDateMaterialPolicy(); - - if(dateMPolicy==null) - dateMPolicy = getMovementDate(); - - // Fallback: Update Storage - see also VMatch.createMatchRecord - if (!MStorageOnHand.add(getCtx(), getM_Warehouse_ID(), - sLine.getM_Locator_ID(), - sLine.getM_Product_ID(), - sLine.getM_AttributeSetInstance_ID(), - Qty,dateMPolicy,get_TrxName())) - { - String lastError = CLogger.retrieveErrorString(""); - m_processMsg = "Cannot correct Inventory OnHand [" + product.getValue() + "] - " + lastError; - return DocAction.STATUS_Invalid; - } - if (oLine!=null && oLine.getQtyOrdered().signum() > 0) - { - if (!MStorageReservation.add(getCtx(), oLine.getM_Warehouse_ID(), - sLine.getM_Product_ID(), - oLine.getM_AttributeSetInstance_ID(), - orderedQtyToUpdate.negate(), isSOTrx(), get_TrxName())) + + // FallBack: Create Transaction + mtrx = new MTransaction (getCtx(), sLine.getAD_Org_ID(), + MovementType, sLine.getM_Locator_ID(), + sLine.getM_Product_ID(), sLine.getM_AttributeSetInstance_ID(), + Qty, getMovementDate(), get_TrxName()); + mtrx.setM_InOutLine_ID(sLine.getM_InOutLine_ID()); + if (!mtrx.save()) { - m_processMsg = "Cannot correct Inventory Reserved " + (isSOTrx()? "Reserved [" :"Ordered [") + product.getValue() + "]"; + m_processMsg = CLogger.retrieveErrorString("Could not create Material Transaction [" + product.getValue() + "]"); return DocAction.STATUS_Invalid; } } - - // FallBack: Create Transaction - mtrx = new MTransaction (getCtx(), sLine.getAD_Org_ID(), - MovementType, sLine.getM_Locator_ID(), - sLine.getM_Product_ID(), sLine.getM_AttributeSetInstance_ID(), - Qty, getMovementDate(), get_TrxName()); - mtrx.setM_InOutLine_ID(sLine.getM_InOutLine_ID()); - if (!mtrx.save()) + } // stock movement + + // Correct Order Line + if (product != null && oLine != null) // other in VMatch.createMatchRecord + { + oLine.setQtyReserved(oLine.getQtyReserved().subtract(sLine.getMovementQty().subtract(sLine.getQtyOverReceipt()))); + } + + // Update Sales Order Line + if (oLine != null) + { + if (isSOTrx() // PO is done by Matching + || sLine.getM_Product_ID() == 0) // PO Charges, empty lines { - m_processMsg = CLogger.retrieveErrorString("Could not create Material Transaction [" + product.getValue() + "]"); + if (isSOTrx()) + oLine.setQtyDelivered(oLine.getQtyDelivered().subtract(Qty)); + else + oLine.setQtyDelivered(oLine.getQtyDelivered().add(Qty)); + oLine.setDateDelivered(getMovementDate()); // overwrite=last + } + if (!oLine.save()) + { + m_processMsg = "Could not update Order Line"; return DocAction.STATUS_Invalid; } - } - } // stock movement - - // Correct Order Line - if (product != null && oLine != null) // other in VMatch.createMatchRecord - { - oLine.setQtyReserved(oLine.getQtyReserved().subtract(sLine.getMovementQty().subtract(sLine.getQtyOverReceipt()))); - } - - // Update Sales Order Line - if (oLine != null) - { - if (isSOTrx() // PO is done by Matching - || sLine.getM_Product_ID() == 0) // PO Charges, empty lines - { - if (isSOTrx()) - oLine.setQtyDelivered(oLine.getQtyDelivered().subtract(Qty)); else - oLine.setQtyDelivered(oLine.getQtyDelivered().add(Qty)); - oLine.setDateDelivered(getMovementDate()); // overwrite=last + if (log.isLoggable(Level.FINE)) log.fine("OrderLine -> Reserved=" + oLine.getQtyReserved() + + ", Delivered=" + oLine.getQtyReserved()); } - if (!oLine.save()) + // Update RMA Line Qty Delivered + else if (rmaLine != null) + { + if (isSOTrx()) + { + rmaLine.setQtyDelivered(rmaLine.getQtyDelivered().add(Qty)); + } + else + { + rmaLine.setQtyDelivered(rmaLine.getQtyDelivered().subtract(Qty)); + } + if (!rmaLine.save()) + { + m_processMsg = "Could not update RMA Line"; + return DocAction.STATUS_Invalid; + } + } + + // Create Asset for SO + if (product != null + && isSOTrx() + && product.isCreateAsset() + && !product.getM_Product_Category().getA_Asset_Group().isFixedAsset() + && sLine.getMovementQty().signum() > 0 + && !isReversal()) { - m_processMsg = "Could not update Order Line"; - return DocAction.STATUS_Invalid; - } - else - if (log.isLoggable(Level.FINE)) log.fine("OrderLine -> Reserved=" + oLine.getQtyReserved() - + ", Delivered=" + oLine.getQtyReserved()); - } - // Update RMA Line Qty Delivered - else if (rmaLine != null) - { - if (isSOTrx()) - { - rmaLine.setQtyDelivered(rmaLine.getQtyDelivered().add(Qty)); - } - else - { - rmaLine.setQtyDelivered(rmaLine.getQtyDelivered().subtract(Qty)); - } - if (!rmaLine.save()) - { - m_processMsg = "Could not update RMA Line"; - return DocAction.STATUS_Invalid; - } - } - - // Create Asset for SO - if (product != null - && isSOTrx() - && product.isCreateAsset() - && !product.getM_Product_Category().getA_Asset_Group().isFixedAsset() - && sLine.getMovementQty().signum() > 0 - && !isReversal()) - { - log.fine("Asset"); - info.append("@A_Asset_ID@: "); - int noAssets = sLine.getMovementQty().intValue(); - if (!product.isOneAssetPerUOM()) - noAssets = 1; - for (int i = 0; i < noAssets; i++) - { - if (i > 0) - info.append(" - "); - int deliveryCount = i+1; + log.fine("Asset"); + info.append("@A_Asset_ID@: "); + int noAssets = sLine.getMovementQty().intValue(); if (!product.isOneAssetPerUOM()) - deliveryCount = 0; - MAsset asset = new MAsset (this, sLine, deliveryCount); - if (!asset.save(get_TrxName())) + noAssets = 1; + for (int i = 0; i < noAssets; i++) { - m_processMsg = "Could not create Asset"; - return DocAction.STATUS_Invalid; - } - info.append(asset.getValue()); - } - } // Asset - - - // Matching - if (!isSOTrx() - && sLine.getM_Product_ID() != 0 - && !isReversal()) - { - BigDecimal matchQty = sLine.getMovementQty(); - // Invoice - Receipt Match (requires Product) - MInvoiceLine iLine = MInvoiceLine.getOfInOutLine (sLine); - if (iLine != null && iLine.getM_Product_ID() != 0) - { - if (matchQty.compareTo(iLine.getQtyInvoiced())>0) - matchQty = iLine.getQtyInvoiced(); - - MMatchInv[] matches = MMatchInv.get(getCtx(), - sLine.getM_InOutLine_ID(), iLine.getC_InvoiceLine_ID(), get_TrxName()); - if (matches == null || matches.length == 0) - { - MMatchInv inv = new MMatchInv (iLine, getMovementDate(), matchQty); - if (sLine.getM_AttributeSetInstance_ID() != iLine.getM_AttributeSetInstance_ID()) + if (i > 0) + info.append(" - "); + int deliveryCount = i+1; + if (!product.isOneAssetPerUOM()) + deliveryCount = 0; + MAsset asset = new MAsset (this, sLine, deliveryCount); + if (!asset.save(get_TrxName())) { - iLine.setM_AttributeSetInstance_ID(sLine.getM_AttributeSetInstance_ID()); - iLine.saveEx(); // update matched invoice with ASI - inv.setM_AttributeSetInstance_ID(sLine.getM_AttributeSetInstance_ID()); - } - if (!inv.save(get_TrxName())) - { - m_processMsg = CLogger.retrieveErrorString("Could not create Inv Matching"); + m_processMsg = "Could not create Asset"; return DocAction.STATUS_Invalid; } - addDocsPostProcess(inv); + info.append(asset.getValue()); } - } - - // Link to Order - if (sLine.getC_OrderLine_ID() != 0) + } // Asset + + + // Matching + if (!isSOTrx() + && sLine.getM_Product_ID() != 0 + && !isReversal()) { - log.fine("PO Matching"); - // Ship - PO - MMatchPO po = MMatchPO.create (null, sLine, getMovementDate(), matchQty); - if (po != null) { - if (!po.save(get_TrxName())) + BigDecimal matchQty = sLine.getMovementQty(); + // Invoice - Receipt Match (requires Product) + MInvoiceLine iLine = MInvoiceLine.getOfInOutLine (sLine); + if (iLine != null && iLine.getM_Product_ID() != 0) + { + if (matchQty.compareTo(iLine.getQtyInvoiced())>0) + matchQty = iLine.getQtyInvoiced(); + + MMatchInv[] matches = MMatchInv.get(getCtx(), + sLine.getM_InOutLine_ID(), iLine.getC_InvoiceLine_ID(), get_TrxName()); + if (matches == null || matches.length == 0) { - m_processMsg = "Could not create PO Matching"; - return DocAction.STATUS_Invalid; - } - if (!po.isPosted()) - addDocsPostProcess(po); - MMatchInv matchInvCreated = po.getMatchInvCreated(); - if (matchInvCreated != null) { - addDocsPostProcess(matchInvCreated); + MMatchInv inv = new MMatchInv (iLine, getMovementDate(), matchQty); + if (sLine.getM_AttributeSetInstance_ID() != iLine.getM_AttributeSetInstance_ID()) + { + iLine.setM_AttributeSetInstance_ID(sLine.getM_AttributeSetInstance_ID()); + iLine.saveEx(); // update matched invoice with ASI + inv.setM_AttributeSetInstance_ID(sLine.getM_AttributeSetInstance_ID()); + } + if (!inv.save(get_TrxName())) + { + m_processMsg = CLogger.retrieveErrorString("Could not create Inv Matching"); + return DocAction.STATUS_Invalid; + } + addDocsPostProcess(inv); } } - // Update PO with ASI - if ( oLine != null && oLine.getM_AttributeSetInstance_ID() == 0 - && sLine.getMovementQty().compareTo(oLine.getQtyOrdered()) == 0) // just if full match [ 1876965 ] + + // Link to Order + if (sLine.getC_OrderLine_ID() != 0) { - oLine.setM_AttributeSetInstance_ID(sLine.getM_AttributeSetInstance_ID()); - oLine.saveEx(get_TrxName()); - } - } - else // No Order - Try finding links via Invoice - { - // Invoice has an Order Link - if (iLine != null && iLine.getC_OrderLine_ID() != 0) - { - // Invoice is created before Shipment - log.fine("PO(Inv) Matching"); - // Ship - Invoice - MMatchPO po = MMatchPO.create (iLine, sLine, - getMovementDate(), matchQty); + log.fine("PO Matching"); + // Ship - PO + MMatchPO po = MMatchPO.create (null, sLine, getMovementDate(), matchQty); if (po != null) { if (!po.save(get_TrxName())) { - m_processMsg = "Could not create PO(Inv) Matching"; + m_processMsg = "Could not create PO Matching"; return DocAction.STATUS_Invalid; } if (!po.isPosted()) addDocsPostProcess(po); + MMatchInv matchInvCreated = po.getMatchInvCreated(); + if (matchInvCreated != null) { + addDocsPostProcess(matchInvCreated); + } } - // Update PO with ASI - oLine = new MOrderLine (getCtx(), iLine.getC_OrderLine_ID(), get_TrxName()); if ( oLine != null && oLine.getM_AttributeSetInstance_ID() == 0 && sLine.getMovementQty().compareTo(oLine.getQtyOrdered()) == 0) // just if full match [ 1876965 ] { @@ -1647,11 +1622,52 @@ public class MInOut extends X_M_InOut implements DocAction oLine.saveEx(get_TrxName()); } } - } // No Order - } // PO Matching - + else // No Order - Try finding links via Invoice + { + // Invoice has an Order Link + if (iLine != null && iLine.getC_OrderLine_ID() != 0) + { + // Invoice is created before Shipment + log.fine("PO(Inv) Matching"); + // Ship - Invoice + MMatchPO po = MMatchPO.create (iLine, sLine, + getMovementDate(), matchQty); + if (po != null) { + if (!po.save(get_TrxName())) + { + m_processMsg = "Could not create PO(Inv) Matching"; + return DocAction.STATUS_Invalid; + } + if (!po.isPosted()) + addDocsPostProcess(po); + } + + // Update PO with ASI + oLine = new MOrderLine (getCtx(), iLine.getC_OrderLine_ID(), get_TrxName()); + if ( oLine != null && oLine.getM_AttributeSetInstance_ID() == 0 + && sLine.getMovementQty().compareTo(oLine.getQtyOrdered()) == 0) // just if full match [ 1876965 ] + { + oLine.setM_AttributeSetInstance_ID(sLine.getM_AttributeSetInstance_ID()); + oLine.saveEx(get_TrxName()); + } + } + } // No Order + } // PO Matching + } + catch (NegativeInventoryDisallowedException e) + { + log.severe(e.getMessage()); + errors.append(Msg.getElement(getCtx(), "Line")).append(" ").append(sLine.getLine()).append(": "); + errors.append(e.getMessage()).append("\n"); + } } // for all lines + if (errors.toString().length() > 0) + { + m_processMsg = errors.toString(); + return DocAction.STATUS_Invalid; + } + // Counter Documents MInOut counter = createCounterDoc(); if (counter != null) diff --git a/org.adempiere.base/src/org/compiere/model/MInventory.java b/org.adempiere.base/src/org/compiere/model/MInventory.java index 2d360d8155..4cd75b68e0 100644 --- a/org.adempiere.base/src/org/compiere/model/MInventory.java +++ b/org.adempiere.base/src/org/compiere/model/MInventory.java @@ -24,6 +24,7 @@ import java.util.List; import java.util.Properties; import java.util.logging.Level; +import org.adempiere.exceptions.NegativeInventoryDisallowedException; import org.adempiere.exceptions.PeriodClosedException; import org.compiere.process.DocAction; import org.compiere.process.DocumentEngine; @@ -435,6 +436,7 @@ public class MInventory extends X_M_Inventory implements DocAction approveIt(); if (log.isLoggable(Level.INFO)) log.info(toString()); + StringBuilder errors = new StringBuilder(); MInventoryLine[] lines = getLines(false); for (MInventoryLine line : lines) { @@ -442,98 +444,159 @@ public class MInventory extends X_M_Inventory implements DocAction continue; MProduct product = line.getProduct(); - - BigDecimal qtyDiff = Env.ZERO; - if (MDocType.DOCSUBTYPEINV_InternalUseInventory.equals(docSubTypeInv)) - qtyDiff = line.getQtyInternalUse().negate(); - else if (MDocType.DOCSUBTYPEINV_PhysicalInventory.equals(docSubTypeInv)) - qtyDiff = line.getQtyCount().subtract(line.getQtyBook()); - else if (MDocType.DOCSUBTYPEINV_CostAdjustment.equals(docSubTypeInv)) + try { - if (!isReversal()) + BigDecimal qtyDiff = Env.ZERO; + if (MDocType.DOCSUBTYPEINV_InternalUseInventory.equals(docSubTypeInv)) + qtyDiff = line.getQtyInternalUse().negate(); + else if (MDocType.DOCSUBTYPEINV_PhysicalInventory.equals(docSubTypeInv)) + qtyDiff = line.getQtyCount().subtract(line.getQtyBook()); + else if (MDocType.DOCSUBTYPEINV_CostAdjustment.equals(docSubTypeInv)) { - BigDecimal currentCost = line.getCurrentCostPrice(); - MClient client = MClient.get(getCtx(), getAD_Client_ID()); - MAcctSchema as = client.getAcctSchema(); - MAcctSchema[] ass = MAcctSchema.getClientAcctSchema(getCtx(), client.get_ID()); - - if (as.getC_Currency_ID() != getC_Currency_ID()) + if (!isReversal()) { - for (int i = 0; i < ass.length ; i ++) + BigDecimal currentCost = line.getCurrentCostPrice(); + MClient client = MClient.get(getCtx(), getAD_Client_ID()); + MAcctSchema as = client.getAcctSchema(); + MAcctSchema[] ass = MAcctSchema.getClientAcctSchema(getCtx(), client.get_ID()); + + if (as.getC_Currency_ID() != getC_Currency_ID()) { - MAcctSchema a = ass[i]; - if (a.getC_Currency_ID() == getC_Currency_ID()) - as = a ; + for (int i = 0; i < ass.length ; i ++) + { + MAcctSchema a = ass[i]; + if (a.getC_Currency_ID() == getC_Currency_ID()) + as = a ; + } + } + + MCost cost = product.getCostingRecord(as, getAD_Org_ID(), line.getM_AttributeSetInstance_ID(), getCostingMethod()); + if (cost != null && cost.getCurrentCostPrice().compareTo(currentCost) != 0) + { + m_processMsg = "Current Cost for Line " + line.getLine() + " have changed."; + return DocAction.STATUS_Invalid; } } - - MCost cost = product.getCostingRecord(as, getAD_Org_ID(), line.getM_AttributeSetInstance_ID(), getCostingMethod()); - if (cost != null && cost.getCurrentCostPrice().compareTo(currentCost) != 0) - { - m_processMsg = "Current Cost for Line " + line.getLine() + " have changed."; - return DocAction.STATUS_Invalid; - } } - } - - //If Quantity Count minus Quantity Book = Zero, then no change in Inventory - if (qtyDiff.signum() == 0) - continue; - - //Ignore the Material Policy when is Reverse Correction - if(!isReversal()){ - BigDecimal qtyOnLineMA = MInventoryLineMA.getManualQty(line.getM_InventoryLine_ID(), get_TrxName()); - - if(qtyDiff.signum()<0){ - if(qtyOnLineMA.compareTo(qtyDiff)<0){ - m_processMsg = "@Over_Qty_On_Attribute_Tab@ " + line.getLine(); - return DOCSTATUS_Invalid; - } - }else{ - if(qtyOnLineMA.compareTo(qtyDiff)>0){ - m_processMsg = "@Over_Qty_On_Attribute_Tab@ " + line.getLine(); - return DOCSTATUS_Invalid; + + //If Quantity Count minus Quantity Book = Zero, then no change in Inventory + if (qtyDiff.signum() == 0) + continue; + + //Ignore the Material Policy when is Reverse Correction + if(!isReversal()){ + BigDecimal qtyOnLineMA = MInventoryLineMA.getManualQty(line.getM_InventoryLine_ID(), get_TrxName()); + + if(qtyDiff.signum()<0){ + if(qtyOnLineMA.compareTo(qtyDiff)<0){ + m_processMsg = "@Over_Qty_On_Attribute_Tab@ " + line.getLine(); + return DOCSTATUS_Invalid; + } + }else{ + if(qtyOnLineMA.compareTo(qtyDiff)>0){ + m_processMsg = "@Over_Qty_On_Attribute_Tab@ " + line.getLine(); + return DOCSTATUS_Invalid; + } } + checkMaterialPolicy(line, qtyDiff.subtract(qtyOnLineMA)); } - checkMaterialPolicy(line, qtyDiff.subtract(qtyOnLineMA)); - } - // 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) + // Stock Movement - Counterpart MOrder.reserveStock + if (product != null + && product.isStocked() ) { - MInventoryLineMA mas[] = MInventoryLineMA.get(getCtx(), - line.getM_InventoryLine_ID(), get_TrxName()); - - for (int j = 0; j < mas.length; j++) + 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 ma = mas[j]; - BigDecimal QtyMA = ma.getMovementQty(); - BigDecimal QtyNew = QtyMA.add(qtyDiff); - if (log.isLoggable(Level.FINE)) log.fine("Diff=" + qtyDiff - + " - Instance OnHand=" + QtyMA + "->" + QtyNew); - + 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); + if (log.isLoggable(Level.FINE)) log.fine("Diff=" + qtyDiff + + " - Instance OnHand=" + QtyMA + "->" + QtyNew); + + if (!MStorageOnHand.add(getCtx(), getM_Warehouse_ID(), + line.getM_Locator_ID(), + line.getM_Product_ID(), + ma.getM_AttributeSetInstance_ID(), + QtyMA.negate(),ma.getDateMaterialPolicy(), get_TrxName())) + { + String lastError = CLogger.retrieveErrorString(""); + m_processMsg = "Cannot correct Inventory (MA) - " + lastError; + return DocAction.STATUS_Invalid; + } + + // Only Update Date Last Inventory if is a Physical Inventory + if (MDocType.DOCSUBTYPEINV_PhysicalInventory.equals(docSubTypeInv)) + { + MStorageOnHand storage = MStorageOnHand.get(getCtx(), line.getM_Locator_ID(), + line.getM_Product_ID(), ma.getM_AttributeSetInstance_ID(),ma.getDateMaterialPolicy(),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) + { + Timestamp dateMPolicy= qtyDiff.signum() > 0 ? getMovementDate() : null; + if (line.getM_AttributeSetInstance_ID() > 0) + { + Timestamp t = MStorageOnHand.getDateMaterialPolicy(line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), line.get_TrxName()); + if (t != null) + dateMPolicy = t; + } + + //Fallback: Update Storage - see also VMatch.createMatchRecord if (!MStorageOnHand.add(getCtx(), getM_Warehouse_ID(), line.getM_Locator_ID(), line.getM_Product_ID(), - ma.getM_AttributeSetInstance_ID(), - QtyMA.negate(),ma.getDateMaterialPolicy(), get_TrxName())) + line.getM_AttributeSetInstance_ID(), + qtyDiff,dateMPolicy,get_TrxName())) { String lastError = CLogger.retrieveErrorString(""); - m_processMsg = "Cannot correct Inventory (MA) - " + lastError; + m_processMsg = "Cannot correct Inventory OnHand (MA) - " + lastError; return DocAction.STATUS_Invalid; } - + // Only Update Date Last Inventory if is a Physical Inventory if (MDocType.DOCSUBTYPEINV_PhysicalInventory.equals(docSubTypeInv)) { MStorageOnHand storage = MStorageOnHand.get(getCtx(), line.getM_Locator_ID(), - line.getM_Product_ID(), ma.getM_AttributeSetInstance_ID(),ma.getDateMaterialPolicy(),get_TrxName()); + line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(),dateMPolicy, get_TrxName()); + storage.setDateLastInventory(getMovementDate()); if (!storage.save(get_TrxName())) { @@ -541,87 +604,40 @@ public class MInventory extends X_M_Inventory implements DocAction return DocAction.STATUS_Invalid; } } - - String m_MovementType =null; - if(QtyMA.negate().compareTo(Env.ZERO) > 0 ) + + 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(), 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) - { - Timestamp dateMPolicy= qtyDiff.signum() > 0 ? getMovementDate() : null; - if (line.getM_AttributeSetInstance_ID() > 0) - { - Timestamp t = MStorageOnHand.getDateMaterialPolicy(line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), line.get_TrxName()); - if (t != null) - dateMPolicy = t; - } - - //Fallback: Update Storage - see also VMatch.createMatchRecord - if (!MStorageOnHand.add(getCtx(), getM_Warehouse_ID(), - line.getM_Locator_ID(), - line.getM_Product_ID(), - line.getM_AttributeSetInstance_ID(), - qtyDiff,dateMPolicy,get_TrxName())) - { - String lastError = CLogger.retrieveErrorString(""); - m_processMsg = "Cannot correct Inventory OnHand (MA) - " + lastError; - return DocAction.STATUS_Invalid; - } - - // Only Update Date Last Inventory if is a Physical Inventory - if (MDocType.DOCSUBTYPEINV_PhysicalInventory.equals(docSubTypeInv)) - { - MStorageOnHand storage = MStorageOnHand.get(getCtx(), line.getM_Locator_ID(), - line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(),dateMPolicy, get_TrxName()); - - storage.setDateLastInventory(getMovementDate()); - if (!storage.save(get_TrxName())) + 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 = "Storage not updated(2)"; + m_processMsg = "Transaction not inserted(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 + } + } // Fallback + } // stock movement + } + catch (NegativeInventoryDisallowedException e) + { + log.severe(e.getMessage()); + errors.append(Msg.getElement(getCtx(), "Line")).append(" ").append(line.getLine()).append(": "); + errors.append(e.getMessage()).append("\n"); + } } // for all lines + if (errors.toString().length() > 0) + { + m_processMsg = errors.toString(); + return DocAction.STATUS_Invalid; + } + // User Validation String valid = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_AFTER_COMPLETE); if (valid != null) diff --git a/org.adempiere.base/src/org/compiere/model/MMovement.java b/org.adempiere.base/src/org/compiere/model/MMovement.java index a56cfd7268..d84c0d717f 100644 --- a/org.adempiere.base/src/org/compiere/model/MMovement.java +++ b/org.adempiere.base/src/org/compiere/model/MMovement.java @@ -24,6 +24,7 @@ import java.util.List; import java.util.Properties; import java.util.logging.Level; +import org.adempiere.exceptions.NegativeInventoryDisallowedException; import org.adempiere.exceptions.PeriodClosedException; import org.compiere.process.DocAction; import org.compiere.process.DocumentEngine; @@ -416,6 +417,7 @@ public class MMovement extends X_M_Movement implements DocAction approveIt(); if (log.isLoggable(Level.INFO)) log.info(toString()); + StringBuilder errors = new StringBuilder(); // MMovementLine[] lines = getLines(false); for (int i = 0; i < lines.length; i++) @@ -425,174 +427,190 @@ public class MMovement extends X_M_Movement implements DocAction //Stock Movement - Counterpart MOrder.reserveStock MProduct product = line.getProduct(); - if (product != null - && product.isStocked() ) + try { - //Ignore the Material Policy when is Reverse Correction - if(!isReversal()){ - BigDecimal qtyOnLineMA = MMovementLineMA.getManualQty(line.getM_MovementLine_ID(), get_TrxName()); - BigDecimal movementQty = line.getMovementQty(); - - if(qtyOnLineMA.compareTo(movementQty)>0) - { - // More then line qty on attribute tab for line 10 - m_processMsg = "@Over_Qty_On_Attribute_Tab@ " + line.getLine(); - return DOCSTATUS_Invalid; - } - - checkMaterialPolicy(line,movementQty.subtract(qtyOnLineMA)); - } - - - if (line.getM_AttributeSetInstance_ID() == 0) + if (product != null + && product.isStocked() ) { - MMovementLineMA mas[] = MMovementLineMA.get(getCtx(), - line.getM_MovementLine_ID(), get_TrxName()); - for (int j = 0; j < mas.length; j++) + //Ignore the Material Policy when is Reverse Correction + if(!isReversal()){ + BigDecimal qtyOnLineMA = MMovementLineMA.getManualQty(line.getM_MovementLine_ID(), get_TrxName()); + BigDecimal movementQty = line.getMovementQty(); + + if(qtyOnLineMA.compareTo(movementQty)>0) + { + // More then line qty on attribute tab for line 10 + m_processMsg = "@Over_Qty_On_Attribute_Tab@ " + line.getLine(); + return DOCSTATUS_Invalid; + } + + checkMaterialPolicy(line,movementQty.subtract(qtyOnLineMA)); + } + + + if (line.getM_AttributeSetInstance_ID() == 0) { - MMovementLineMA ma = mas[j]; - // + MMovementLineMA mas[] = MMovementLineMA.get(getCtx(), + line.getM_MovementLine_ID(), get_TrxName()); + for (int j = 0; j < mas.length; j++) + { + MMovementLineMA ma = mas[j]; + // + MLocator locator = new MLocator (getCtx(), line.getM_Locator_ID(), get_TrxName()); + //Update Storage + if (!MStorageOnHand.add(getCtx(),locator.getM_Warehouse_ID(), + line.getM_Locator_ID(), + line.getM_Product_ID(), + ma.getM_AttributeSetInstance_ID(), + ma.getMovementQty().negate(),ma.getDateMaterialPolicy(), get_TrxName())) + { + String lastError = CLogger.retrieveErrorString(""); + m_processMsg = "Cannot correct Inventory OnHand (MA) - " + lastError; + return DocAction.STATUS_Invalid; + } + + int M_AttributeSetInstanceTo_ID = line.getM_AttributeSetInstanceTo_ID(); + //only can be same asi if locator is different + if (M_AttributeSetInstanceTo_ID == 0 && line.getM_Locator_ID() != line.getM_LocatorTo_ID()) + { + M_AttributeSetInstanceTo_ID = ma.getM_AttributeSetInstance_ID(); + } + //Update Storage + MLocator locatorTo = new MLocator (getCtx(), line.getM_LocatorTo_ID(), get_TrxName()); + if (!MStorageOnHand.add(getCtx(),locatorTo.getM_Warehouse_ID(), + line.getM_LocatorTo_ID(), + line.getM_Product_ID(), + M_AttributeSetInstanceTo_ID, + ma.getMovementQty(),ma.getDateMaterialPolicy(), get_TrxName())) + { + String lastError = CLogger.retrieveErrorString(""); + m_processMsg = "Cannot correct Inventory OnHand (MA) - " + lastError; + return DocAction.STATUS_Invalid; + } + + // + trxFrom = new MTransaction (getCtx(), line.getAD_Org_ID(), + MTransaction.MOVEMENTTYPE_MovementFrom, + line.getM_Locator_ID(), line.getM_Product_ID(), ma.getM_AttributeSetInstance_ID(), + ma.getMovementQty().negate(), getMovementDate(), get_TrxName()); + trxFrom.setM_MovementLine_ID(line.getM_MovementLine_ID()); + if (!trxFrom.save()) + { + m_processMsg = "Transaction From not inserted (MA)"; + return DocAction.STATUS_Invalid; + } + // + MTransaction trxTo = new MTransaction (getCtx(), line.getAD_Org_ID(), + MTransaction.MOVEMENTTYPE_MovementTo, + line.getM_LocatorTo_ID(), line.getM_Product_ID(), M_AttributeSetInstanceTo_ID, + ma.getMovementQty(), getMovementDate(), get_TrxName()); + trxTo.setM_MovementLine_ID(line.getM_MovementLine_ID()); + if (!trxTo.save()) + { + m_processMsg = "Transaction To not inserted (MA)"; + return DocAction.STATUS_Invalid; + } + } + } + // Fallback - We have ASI + if (trxFrom == null) + { + Timestamp dateMPolicy= null; + MStorageOnHand[] storages = null; + if (line.getMovementQty().compareTo(Env.ZERO) > 0) { + // Find Date Material Policy bases on ASI + storages = MStorageOnHand.getWarehouse(getCtx(), 0, + line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), null, + MClient.MMPOLICY_FiFo.equals(product.getMMPolicy()), false, + line.getM_Locator_ID(), get_TrxName()); + } else { + //Case of reversal + storages = MStorageOnHand.getWarehouse(getCtx(), 0, + line.getM_Product_ID(), line.getM_AttributeSetInstanceTo_ID(), null, + MClient.MMPOLICY_FiFo.equals(product.getMMPolicy()), false, + line.getM_LocatorTo_ID(), get_TrxName()); + } + for (MStorageOnHand storage : storages) { + if (storage.getQtyOnHand().compareTo(line.getMovementQty()) >= 0) { + dateMPolicy = storage.getDateMaterialPolicy(); + break; + } + } + + if (dateMPolicy == null && storages.length > 0) + dateMPolicy = storages[0].getDateMaterialPolicy(); + MLocator locator = new MLocator (getCtx(), line.getM_Locator_ID(), get_TrxName()); //Update Storage + Timestamp effDateMPolicy = dateMPolicy; + if (dateMPolicy == null && line.getMovementQty().negate().signum() > 0) + effDateMPolicy = getMovementDate(); if (!MStorageOnHand.add(getCtx(),locator.getM_Warehouse_ID(), line.getM_Locator_ID(), line.getM_Product_ID(), - ma.getM_AttributeSetInstance_ID(), - ma.getMovementQty().negate(),ma.getDateMaterialPolicy(), get_TrxName())) + line.getM_AttributeSetInstance_ID(), + line.getMovementQty().negate(),effDateMPolicy, get_TrxName())) { String lastError = CLogger.retrieveErrorString(""); m_processMsg = "Cannot correct Inventory OnHand (MA) - " + lastError; return DocAction.STATUS_Invalid; } - - int M_AttributeSetInstanceTo_ID = line.getM_AttributeSetInstanceTo_ID(); - //only can be same asi if locator is different - if (M_AttributeSetInstanceTo_ID == 0 && line.getM_Locator_ID() != line.getM_LocatorTo_ID()) - { - M_AttributeSetInstanceTo_ID = ma.getM_AttributeSetInstance_ID(); - } - //Update Storage + + //Update Storage + effDateMPolicy = dateMPolicy; + if (dateMPolicy == null && line.getMovementQty().signum() > 0) + effDateMPolicy = getMovementDate(); MLocator locatorTo = new MLocator (getCtx(), line.getM_LocatorTo_ID(), get_TrxName()); if (!MStorageOnHand.add(getCtx(),locatorTo.getM_Warehouse_ID(), line.getM_LocatorTo_ID(), line.getM_Product_ID(), - M_AttributeSetInstanceTo_ID, - ma.getMovementQty(),ma.getDateMaterialPolicy(), get_TrxName())) + line.getM_AttributeSetInstanceTo_ID(), + line.getMovementQty(),effDateMPolicy, get_TrxName())) { String lastError = CLogger.retrieveErrorString(""); m_processMsg = "Cannot correct Inventory OnHand (MA) - " + lastError; return DocAction.STATUS_Invalid; } - + // trxFrom = new MTransaction (getCtx(), line.getAD_Org_ID(), MTransaction.MOVEMENTTYPE_MovementFrom, - line.getM_Locator_ID(), line.getM_Product_ID(), ma.getM_AttributeSetInstance_ID(), - ma.getMovementQty().negate(), getMovementDate(), get_TrxName()); + line.getM_Locator_ID(), line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), + line.getMovementQty().negate(), getMovementDate(), get_TrxName()); trxFrom.setM_MovementLine_ID(line.getM_MovementLine_ID()); if (!trxFrom.save()) { - m_processMsg = "Transaction From not inserted (MA)"; + m_processMsg = "Transaction From not inserted"; return DocAction.STATUS_Invalid; } // MTransaction trxTo = new MTransaction (getCtx(), line.getAD_Org_ID(), MTransaction.MOVEMENTTYPE_MovementTo, - line.getM_LocatorTo_ID(), line.getM_Product_ID(), M_AttributeSetInstanceTo_ID, - ma.getMovementQty(), getMovementDate(), get_TrxName()); + line.getM_LocatorTo_ID(), line.getM_Product_ID(), line.getM_AttributeSetInstanceTo_ID(), + line.getMovementQty(), getMovementDate(), get_TrxName()); trxTo.setM_MovementLine_ID(line.getM_MovementLine_ID()); if (!trxTo.save()) { - m_processMsg = "Transaction To not inserted (MA)"; + m_processMsg = "Transaction To not inserted"; return DocAction.STATUS_Invalid; } - } - } - // Fallback - We have ASI - if (trxFrom == null) - { - Timestamp dateMPolicy= null; - MStorageOnHand[] storages = null; - if (line.getMovementQty().compareTo(Env.ZERO) > 0) { - // Find Date Material Policy bases on ASI - storages = MStorageOnHand.getWarehouse(getCtx(), 0, - line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), null, - MClient.MMPOLICY_FiFo.equals(product.getMMPolicy()), false, - line.getM_Locator_ID(), get_TrxName()); - } else { - //Case of reversal - storages = MStorageOnHand.getWarehouse(getCtx(), 0, - line.getM_Product_ID(), line.getM_AttributeSetInstanceTo_ID(), null, - MClient.MMPOLICY_FiFo.equals(product.getMMPolicy()), false, - line.getM_LocatorTo_ID(), get_TrxName()); - } - for (MStorageOnHand storage : storages) { - if (storage.getQtyOnHand().compareTo(line.getMovementQty()) >= 0) { - dateMPolicy = storage.getDateMaterialPolicy(); - break; - } - } - - if (dateMPolicy == null && storages.length > 0) - dateMPolicy = storages[0].getDateMaterialPolicy(); - - MLocator locator = new MLocator (getCtx(), line.getM_Locator_ID(), get_TrxName()); - //Update Storage - Timestamp effDateMPolicy = dateMPolicy; - if (dateMPolicy == null && line.getMovementQty().negate().signum() > 0) - effDateMPolicy = getMovementDate(); - if (!MStorageOnHand.add(getCtx(),locator.getM_Warehouse_ID(), - line.getM_Locator_ID(), - line.getM_Product_ID(), - line.getM_AttributeSetInstance_ID(), - line.getMovementQty().negate(),effDateMPolicy, get_TrxName())) - { - String lastError = CLogger.retrieveErrorString(""); - m_processMsg = "Cannot correct Inventory OnHand (MA) - " + lastError; - return DocAction.STATUS_Invalid; - } - - //Update Storage - effDateMPolicy = dateMPolicy; - if (dateMPolicy == null && line.getMovementQty().signum() > 0) - effDateMPolicy = getMovementDate(); - MLocator locatorTo = new MLocator (getCtx(), line.getM_LocatorTo_ID(), get_TrxName()); - if (!MStorageOnHand.add(getCtx(),locatorTo.getM_Warehouse_ID(), - line.getM_LocatorTo_ID(), - line.getM_Product_ID(), - line.getM_AttributeSetInstanceTo_ID(), - line.getMovementQty(),effDateMPolicy, get_TrxName())) - { - String lastError = CLogger.retrieveErrorString(""); - m_processMsg = "Cannot correct Inventory OnHand (MA) - " + lastError; - return DocAction.STATUS_Invalid; - } - - // - trxFrom = new MTransaction (getCtx(), line.getAD_Org_ID(), - MTransaction.MOVEMENTTYPE_MovementFrom, - line.getM_Locator_ID(), line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), - line.getMovementQty().negate(), getMovementDate(), get_TrxName()); - trxFrom.setM_MovementLine_ID(line.getM_MovementLine_ID()); - if (!trxFrom.save()) - { - m_processMsg = "Transaction From not inserted"; - return DocAction.STATUS_Invalid; - } - // - MTransaction trxTo = new MTransaction (getCtx(), line.getAD_Org_ID(), - MTransaction.MOVEMENTTYPE_MovementTo, - line.getM_LocatorTo_ID(), line.getM_Product_ID(), line.getM_AttributeSetInstanceTo_ID(), - line.getMovementQty(), getMovementDate(), get_TrxName()); - trxTo.setM_MovementLine_ID(line.getM_MovementLine_ID()); - if (!trxTo.save()) - { - m_processMsg = "Transaction To not inserted"; - return DocAction.STATUS_Invalid; - } - } // Fallback - } // product stock + } // Fallback + } // product stock + } + catch (NegativeInventoryDisallowedException e) + { + log.severe(e.getMessage()); + errors.append(Msg.getElement(getCtx(), "Line")).append(" ").append(line.getLine()).append(": "); + errors.append(e.getMessage()).append("\n"); + } } // for all lines + + if (errors.toString().length() > 0) + { + m_processMsg = errors.toString(); + return DocAction.STATUS_Invalid; + } + // User Validation String valid = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_AFTER_COMPLETE); if (valid != null) diff --git a/org.adempiere.base/src/org/compiere/model/MProjectIssue.java b/org.adempiere.base/src/org/compiere/model/MProjectIssue.java index 729a8c122d..0d84a4ee9e 100644 --- a/org.adempiere.base/src/org/compiere/model/MProjectIssue.java +++ b/org.adempiere.base/src/org/compiere/model/MProjectIssue.java @@ -22,8 +22,11 @@ import java.sql.Timestamp; import java.util.Properties; import java.util.logging.Level; +import org.adempiere.exceptions.AdempiereException; +import org.adempiere.exceptions.NegativeInventoryDisallowedException; import org.compiere.util.DB; import org.compiere.util.Env; +import org.compiere.util.Msg; /** * Project Issue Model @@ -172,23 +175,35 @@ public class MProjectIssue extends X_C_ProjectIssue dateMPolicy = t; } - if (MStorageOnHand.add(getCtx(), loc.getM_Warehouse_ID(), getM_Locator_ID(), - getM_Product_ID(), getM_AttributeSetInstance_ID(), - getMovementQty().negate(),dateMPolicy, get_TrxName())) + try { - if (mTrx.save(get_TrxName())) + if (MStorageOnHand.add(getCtx(), loc.getM_Warehouse_ID(), getM_Locator_ID(), + getM_Product_ID(), getM_AttributeSetInstance_ID(), + getMovementQty().negate(),dateMPolicy, get_TrxName())) { - setProcessed (true); - if (save()) - return true; + if (mTrx.save(get_TrxName())) + { + setProcessed (true); + if (save()) + return true; + else + log.log(Level.SEVERE, "Issue not saved"); // requires trx !! + } else - log.log(Level.SEVERE, "Issue not saved"); // requires trx !! + log.log(Level.SEVERE, "Transaction not saved"); // requires trx !! } else - log.log(Level.SEVERE, "Transaction not saved"); // requires trx !! + log.log(Level.SEVERE, "Storage not updated"); // OK } - else - log.log(Level.SEVERE, "Storage not updated"); // OK + catch (NegativeInventoryDisallowedException e) + { + log.severe(e.getMessage()); + StringBuilder error = new StringBuilder(); + error.append(Msg.getElement(getCtx(), "Line")).append(" ").append(getLine()).append(": "); + error.append(e.getMessage()).append("\n"); + throw new AdempiereException(error.toString()); + } + // return false; } // process diff --git a/org.adempiere.base/src/org/compiere/model/MStorageOnHand.java b/org.adempiere.base/src/org/compiere/model/MStorageOnHand.java index c20f5284d2..677522cfe4 100644 --- a/org.adempiere.base/src/org/compiere/model/MStorageOnHand.java +++ b/org.adempiere.base/src/org/compiere/model/MStorageOnHand.java @@ -27,11 +27,10 @@ import java.util.List; import java.util.Properties; import java.util.logging.Level; -import org.adempiere.exceptions.AdempiereException; +import org.adempiere.exceptions.NegativeInventoryDisallowedException; import org.compiere.util.CLogger; import org.compiere.util.DB; import org.compiere.util.Env; -import org.compiere.util.Msg; import org.compiere.util.Util; /** @@ -735,7 +734,8 @@ public class MStorageOnHand extends X_M_StorageOnHand if (getQtyOnHand().signum() == -1) { MWarehouse wh = MWarehouse.get(Env.getCtx(), getM_Warehouse_ID()); if (wh.isDisallowNegativeInv()) { - throw new AdempiereException(Msg.getMsg(Env.getCtx(), "NegativeInventoryDisallowed")); + throw new NegativeInventoryDisallowedException(getCtx(), getM_Warehouse_ID(), getM_Product_ID(), getM_AttributeSetInstance_ID(), getM_Locator_ID(), + getQtyOnHand().subtract(addition), addition.negate()); } } } @@ -899,13 +899,15 @@ public class MStorageOnHand extends X_M_StorageOnHand if (getQtyOnHand().compareTo(BigDecimal.ZERO) < 0 || QtyOnHand.compareTo(Env.ZERO) < 0) { - log.saveError("Error", Msg.getMsg(getCtx(), "NegativeInventoryDisallowed")); + log.saveError("Error", new NegativeInventoryDisallowedException(getCtx(), getM_Warehouse_ID(), getM_Product_ID(), + getM_AttributeSetInstance_ID(), getM_Locator_ID(), QtyOnHand.subtract(getQtyOnHand()), getQtyOnHand().negate())); return false; } if (getM_AttributeSetInstance_ID() > 0 && getQtyOnHand().signum() < 0) { - log.saveError("Error", Msg.getMsg(getCtx(), "NegativeInventoryDisallowed")); + log.saveError("Error", new NegativeInventoryDisallowedException(getCtx(), getM_Warehouse_ID(), getM_Product_ID(), + getM_AttributeSetInstance_ID(), getM_Locator_ID(), QtyOnHand.subtract(getQtyOnHand()), getQtyOnHand().negate())); return false; } } diff --git a/org.adempiere.base/src/org/eevolution/model/MDDOrder.java b/org.adempiere.base/src/org/eevolution/model/MDDOrder.java index abc0e43dd1..ef01e324ba 100644 --- a/org.adempiere.base/src/org/eevolution/model/MDDOrder.java +++ b/org.adempiere.base/src/org/eevolution/model/MDDOrder.java @@ -27,6 +27,7 @@ import java.util.Properties; import java.util.logging.Level; import org.adempiere.exceptions.AdempiereException; +import org.adempiere.exceptions.NegativeInventoryDisallowedException; import org.compiere.model.MBPartner; import org.compiere.model.MBPartnerLocation; import org.compiere.model.MDocType; @@ -847,6 +848,7 @@ public class MDDOrder extends X_DD_Order implements DocAction BigDecimal Volume = Env.ZERO; BigDecimal Weight = Env.ZERO; + StringBuilder errors = new StringBuilder(); // Always check and (un) Reserve Inventory for (MDDOrderLine line : lines) { @@ -874,35 +876,47 @@ public class MDDOrder extends X_DD_Order implements DocAction MProduct product = line.getProduct(); if (product != null) { - if (product.isStocked()) + try { - // Update Storage - if (!MStorageOnHand.add(getCtx(), locator_to.getM_Warehouse_ID(), locator_to.getM_Locator_ID(), - line.getM_Product_ID(), - line.getM_AttributeSetInstance_ID(), - Env.ZERO,null, get_TrxName())) + if (product.isStocked()) { - throw new AdempiereException(); - } - - if (!MStorageOnHand.add(getCtx(), locator_from.getM_Warehouse_ID(), locator_from.getM_Locator_ID(), - line.getM_Product_ID(), - line.getM_AttributeSetInstanceTo_ID(), - Env.ZERO,null, get_TrxName())) - { - throw new AdempiereException(); - } - - } // stockec - // update line - line.setQtyReserved(line.getQtyReserved().add(reserved_ordered)); - line.saveEx(); - // - Volume = Volume.add(product.getVolume().multiply(line.getQtyOrdered())); - Weight = Weight.add(product.getWeight().multiply(line.getQtyOrdered())); + // Update Storage + if (!MStorageOnHand.add(getCtx(), locator_to.getM_Warehouse_ID(), locator_to.getM_Locator_ID(), + line.getM_Product_ID(), + line.getM_AttributeSetInstance_ID(), + Env.ZERO,null, get_TrxName())) + { + throw new AdempiereException(); + } + + if (!MStorageOnHand.add(getCtx(), locator_from.getM_Warehouse_ID(), locator_from.getM_Locator_ID(), + line.getM_Product_ID(), + line.getM_AttributeSetInstanceTo_ID(), + Env.ZERO,null, get_TrxName())) + { + throw new AdempiereException(); + } + + } // stockec + // update line + line.setQtyReserved(line.getQtyReserved().add(reserved_ordered)); + line.saveEx(); + // + Volume = Volume.add(product.getVolume().multiply(line.getQtyOrdered())); + Weight = Weight.add(product.getWeight().multiply(line.getQtyOrdered())); + } + catch (NegativeInventoryDisallowedException e) + { + log.severe(e.getMessage()); + errors.append(Msg.getElement(getCtx(), "Line")).append(" ").append(line.getLine()).append(": "); + errors.append(e.getMessage()).append("\n"); + } } // product } // reverse inventory + if (errors.toString().length() > 0) + throw new AdempiereException(errors.toString()); + setVolume(Volume); setWeight(Weight); } // reserveStock