IDEMPIERE-385 Resolve M_Storage locking and data consistency / Code refinements

This commit is contained in:
Carlos Ruiz 2012-11-20 17:30:44 -05:00
parent d74f7933ba
commit c93620b3c9
24 changed files with 414 additions and 216 deletions

View File

@ -118,15 +118,14 @@ public class CalloutMovement extends CalloutEngine
if (MovementQty == null) if (MovementQty == null)
MovementQty = (BigDecimal) mTab.getValue("MovementQty"); MovementQty = (BigDecimal) mTab.getValue("MovementQty");
int M_Locator_ID = Env.getContextAsInt(ctx, WindowNo, "M_Locator_ID"); int M_Locator_ID = Env.getContextAsInt(ctx, WindowNo, "M_Locator_ID");
// If no locator, don't check anything and assume is ok // If no locator, don't check anything and assume is ok
if (M_Locator_ID <= 0) if (M_Locator_ID <= 0)
return; return;
//@win - IDEMPIERE-385
int M_Warehouse_ID = DB.getSQLValue(null, "SELECT M_Warehouse_ID FROM M_Locator WHERE M_Locator_ID=?", M_Locator_ID);
//
int M_AttributeSetInstance_ID = Env.getContextAsInt(ctx, WindowNo, "M_AttributeSetInstance_ID"); int M_AttributeSetInstance_ID = Env.getContextAsInt(ctx, WindowNo, "M_AttributeSetInstance_ID");
BigDecimal available = MStorageReservation.getQtyAvailable(M_Warehouse_ID, M_Product_ID, M_AttributeSetInstance_ID, null); BigDecimal available = Env.ZERO;
MStorageOnHand oh = MStorageOnHand.get(ctx, M_Locator_ID, M_Product_ID, M_AttributeSetInstance_ID, null);
if (oh != null)
available = oh.getQtyOnHand();
if (available == null) if (available == null)
available = Env.ZERO; available = Env.ZERO;
if (available.signum() == 0) if (available.signum() == 0)

View File

@ -150,6 +150,16 @@ public class OrgOwnership extends SvrProcess
no = DB.executeUpdate(sql.toString(), get_TrxName()); no = DB.executeUpdate(sql.toString(), get_TrxName());
addLog (0,null, new BigDecimal(no), Msg.translate(getCtx(), "Storage")); addLog (0,null, new BigDecimal(no), Msg.translate(getCtx(), "Storage"));
// Set Storage Reservation
sql = new StringBuilder();
sql.append("UPDATE M_StorageReservation s ")
.append("SET AD_Org_ID=").append(p_AD_Org_ID)
.append(" WHERE M_Warehouse_ID=").append(p_M_Warehouse_ID)
.append(" AND AD_Client_ID=").append(getAD_Client_ID())
.append(" AND AD_Org_ID<>").append(p_AD_Org_ID);
no = DB.executeUpdate(sql.toString(), get_TrxName());
addLog (0,null, new BigDecimal(no), Msg.translate(getCtx(), "StorageReservation"));
return ""; return "";
} // warehouseOwnership } // warehouseOwnership

View File

@ -251,10 +251,10 @@ public class ReplenishReport extends SvrProcess
sql = new StringBuilder("UPDATE T_Replenish t SET "); sql = new StringBuilder("UPDATE T_Replenish t SET ");
sql.append("QtyOnHand = (SELECT COALESCE(SUM(QtyOnHand),0) FROM M_StorageOnHand s, M_Locator l WHERE t.M_Product_ID=s.M_Product_ID"); sql.append("QtyOnHand = (SELECT COALESCE(SUM(QtyOnHand),0) FROM M_StorageOnHand s, M_Locator l WHERE t.M_Product_ID=s.M_Product_ID");
sql.append(" AND l.M_Locator_ID=s.M_Locator_ID AND l.M_Warehouse_ID=t.M_Warehouse_ID),"); sql.append(" AND l.M_Locator_ID=s.M_Locator_ID AND l.M_Warehouse_ID=t.M_Warehouse_ID),");
sql.append("QtyReserved = (SELECT COALESCE(SUM(QtyReserved),0) FROM M_StorageReservation s, M_Locator l WHERE t.M_Product_ID=s.M_Product_ID"); sql.append("QtyReserved = (SELECT COALESCE(SUM(Qty),0) FROM M_StorageReservation s WHERE t.M_Product_ID=s.M_Product_ID");
sql.append(" AND l.M_Warehouse_ID=t.M_Warehouse_ID),"); sql.append(" AND t.M_Warehouse_ID=s.M_Warehouse_ID),");
sql.append("QtyOrdered = (SELECT COALESCE(SUM(QtyOrdered),0) FROM M_StorageReservation s, M_Locator l WHERE t.M_Product_ID=s.M_Product_ID"); sql.append("QtyOrdered = (SELECT COALESCE(SUM(Qty),0) FROM M_StorageReservation s WHERE t.M_Product_ID=s.M_Product_ID");
sql.append(" AND l.M_Warehouse_ID=t.M_Warehouse_ID)"); sql.append(" AND t.M_Warehouse_ID=s.M_Warehouse_ID)");
if (p_C_DocType_ID != 0) if (p_C_DocType_ID != 0)
sql.append(", C_DocType_ID=").append(p_C_DocType_ID); sql.append(", C_DocType_ID=").append(p_C_DocType_ID);
sql.append(" WHERE AD_PInstance_ID=").append(getAD_PInstance_ID()); sql.append(" WHERE AD_PInstance_ID=").append(getAD_PInstance_ID());
@ -535,9 +535,8 @@ public class ReplenishReport extends SvrProcess
MProduct product = MProduct.get(getCtx(), replenish.getM_Product_ID()); MProduct product = MProduct.get(getCtx(), replenish.getM_Product_ID());
String MMPolicy = product.getMMPolicy(); String MMPolicy = product.getMMPolicy();
MStorageOnHand[] storages = MStorageOnHand.getWarehouse(getCtx(), MStorageOnHand[] storages = MStorageOnHand.getWarehouse(getCtx(),
whSource.getM_Warehouse_ID(), replenish.getM_Product_ID(), 0, 0, whSource.getM_Warehouse_ID(), replenish.getM_Product_ID(), 0, null,
true, null, MClient.MMPOLICY_FiFo.equals(MMPolicy), false, 0, get_TrxName());
MClient.MMPOLICY_FiFo.equals(MMPolicy), get_TrxName());
// //
BigDecimal target = replenish.getQtyToOrder(); BigDecimal target = replenish.getQtyToOrder();
for (int j = 0; j < storages.length; j++) for (int j = 0; j < storages.length; j++)

View File

@ -269,12 +269,12 @@ public class ReplenishReportProduction extends SvrProcess
} }
sql = new StringBuilder("UPDATE T_Replenish t SET "); sql = new StringBuilder("UPDATE T_Replenish t SET ");
sql.append("QtyOnHand = (SELECT COALESCE(SUM(QtyOnHand),0) FROM M_Storage s, M_Locator l WHERE t.M_Product_ID=s.M_Product_ID"); sql.append("QtyOnHand = (SELECT COALESCE(SUM(QtyOnHand),0) FROM M_StorageOnHand s, M_Locator l WHERE t.M_Product_ID=s.M_Product_ID");
sql.append(" AND l.M_Locator_ID=s.M_Locator_ID AND l.M_Warehouse_ID=t.M_Warehouse_ID),"); sql.append(" AND l.M_Locator_ID=s.M_Locator_ID AND l.M_Warehouse_ID=t.M_Warehouse_ID),");
sql.append("QtyReserved = (SELECT COALESCE(SUM(QtyReserved),0) FROM M_StorageReservation s, M_Locator l WHERE t.M_Product_ID=s.M_Product_ID"); sql.append("QtyReserved = (SELECT COALESCE(SUM(Qty),0) FROM M_StorageReservation s WHERE t.M_Product_ID=s.M_Product_ID");
sql.append(" AND l.M_Warehouse_ID=t.M_Warehouse_ID),"); sql.append(" AND t.M_Warehouse_ID=s.M_Warehouse_ID),");
sql.append("QtyOrdered = (SELECT COALESCE(SUM(QtyOrdered),0) FROM M_StorageReservation s, M_Locator l WHERE t.M_Product_ID=s.M_Product_ID"); sql.append("QtyOrdered = (SELECT COALESCE(SUM(Qty),0) FROM M_StorageReservation s WHERE t.M_Product_ID=s.M_Product_ID");
sql.append(" AND l.M_Warehouse_ID=t.M_Warehouse_ID)"); sql.append(" AND t.M_Warehouse_ID=s.M_Warehouse_ID)");
if (p_C_DocType_ID != 0) if (p_C_DocType_ID != 0)
sql.append(", C_DocType_ID=").append(p_C_DocType_ID); sql.append(", C_DocType_ID=").append(p_C_DocType_ID);
sql.append(" WHERE AD_PInstance_ID=").append(getAD_PInstance_ID()); sql.append(" WHERE AD_PInstance_ID=").append(getAD_PInstance_ID());
@ -571,9 +571,8 @@ public class ReplenishReportProduction extends SvrProcess
MProduct product = MProduct.get(getCtx(), replenish.getM_Product_ID()); MProduct product = MProduct.get(getCtx(), replenish.getM_Product_ID());
String MMPolicy = product.getMMPolicy(); String MMPolicy = product.getMMPolicy();
MStorageOnHand[] storages = MStorageOnHand.getWarehouse(getCtx(), MStorageOnHand[] storages = MStorageOnHand.getWarehouse(getCtx(),
whSource.getM_Warehouse_ID(), replenish.getM_Product_ID(), 0, 0, whSource.getM_Warehouse_ID(), replenish.getM_Product_ID(), 0, null,
true, null, MClient.MMPOLICY_FiFo.equals(MMPolicy), false, 0, get_TrxName());
MClient.MMPOLICY_FiFo.equals(MMPolicy), get_TrxName());
// //
BigDecimal target = replenish.getQtyToOrder(); BigDecimal target = replenish.getQtyToOrder();
for (int j = 0; j < storages.length; j++) for (int j = 0; j < storages.length; j++)

View File

@ -69,11 +69,18 @@ public class StorageCleanup extends SvrProcess
log.info(""); log.info("");
// Clean up empty Storage // Clean up empty Storage
String sql = "DELETE FROM M_StorageOnHand " String sql = "DELETE FROM M_StorageOnHand "
+ "WHERE QtyOnHand = 0 AND QtyReserved = 0 AND QtyOrdered = 0" + "WHERE QtyOnHand = 0"
+ " AND Created < SysDate-3"; + " AND Created < SysDate-3";
int no = DB.executeUpdate(sql, get_TrxName()); int no = DB.executeUpdate(sql, get_TrxName());
log.info("Delete Empty #" + no); log.info("Delete Empty #" + no);
// Clean up empty Reservation Storage
sql = "DELETE FROM M_StorageReservation "
+ "WHERE Qty = 0"
+ " AND Created < SysDate-3";
no = DB.executeUpdate(sql, get_TrxName());
log.info("Delete Empty #" + no);
// //
sql = "SELECT * " sql = "SELECT * "
+ "FROM M_StorageOnHand s " + "FROM M_StorageOnHand s "

View File

@ -1361,22 +1361,60 @@ public class MInOut extends X_M_InOut implements DocAction
get_TrxName())) get_TrxName()))
{ {
String lastError = CLogger.retrieveErrorString(""); String lastError = CLogger.retrieveErrorString("");
m_processMsg = "Cannot correct Inventory (MA) - " + lastError; m_processMsg = "Cannot correct Inventory OnHand (MA) - " + lastError;
return DocAction.STATUS_Invalid; return DocAction.STATUS_Invalid;
} }
if (sameWarehouse && reservedDiff.signum() != 0) {
if (!MStorageReservation.add(getCtx(), getM_Warehouse_ID(),
sLine.getM_Product_ID(),
ma.getM_AttributeSetInstance_ID(), reservationAttributeSetInstance_ID,
reservedDiff,
true,
get_TrxName()))
{
String lastError = CLogger.retrieveErrorString("");
m_processMsg = "Cannot correct Inventory Reserved (MA) - " + lastError;
return DocAction.STATUS_Invalid;
}
}
if (sameWarehouse && orderedDiff.signum() != 0) {
if (!MStorageReservation.add(getCtx(), getM_Warehouse_ID(),
sLine.getM_Product_ID(),
ma.getM_AttributeSetInstance_ID(), reservationAttributeSetInstance_ID,
orderedDiff,
false,
get_TrxName()))
{
String lastError = CLogger.retrieveErrorString("");
m_processMsg = "Cannot correct Inventory Ordered (MA) - " + lastError;
return DocAction.STATUS_Invalid;
}
}
if (!sameWarehouse) { if (!sameWarehouse) {
//correct qtyOrdered in warehouse of order //correct qtyOrdered in warehouse of order
MWarehouse wh = MWarehouse.get(getCtx(), oLine.getM_Warehouse_ID()); MWarehouse wh = MWarehouse.get(getCtx(), oLine.getM_Warehouse_ID());
if (!MStorageOnHand.add(getCtx(), oLine.getM_Warehouse_ID(), if (reservedDiff.signum() != 0) {
wh.getDefaultLocator().getM_Locator_ID(), if (!MStorageReservation.add(getCtx(), oLine.getM_Warehouse_ID(),
sLine.getM_Product_ID(), sLine.getM_Product_ID(),
ma.getM_AttributeSetInstance_ID(), reservationAttributeSetInstance_ID, ma.getM_AttributeSetInstance_ID(), reservationAttributeSetInstance_ID,
Env.ZERO, get_TrxName())) reservedDiff, true, get_TrxName()))
{ {
m_processMsg = "Cannot correct Inventory (MA) in order warehouse"; m_processMsg = "Cannot correct Inventory Reserved (MA) in order warehouse";
return DocAction.STATUS_Invalid; return DocAction.STATUS_Invalid;
} }
} }
if (orderedDiff.signum() != 0) {
if (!MStorageReservation.add(getCtx(), oLine.getM_Warehouse_ID(),
sLine.getM_Product_ID(),
ma.getM_AttributeSetInstance_ID(), reservationAttributeSetInstance_ID,
orderedDiff, false, get_TrxName()))
{
m_processMsg = "Cannot correct Inventory Ordered (MA) in order warehouse";
return DocAction.STATUS_Invalid;
}
}
}
// Create Transaction // Create Transaction
mtrx = new MTransaction (getCtx(), sLine.getAD_Org_ID(), mtrx = new MTransaction (getCtx(), sLine.getAD_Org_ID(),
MovementType, sLine.getM_Locator_ID(), MovementType, sLine.getM_Locator_ID(),
@ -1403,22 +1441,53 @@ public class MInOut extends X_M_InOut implements DocAction
sLine.getM_AttributeSetInstance_ID(), reservationAttributeSetInstance_ID, sLine.getM_AttributeSetInstance_ID(), reservationAttributeSetInstance_ID,
Qty, get_TrxName())) Qty, get_TrxName()))
{ {
m_processMsg = "Cannot correct Inventory"; m_processMsg = "Cannot correct Inventory OnHand";
return DocAction.STATUS_Invalid; return DocAction.STATUS_Invalid;
} }
if (reservedDiff.signum() != 0) {
if (!MStorageReservation.add(getCtx(), getM_Warehouse_ID(),
sLine.getM_Product_ID(),
sLine.getM_AttributeSetInstance_ID(), reservationAttributeSetInstance_ID,
reservedDiff, true, get_TrxName()))
{
m_processMsg = "Cannot correct Inventory Reserved";
return DocAction.STATUS_Invalid;
}
}
if (orderedDiff.signum() != 0) {
if (!MStorageReservation.add(getCtx(), getM_Warehouse_ID(),
sLine.getM_Product_ID(),
sLine.getM_AttributeSetInstance_ID(), reservationAttributeSetInstance_ID,
orderedDiff, false, get_TrxName()))
{
m_processMsg = "Cannot correct Inventory Ordered";
return DocAction.STATUS_Invalid;
}
}
if (!sameWarehouse) { if (!sameWarehouse) {
//correct qtyOrdered in warehouse of order //correct qtyOrdered in warehouse of order
MWarehouse wh = MWarehouse.get(getCtx(), oLine.getM_Warehouse_ID()); MWarehouse wh = MWarehouse.get(getCtx(), oLine.getM_Warehouse_ID());
if (!MStorageOnHand.add(getCtx(), oLine.getM_Warehouse_ID(), if (QtySO.signum() != 0) {
wh.getDefaultLocator().getM_Locator_ID(), if (!MStorageReservation.add(getCtx(), oLine.getM_Warehouse_ID(),
sLine.getM_Product_ID(), sLine.getM_Product_ID(),
sLine.getM_AttributeSetInstance_ID(), reservationAttributeSetInstance_ID, sLine.getM_AttributeSetInstance_ID(), reservationAttributeSetInstance_ID,
Env.ZERO, get_TrxName())) QtySO.negate(), true, get_TrxName()))
{ {
m_processMsg = "Cannot correct Inventory"; m_processMsg = "Cannot correct Inventory Reserved";
return DocAction.STATUS_Invalid; return DocAction.STATUS_Invalid;
} }
} }
if (QtyPO.signum() != 0) {
if (!MStorageReservation.add(getCtx(), oLine.getM_Warehouse_ID(),
sLine.getM_Product_ID(),
sLine.getM_AttributeSetInstance_ID(), reservationAttributeSetInstance_ID,
QtyPO.negate(), false, get_TrxName()))
{
m_processMsg = "Cannot correct Inventory Ordered";
return DocAction.STATUS_Invalid;
}
}
}
// FallBack: Create Transaction // FallBack: Create Transaction
mtrx = new MTransaction (getCtx(), sLine.getAD_Org_ID(), mtrx = new MTransaction (getCtx(), sLine.getAD_Org_ID(),
MovementType, sLine.getM_Locator_ID(), MovementType, sLine.getM_Locator_ID(),

View File

@ -332,7 +332,7 @@ public final class MLocatorLookup extends Lookup implements Serializable
sql.append(" AND (IsDefault='Y' ") // Default Locator sql.append(" AND (IsDefault='Y' ") // Default Locator
.append("OR EXISTS (SELECT * FROM M_Product p ") // Product Locator .append("OR EXISTS (SELECT * FROM M_Product p ") // Product Locator
.append("WHERE p.M_Locator_ID=M_Locator.M_Locator_ID AND p.M_Product_ID=?)") .append("WHERE p.M_Locator_ID=M_Locator.M_Locator_ID AND p.M_Product_ID=?)")
.append("OR EXISTS (SELECT * FROM M_StorageOnHand s ") // Storage Locator .append("OR EXISTS (SELECT * FROM M_Storage s ") // Storage Locator
.append("WHERE s.M_Locator_ID=M_Locator.M_Locator_ID AND s.M_Product_ID=?))"); .append("WHERE s.M_Locator_ID=M_Locator.M_Locator_ID AND s.M_Product_ID=?))");
sql.append(" ORDER BY "); sql.append(" ORDER BY ");
if (local_only_warehouse_id == 0) if (local_only_warehouse_id == 0)

View File

@ -1566,38 +1566,13 @@ public class MOrder extends X_C_Order implements DocAction
{ {
if (product.isStocked()) if (product.isStocked())
{ {
BigDecimal ordered = isSOTrx ? Env.ZERO : difference; // Update Reservation Storage
BigDecimal reserved = isSOTrx ? difference : Env.ZERO; if (!MStorageReservation.add(getCtx(), line.getM_Warehouse_ID(),
int M_Locator_ID = 0;
// Get Locator to reserve
if (line.getM_AttributeSetInstance_ID() != 0) // Get existing Location
M_Locator_ID = MStorageOnHand.getM_Locator_ID (line.getM_Warehouse_ID(),
line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(),
ordered, get_TrxName());
// Get default Location
if (M_Locator_ID == 0)
{
// try to take default locator for product first
// if it is from the selected warehouse
MWarehouse wh = MWarehouse.get(getCtx(), line.getM_Warehouse_ID());
M_Locator_ID = product.getM_Locator_ID();
if (M_Locator_ID!=0) {
MLocator locator = new MLocator(getCtx(), product.getM_Locator_ID(), get_TrxName());
//product has default locator defined but is not from the order warehouse
if(locator.getM_Warehouse_ID()!=wh.get_ID()) {
M_Locator_ID = wh.getDefaultLocator().getM_Locator_ID();
}
} else {
M_Locator_ID = wh.getDefaultLocator().getM_Locator_ID();
}
}
// Update Storage
if (!MStorageOnHand.add(getCtx(), line.getM_Warehouse_ID(), M_Locator_ID,
line.getM_Product_ID(), line.getM_Product_ID(),
line.getM_AttributeSetInstance_ID(), line.getM_AttributeSetInstance_ID(), line.getM_AttributeSetInstance_ID(), line.getM_AttributeSetInstance_ID(),
Env.ZERO, get_TrxName())) difference, isSOTrx, get_TrxName()))
return false; return false;
} // stockec } // stocked
// update line // update line
line.setQtyReserved(line.getQtyReserved().add(difference)); line.setQtyReserved(line.getQtyReserved().add(difference));
if (!line.save(get_TrxName())) if (!line.save(get_TrxName()))

View File

@ -19,6 +19,7 @@ package org.compiere.model;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.Timestamp;
import java.util.Properties; import java.util.Properties;
import java.util.logging.Level; import java.util.logging.Level;
@ -875,7 +876,7 @@ public class MOrderLine extends X_C_OrderLine
{ {
MStorageOnHand[] storages = MStorageOnHand.getWarehouse(getCtx(), MStorageOnHand[] storages = MStorageOnHand.getWarehouse(getCtx(),
getM_Warehouse_ID(), getM_Product_ID(), getM_AttributeSetInstance_ID(), getM_Warehouse_ID(), getM_Product_ID(), getM_AttributeSetInstance_ID(),
M_AttributeSet_ID, false, null, true, get_TrxName()); null, true, false, 0, get_TrxName());
BigDecimal qty = Env.ZERO; BigDecimal qty = Env.ZERO;
for (int i = 0; i < storages.length; i++) for (int i = 0; i < storages.length; i++)
{ {

View File

@ -26,6 +26,7 @@ import org.compiere.util.CCache;
import org.compiere.util.DB; import org.compiere.util.DB;
import org.compiere.util.Env; import org.compiere.util.Env;
import org.compiere.util.Msg; import org.compiere.util.Msg;
import org.compiere.util.Util;
/** /**
* Product Model * Product Model
@ -571,33 +572,8 @@ public class MProduct extends X_M_Product
|| (is_ValueChanged("ProductType") // from Item || (is_ValueChanged("ProductType") // from Item
&& PRODUCTTYPE_Item.equals(get_ValueOld("ProductType"))))) && PRODUCTTYPE_Item.equals(get_ValueOld("ProductType")))))
{ {
// large modified related to storages by zuhri String errMsg = verifyStorage();
MStorageOnHand[] onHandStorages = MStorageOnHand.getOfProduct(getCtx(), get_ID(), get_TrxName()); if (! Util.isEmpty(errMsg))
MStorageReservation[] reservationStorages = MStorageReservation.getOfProduct(getCtx(), get_ID(), get_TrxName());
BigDecimal OnHand = Env.ZERO;
BigDecimal Ordered = Env.ZERO;
BigDecimal Reserved = Env.ZERO;
for (int i = 0; i < onHandStorages.length; i++)
{
OnHand = OnHand.add(onHandStorages[i].getQtyOnHand());
}
for (int i = 0; i < reservationStorages.length; i++)
{
if(reservationStorages[i].isSOTrx())
Reserved = Reserved.add(reservationStorages[i].getQty());
else
Ordered = Ordered.add(reservationStorages[i].getQty());
}
// end large modified related to storages by zuhri
String errMsg = "";
if (OnHand.signum() != 0)
errMsg = "@QtyOnHand@ = " + OnHand;
if (Ordered.signum() != 0)
errMsg += " - @QtyOrdered@ = " + Ordered;
if (Reserved.signum() != 0)
errMsg += " - @QtyReserved@" + Reserved;
if (errMsg.length() > 0)
{ {
log.saveError("Error", Msg.parseTranslation(getCtx(), errMsg)); log.saveError("Error", Msg.parseTranslation(getCtx(), errMsg));
return false; return false;
@ -638,6 +614,32 @@ public class MProduct extends X_M_Product
return true; return true;
} // beforeSave } // beforeSave
private String verifyStorage() {
BigDecimal qtyOnHand = Env.ZERO;
BigDecimal qtyOrdered = Env.ZERO;
BigDecimal qtyReserved = Env.ZERO;
for (MStorageOnHand ohs: MStorageOnHand.getOfProduct(getCtx(), get_ID(), get_TrxName()))
{
qtyOnHand = qtyOnHand.add(ohs.getQtyOnHand());
}
for (MStorageReservation rs : MStorageReservation.getOfProduct(getCtx(), get_ID(), get_TrxName()))
{
if (rs.isSOTrx())
qtyReserved = qtyReserved.add(rs.getQty());
else
qtyOrdered = qtyOrdered.add(rs.getQty());
}
StringBuilder errMsg = new StringBuilder();
if (qtyOnHand.signum() != 0)
errMsg.append("@QtyOnHand@ = ").append(qtyOnHand);
if (qtyOrdered.signum() != 0)
errMsg.append(" - @QtyOrdered@ = ").append(qtyOrdered);
if (qtyReserved.signum() != 0)
errMsg.append(" - @QtyReserved@").append(qtyReserved);
return errMsg.toString();
}
/** /**
* HasInventoryOrCost * HasInventoryOrCost
* @return true if it has Inventory or Cost * @return true if it has Inventory or Cost
@ -719,31 +721,8 @@ public class MProduct extends X_M_Product
// Check Storage // Check Storage
if (isStocked() || PRODUCTTYPE_Item.equals(getProductType())) if (isStocked() || PRODUCTTYPE_Item.equals(getProductType()))
{ {
MStorageOnHand[] storages = MStorageOnHand.getOfProduct(getCtx(), get_ID(), get_TrxName()); String errMsg = verifyStorage();
MStorageReservation[] reserves = MStorageReservation.getOfProduct(getCtx(), get_ID(), get_TrxName()); if (! Util.isEmpty(errMsg))
BigDecimal OnHand = Env.ZERO;
BigDecimal Ordered = Env.ZERO;
BigDecimal Reserved = Env.ZERO;
for (int i = 0; i < storages.length; i++)
{
OnHand = OnHand.add(storages[i].getQtyOnHand());
}
for (int i = 0; i < reserves.length; i++)
{
Ordered = OnHand.add(reserves[i].getQtyOrdered());
Reserved = OnHand.add(reserves[i].getQtyReserved());
}
String errMsg = "";
if (OnHand.signum() != 0)
errMsg = "@QtyOnHand@ = " + OnHand;
if (Ordered.signum() != 0)
errMsg += " - @QtyOrdered@ = " + Ordered;
if (Reserved.signum() != 0)
errMsg += " - @QtyReserved@" + Reserved;
if (errMsg.length() > 0)
{ {
log.saveError("Error", Msg.parseTranslation(getCtx(), errMsg)); log.saveError("Error", Msg.parseTranslation(getCtx(), errMsg));
return false; return false;

View File

@ -22,11 +22,10 @@ import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
import java.util.Properties; import java.util.Properties;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.List;
import org.compiere.util.CLogMgt;
import org.compiere.util.CLogger; import org.compiere.util.CLogger;
import org.compiere.util.DB; import org.compiere.util.DB;
import org.compiere.util.Env; import org.compiere.util.Env;
@ -36,14 +35,14 @@ import org.compiere.util.Msg;
* Inventory Storage Model * Inventory Storage Model
* *
* @author Jorg Janke * @author Jorg Janke
* @version $Id: MStorage.java,v 1.3 2006/07/30 00:51:05 jjanke Exp $ * @version $Id: MStorageOnHand.java,v 1.3 2006/07/30 00:51:05 jjanke Exp $
*/ */
public class MStorageOnHand extends X_M_StorageOnHand public class MStorageOnHand extends X_M_StorageOnHand
{ {
/** /**
* *
*/ */
private static final long serialVersionUID = 3911132565445025309L; private static final long serialVersionUID = 3649163126231150631L;
/** /**
* Get Storage Info * Get Storage Info
@ -132,12 +131,10 @@ public class MStorageOnHand extends X_M_StorageOnHand
public static MStorageOnHand[] getAll (Properties ctx, public static MStorageOnHand[] getAll (Properties ctx,
int M_Product_ID, int M_Locator_ID, String trxName) int M_Product_ID, int M_Locator_ID, String trxName)
{ {
String sqlWhere = "M_Product_ID=? AND M_Locator_ID=?" String sqlWhere = "M_Product_ID=? AND M_Locator_ID=? AND QtyOnHand <> 0";
+ " AND QtyOnHand <> 0 "
+ "ORDER BY M_AttributeSetInstance_ID";
List<MStorageOnHand> list = new Query(ctx, MStorageOnHand.Table_Name, sqlWhere, trxName) List<MStorageOnHand> list = new Query(ctx, MStorageOnHand.Table_Name, sqlWhere, trxName)
.setParameters(M_Product_ID, M_Locator_ID) .setParameters(M_Product_ID, M_Locator_ID)
.setOrderBy(MStorageOnHand.COLUMNNAME_M_AttributeSetInstance_ID)
.list(); .list();
MStorageOnHand[] retValue = new MStorageOnHand[list.size()]; MStorageOnHand[] retValue = new MStorageOnHand[list.size()];
@ -173,8 +170,8 @@ public class MStorageOnHand extends X_M_StorageOnHand
* @param M_Warehouse_ID * @param M_Warehouse_ID
* @param M_Product_ID product * @param M_Product_ID product
* @param M_AttributeSetInstance_ID instance * @param M_AttributeSetInstance_ID instance
* @param M_AttributeSet_ID attribute set * @param M_AttributeSet_ID attribute set (NOT USED)
* @param allAttributeInstances if true, all attribute set instances * @param allAttributeInstances if true, all attribute set instances (NOT USED)
* @param minGuaranteeDate optional minimum guarantee date if all attribute instances * @param minGuaranteeDate optional minimum guarantee date if all attribute instances
* @param FiFo first in-first-out * @param FiFo first in-first-out
* @param trxName transaction * @param trxName transaction
@ -219,11 +216,7 @@ public class MStorageOnHand extends X_M_StorageOnHand
// Specific Attribute Set Instance // Specific Attribute Set Instance
String sql = "SELECT s.M_Product_ID,s.M_Locator_ID,s.M_AttributeSetInstance_ID," String sql = "SELECT s.M_Product_ID,s.M_Locator_ID,s.M_AttributeSetInstance_ID,"
+ "s.AD_Client_ID,s.AD_Org_ID,s.IsActive,s.Created,s.CreatedBy,s.Updated,s.UpdatedBy," + "s.AD_Client_ID,s.AD_Org_ID,s.IsActive,s.Created,s.CreatedBy,s.Updated,s.UpdatedBy,"
//@win change
//+ "s.QtyOnHand,s.QtyReserved,s.QtyOrdered,s.DateLastInventory "
+ "s.QtyOnHand,s.DateLastInventory " + "s.QtyOnHand,s.DateLastInventory "
//end @win change
+ "FROM M_StorageOnHand s" + "FROM M_StorageOnHand s"
+ " INNER JOIN M_Locator l ON (l.M_Locator_ID=s.M_Locator_ID) "; + " INNER JOIN M_Locator l ON (l.M_Locator_ID=s.M_Locator_ID) ";
if (M_Locator_ID > 0) if (M_Locator_ID > 0)
@ -248,11 +241,7 @@ public class MStorageOnHand extends X_M_StorageOnHand
{ {
sql = "SELECT s.M_Product_ID,s.M_Locator_ID,s.M_AttributeSetInstance_ID," sql = "SELECT s.M_Product_ID,s.M_Locator_ID,s.M_AttributeSetInstance_ID,"
+ "s.AD_Client_ID,s.AD_Org_ID,s.IsActive,s.Created,s.CreatedBy,s.Updated,s.UpdatedBy," + "s.AD_Client_ID,s.AD_Org_ID,s.IsActive,s.Created,s.CreatedBy,s.Updated,s.UpdatedBy,"
//@win change + "s.QtyOnHand,s.DateLastInventory,s.M_StorageOnHand_UU "
//+ "s.QtyOnHand,s.QtyReserved,s.QtyOrdered,s.DateLastInventory "
+ "s.QtyOnHand,s.DateLastInventory "
//end @win change
+ "FROM M_StorageOnHand s" + "FROM M_StorageOnHand s"
+ " INNER JOIN M_Locator l ON (l.M_Locator_ID=s.M_Locator_ID)" + " 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) "; + " LEFT OUTER JOIN M_AttributeSetInstance asi ON (s.M_AttributeSetInstance_ID=asi.M_AttributeSetInstance_ID) ";
@ -349,7 +338,7 @@ public class MStorageOnHand extends X_M_StorageOnHand
throw new IllegalArgumentException("Not found M_Locator_ID=" + M_Locator_ID); throw new IllegalArgumentException("Not found M_Locator_ID=" + M_Locator_ID);
// //
retValue = new MStorageOnHand (locator, M_Product_ID, M_AttributeSetInstance_ID); retValue = new MStorageOnHand (locator, M_Product_ID, M_AttributeSetInstance_ID);
retValue.save(trxName); retValue.saveEx(trxName);
s_log.fine("New " + retValue); s_log.fine("New " + retValue);
return retValue; return retValue;
} // getCreate } // getCreate
@ -442,7 +431,7 @@ public class MStorageOnHand extends X_M_StorageOnHand
diffText.append(") -> ").append(storage.toString()); diffText.append(") -> ").append(storage.toString());
s_log.fine(diffText.toString()); s_log.fine(diffText.toString());
if (storage0 != null) if (storage0 != null)
storage0.save(trxName); // No AttributeSetInstance (reserved/ordered) storage0.saveEx(trxName); // No AttributeSetInstance (reserved/ordered)
return storage.save (trxName); return storage.save (trxName);
} }
@ -525,7 +514,7 @@ public class MStorageOnHand extends X_M_StorageOnHand
// //
setQtyOnHand (Env.ZERO); setQtyOnHand (Env.ZERO);
} // MStorage } // MStorageOnHand
/** /**
* Load Constructor * Load Constructor
@ -536,7 +525,7 @@ public class MStorageOnHand extends X_M_StorageOnHand
public MStorageOnHand (Properties ctx, ResultSet rs, String trxName) public MStorageOnHand (Properties ctx, ResultSet rs, String trxName)
{ {
super(ctx, rs, trxName); super(ctx, rs, trxName);
} // MStorage } // MStorageOnHand
/** /**
* Full NEW Constructor * Full NEW Constructor
@ -551,7 +540,7 @@ public class MStorageOnHand extends X_M_StorageOnHand
setM_Locator_ID (locator.getM_Locator_ID()); setM_Locator_ID (locator.getM_Locator_ID());
setM_Product_ID (M_Product_ID); setM_Product_ID (M_Product_ID);
setM_AttributeSetInstance_ID (M_AttributeSetInstance_ID); setM_AttributeSetInstance_ID (M_AttributeSetInstance_ID);
} // MStorage } // MStorageOnHand
/** Log */ /** Log */
private static CLogger s_log = CLogger.getCLogger (MStorageOnHand.class); private static CLogger s_log = CLogger.getCLogger (MStorageOnHand.class);
@ -640,18 +629,18 @@ public class MStorageOnHand extends X_M_StorageOnHand
* @return * @return
*/ */
public static BigDecimal getQtyOnHand(int M_Product_ID, int M_Warehouse_ID, int M_AttributeSetInstance_ID, String trxName) { public static BigDecimal getQtyOnHand(int M_Product_ID, int M_Warehouse_ID, int M_AttributeSetInstance_ID, String trxName) {
ArrayList<Object> params = new ArrayList<Object>();
StringBuffer sql = new StringBuffer(); StringBuffer sql = new StringBuffer();
sql.append(" SELECT SUM(QtyOnHand) FROM M_StorageOnHand oh") sql.append(" SELECT SUM(QtyOnHand) FROM M_StorageOnHand oh JOIN M_Locator loc ON (oh.M_Locator_ID=loc.M_Locator_ID)")
.append(" WHERE oh.M_Product_ID=?") .append(" WHERE oh.M_Product_ID=?")
.append(" AND EXISTS(SELECT 1 FROM M_Locator loc WHERE oh.M_Locator_ID=loc.M_Locator_ID AND loc.M_Warehouse_ID=?)"); .append(" AND loc.M_Warehouse_ID=?");
ArrayList<Object> params = new ArrayList<Object>();
params.add(M_Product_ID); params.add(M_Product_ID);
params.add(M_Warehouse_ID); params.add(M_Warehouse_ID);
// With ASI // With ASI
if (M_AttributeSetInstance_ID != 0) { if (M_AttributeSetInstance_ID != 0) {
sql.append(" AND M_AttributeSetInstance_ID=?"); sql.append(" AND oh.M_AttributeSetInstance_ID=?");
params.add(M_AttributeSetInstance_ID); params.add(M_AttributeSetInstance_ID);
} }
@ -668,7 +657,7 @@ public class MStorageOnHand extends X_M_StorageOnHand
*/ */
public String toString() public String toString()
{ {
StringBuffer sb = new StringBuffer("MStorage[") StringBuffer sb = new StringBuffer("MStorageOnHand[")
.append("M_Locator_ID=").append(getM_Locator_ID()) .append("M_Locator_ID=").append(getM_Locator_ID())
.append(",M_Product_ID=").append(getM_Product_ID()) .append(",M_Product_ID=").append(getM_Product_ID())
.append(",M_AttributeSetInstance_ID=").append(getM_AttributeSetInstance_ID()) .append(",M_AttributeSetInstance_ID=").append(getM_AttributeSetInstance_ID())
@ -681,4 +670,4 @@ public class MStorageOnHand extends X_M_StorageOnHand
return sb.toString(); return sb.toString();
} // toString } // toString
} // MStorage } // MStorageOnHand

View File

@ -1,36 +1,118 @@
/******************************************************************************
* Product: iDempiere ERP & CRM Smart Business Solution *
* Copyright (C) 1999-2012 iDempiere All Rights Reserved. *
* This program is free software; you can redistribute it and/or modify it *
* under the terms version 2 of the GNU General Public License as published *
* by the Free Software Foundation. This program is distributed in the hope *
* that it will be useful, but WITHOUT ANY WARRANTY; without even the implied *
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* See the GNU General Public License for more details. *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
*****************************************************************************/
package org.compiere.model; package org.compiere.model;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Properties; import java.util.Properties;
import java.util.logging.Level;
import org.compiere.util.CLogMgt;
import org.compiere.util.CLogger; import org.compiere.util.CLogger;
import org.compiere.util.DB; import org.compiere.util.DB;
import org.compiere.util.Env; import org.compiere.util.Env;
public class MStorageReservation extends X_M_StorageReservation { public class MStorageReservation extends X_M_StorageReservation {
/** /**
* *
*/ */
private static final long serialVersionUID = 8114446879871270122L; private static final long serialVersionUID = -8646802850122507899L;
/**
* Get Storage Info
* @param ctx context
* @param M_Warehouse_ID warehouse
* @param M_Product_ID product
* @param M_AttributeSetInstance_ID instance
* @param isSOTrx
* @param trxName transaction
* @return existing or null
*/
public static MStorageReservation get (Properties ctx, int M_Warehouse_ID,
int M_Product_ID, int M_AttributeSetInstance_ID, boolean isSOTrx, String trxName)
{
MStorageReservation retValue = null;
String sql = "SELECT * FROM M_StorageReservation "
+ "WHERE M_Warehouse_ID=? AND M_Product_ID=? AND IsSOTrx=? AND ";
if (M_AttributeSetInstance_ID == 0)
sql += "(M_AttributeSetInstance_ID=? OR M_AttributeSetInstance_ID IS NULL)";
else
sql += "M_AttributeSetInstance_ID=?";
PreparedStatement pstmt = null;
ResultSet rs = null;
try
{
pstmt = DB.prepareStatement (sql, trxName);
pstmt.setInt (1, M_Warehouse_ID);
pstmt.setInt (2, M_Product_ID);
pstmt.setString (3, isSOTrx ? "Y" : "N");
pstmt.setInt (4, M_AttributeSetInstance_ID);
rs = pstmt.executeQuery ();
if (rs.next ())
retValue = new MStorageReservation (ctx, rs, trxName);
}
catch (SQLException ex)
{
s_log.log(Level.SEVERE, sql, ex);
}
finally
{
DB.close(rs, pstmt);
rs = null; pstmt = null;
}
if (retValue == null)
s_log.fine("Not Found - M_Warehouse_ID=" + M_Warehouse_ID
+ ", M_Product_ID=" + M_Product_ID + ", M_AttributeSetInstance_ID=" + M_AttributeSetInstance_ID + ", IsSOTrx=" + isSOTrx);
else
s_log.fine("M_Warehouse_ID=" + M_Warehouse_ID
+ ", M_Product_ID=" + M_Product_ID + ", M_AttributeSetInstance_ID=" + M_AttributeSetInstance_ID + ", IsSOTrx=" + isSOTrx);
return retValue;
} // get
private static CLogger s_log = CLogger.getCLogger(MStorageReservation.class); private static CLogger s_log = CLogger.getCLogger(MStorageReservation.class);
public MStorageReservation(Properties ctx, int M_StorageReservation_ID, public MStorageReservation(Properties ctx, int M_StorageReservation_ID,
String trxName) { String trxName) {
super(ctx, M_StorageReservation_ID, trxName); super(ctx, M_StorageReservation_ID, trxName);
// TODO Auto-generated constructor stub
} }
public MStorageReservation(Properties ctx, ResultSet rs, public MStorageReservation(Properties ctx, ResultSet rs,
String trxName) { String trxName) {
super(ctx, rs, trxName); super(ctx, rs, trxName);
// TODO Auto-generated constructor stub
} }
/**
* Full NEW Constructor
* @param warehouse (parent) warehouse
* @param M_Product_ID product
* @param M_AttributeSetInstance_ID attribute
* @param isSOTrx
*/
private MStorageReservation (MWarehouse warehouse, int M_Product_ID, int M_AttributeSetInstance_ID, boolean isSOTrx)
{
this (warehouse.getCtx(), 0, warehouse.get_TrxName());
setClientOrg(warehouse);
setM_Warehouse_ID(warehouse.getM_Warehouse_ID());
setM_Product_ID (M_Product_ID);
setM_AttributeSetInstance_ID (M_AttributeSetInstance_ID);
setIsSOTrx(isSOTrx);
setQty(Env.ZERO);
} // MStorageReservation
/** /**
* Get Storage Info for Product on specified Warehouse * Get Storage Info for Product on specified Warehouse
* @param ctx * @param ctx
@ -74,25 +156,25 @@ public class MStorageReservation extends X_M_StorageReservation {
} // getOfProduct } // getOfProduct
/** /**
* Get Quantity Reserved of Warehouse * Get Quantity Reserved of Warehouse
* @param M_Product_ID * @param M_Product_ID
* @param M_Warehouse_ID * @param M_Warehouse_ID
* @param M_AttributeSetInstance_ID * @param M_AttributeSetInstance_ID
* @param isSOTrx - true to get reserved, false to get ordered
* @param trxName * @param trxName
* @return * @return
*/ */
public static BigDecimal getQtyReserved(int M_Product_ID, int M_Warehouse_ID, int M_AttributeSetInstance_ID, String trxName){ private static BigDecimal getQty(int M_Product_ID, int M_Warehouse_ID, int M_AttributeSetInstance_ID, boolean isSOTrx, String trxName) {
ArrayList<Object> params = new ArrayList<Object>(); ArrayList<Object> params = new ArrayList<Object>();
StringBuffer sql = new StringBuffer(); StringBuffer sql = new StringBuffer();
sql.append(" SELECT SUM(Qty) FROM M_StorageReservation oh") sql.append(" SELECT SUM(Qty) FROM M_StorageReservation sr")
.append(" WHERE oh.M_Product_ID=? AND oh.M_Warehouse_ID=?") .append(" WHERE sr.M_Product_ID=? AND sr.M_Warehouse_ID=?")
.append(" AND oh.IsSOTrx='Y'"); .append(" AND sr.IsSOTrx=?");
params.add(M_Product_ID); params.add(M_Product_ID);
params.add(M_Warehouse_ID); params.add(M_Warehouse_ID);
params.add(isSOTrx ? "Y" : "N");
// With ASI // With ASI
if (M_AttributeSetInstance_ID != 0) { if (M_AttributeSetInstance_ID != 0) {
@ -107,13 +189,6 @@ public class MStorageReservation extends X_M_StorageReservation {
return qty; return qty;
} }
public static BigDecimal getQtyOrdered (int M_Warehouse_ID,
int M_Product_ID, int M_AttributeSetInstance_ID, String trxName)
{
BigDecimal retValue = null;
return retValue;
}
/** /**
* Get Available Qty. * Get Available Qty.
* The call is accurate only if there is a storage record * The call is accurate only if there is a storage record
@ -128,19 +203,116 @@ public class MStorageReservation extends X_M_StorageReservation {
int M_Product_ID, int M_AttributeSetInstance_ID, String trxName) int M_Product_ID, int M_AttributeSetInstance_ID, String trxName)
{ {
BigDecimal qtyOnHand = MStorageOnHand.getQtyOnHand(M_Product_ID, M_Warehouse_ID, M_AttributeSetInstance_ID, trxName); BigDecimal qtyOnHand = MStorageOnHand.getQtyOnHand(M_Product_ID, M_Warehouse_ID, M_AttributeSetInstance_ID, trxName);
BigDecimal qtyReserved = getQtyReserved(M_Product_ID, M_Warehouse_ID, M_AttributeSetInstance_ID, trxName); BigDecimal qtyReserved = MStorageReservation.getQty(M_Product_ID, M_Warehouse_ID, M_AttributeSetInstance_ID, true, trxName);
BigDecimal retValue = qtyOnHand.subtract(qtyReserved); BigDecimal retValue = qtyOnHand.subtract(qtyReserved);
return retValue; return retValue;
} }
public BigDecimal getQtyOrdered() { /**
// TODO Auto-generated method stub * Update Storage Info add.
return null; * Called from MProjectIssue
* @param ctx context
* @param M_Warehouse_ID warehouse
* @param M_Product_ID product
* @param M_AttributeSetInstance_ID AS Instance
* @param reservationAttributeSetInstance_ID reservation AS Instance
* @param diffQty add
* @param isSOTrx
* @param trxName transaction
* @return true if updated
*/
public static boolean add (Properties ctx, int M_Warehouse_ID,
int M_Product_ID, int M_AttributeSetInstance_ID, int reservationAttributeSetInstance_ID,
BigDecimal diffQty, boolean isSOTrx, String trxName)
{
/* Do NOT use FIFO ASI for reservation */
MProduct prd = new MProduct(ctx, M_Product_ID, trxName);
if (prd.getM_AttributeSet_ID() == 0 || ! prd.getM_AttributeSet().isInstanceAttribute()) {
// Product doesn't manage attribute set, always reserved with 0
reservationAttributeSetInstance_ID = 0;
M_AttributeSetInstance_ID = 0;
}
//
MStorageReservation storage = null;
StringBuffer diffText = new StringBuffer("(");
// Get Storage
if (storage == null)
storage = getCreate (ctx, M_Warehouse_ID,
M_Product_ID, M_AttributeSetInstance_ID, isSOTrx, trxName);
// Verify
if (storage.getM_Warehouse_ID() != M_Warehouse_ID
&& storage.getM_Product_ID() != M_Product_ID
&& storage.getM_AttributeSetInstance_ID() != M_AttributeSetInstance_ID)
{
s_log.severe ("No Storage found - M_Warehouse_ID=" + M_Warehouse_ID
+ ",M_Product_ID=" + M_Product_ID + ",ASI=" + M_AttributeSetInstance_ID);
return false;
} }
public BigDecimal getQtyReserved() { MStorageReservation storage0 = null;
// TODO Auto-generated method stub if (M_AttributeSetInstance_ID != reservationAttributeSetInstance_ID)
return null; {
storage0 = get(ctx, M_Warehouse_ID,
M_Product_ID, reservationAttributeSetInstance_ID, isSOTrx, trxName);
if (storage0 == null) // create if not existing - should not happen
{
storage0 = getCreate (ctx, M_Warehouse_ID,
M_Product_ID, reservationAttributeSetInstance_ID, isSOTrx, trxName);
}
}
boolean changed = false;
if (diffQty != null && diffQty.signum() != 0)
{
if (storage0 == null)
storage.setQty (storage.getQty().add(diffQty));
else
storage0.setQty (storage0.getQty().add (diffQty));
diffText.append(" Qty=").append(diffQty);
changed = true;
}
if (changed)
{
diffText.append(") -> ").append(storage.toString());
s_log.fine(diffText.toString());
if (storage0 != null)
storage0.saveEx(trxName); // No AttributeSetInstance
return storage.save (trxName);
} }
return true;
} // add
/**
* Create or Get Storage Info
* @param ctx context
* @param M_Locator_ID locator
* @param M_Product_ID product
* @param M_AttributeSetInstance_ID instance
* @param trxName transaction
* @return existing/new or null
*/
public static MStorageReservation getCreate (Properties ctx, int M_Warehouse_ID,
int M_Product_ID, int M_AttributeSetInstance_ID, boolean isSOTrx, String trxName)
{
if (M_Warehouse_ID == 0)
throw new IllegalArgumentException("M_Warehouse_ID=0");
if (M_Product_ID == 0)
throw new IllegalArgumentException("M_Product_ID=0");
MStorageReservation retValue = get(ctx, M_Warehouse_ID, M_Product_ID, M_AttributeSetInstance_ID, isSOTrx, trxName);
if (retValue != null)
return retValue;
// Insert row based on warehouse
MWarehouse warehouse = new MWarehouse (ctx, M_Warehouse_ID, trxName);
if (warehouse.get_ID() != M_Warehouse_ID)
throw new IllegalArgumentException("Not found M_Warehouse_ID=" + M_Warehouse_ID);
//
retValue = new MStorageReservation (warehouse, M_Product_ID, M_AttributeSetInstance_ID, isSOTrx);
retValue.saveEx(trxName);
s_log.fine("New " + retValue);
return retValue;
} // getCreate
} }

View File

@ -23,6 +23,7 @@ import java.util.Properties;
import org.compiere.model.MAttributeSet; import org.compiere.model.MAttributeSet;
import org.compiere.model.MCharge; import org.compiere.model.MCharge;
import org.compiere.model.MClient;
import org.compiere.model.MLocator; import org.compiere.model.MLocator;
import org.compiere.model.MProduct; import org.compiere.model.MProduct;
import org.compiere.model.MStorageOnHand; import org.compiere.model.MStorageOnHand;
@ -579,7 +580,7 @@ public class MDDOrderLine extends X_DD_OrderLine
MLocator locator_from = MLocator.get(getCtx(), getM_Locator_ID()); MLocator locator_from = MLocator.get(getCtx(), getM_Locator_ID());
MStorageOnHand[] storages = MStorageOnHand.getWarehouse(getCtx(), MStorageOnHand[] storages = MStorageOnHand.getWarehouse(getCtx(),
locator_from.getM_Warehouse_ID(), getM_Product_ID(), getM_AttributeSetInstance_ID(), locator_from.getM_Warehouse_ID(), getM_Product_ID(), getM_AttributeSetInstance_ID(),
M_AttributeSet_ID, false, null, true, get_TrxName()); null, true, false, 0, get_TrxName());
BigDecimal qty = Env.ZERO; BigDecimal qty = Env.ZERO;
for (int i = 0; i < storages.length; i++) for (int i = 0; i < storages.length; i++)
{ {

View File

@ -512,7 +512,7 @@ public class InfoPAttribute extends CDialog
// finish Instance Attributes // finish Instance Attributes
if (sb.length() > 0) if (sb.length() > 0)
{ {
sb.insert(0, " AND EXISTS (SELECT * FROM M_StorageOnHand s" sb.insert(0, " AND EXISTS (SELECT * FROM M_Storage s"
+ " INNER JOIN M_AttributeSetInstance asi ON (s.M_AttributeSetInstance_ID=asi.M_AttributeSetInstance_ID) " + " INNER JOIN M_AttributeSetInstance asi ON (s.M_AttributeSetInstance_ID=asi.M_AttributeSetInstance_ID) "
+ "WHERE s.M_Product_ID=p.M_Product_ID"); + "WHERE s.M_Product_ID=p.M_Product_ID");
sb.append(")"); sb.append(")");

View File

@ -1240,23 +1240,21 @@ public class InfoProduct extends Info implements ActionListener, ChangeListener
// Fill Storage Data // Fill Storage Data
boolean showDetail = CLogMgt.isLevelFine(); boolean showDetail = CLogMgt.isLevelFine();
String sql = "SELECT s.QtyOnHand, r.Qty as QtyReserved, o.Qty as QtyOrdered," String sql = "SELECT s.QtyOnHand, s.QtyReserved, s.QtyOrdered,"
+ " productAttribute(s.M_AttributeSetInstance_ID), s.M_AttributeSetInstance_ID,"; + " productAttribute(s.M_AttributeSetInstance_ID), s.M_AttributeSetInstance_ID,";
if (!showDetail) if (!showDetail)
sql = "SELECT SUM(s.QtyOnHand), SUM(r.Qty), SUM(o.Qty)," sql = "SELECT SUM(s.QtyOnHand), SUM(s.QtyReserved), SUM(s.QtyOrdered),"
+ " productAttribute(s.M_AttributeSetInstance_ID), 0,"; + " productAttribute(s.M_AttributeSetInstance_ID), 0,";
sql += " w.Name, l.Value " sql += " w.Name, l.Value "
+ "FROM M_StorageOnHand s" + "FROM M_Storage s"
+ " INNER JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)" + " INNER JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)"
+ " INNER JOIN M_Warehouse w ON (l.M_Warehouse_ID=w.M_Warehouse_ID) " + " INNER JOIN M_Warehouse w ON (l.M_Warehouse_ID=w.M_Warehouse_ID) "
+ " LEFT JOIN M_StorageReservation r ON (s.M_Product_ID=r.M_Product_ID AND w.M_Warehouse_ID=r.M_Warehouse_ID AND r.IsSOTrx='Y') " + "WHERE M_Product_ID=?";
+ " LEFT JOIN M_StorageReservation o ON (s.M_Product_ID=o.M_Product_ID AND w.M_Warehouse_ID=o.M_Warehouse_ID AND o.IsSOTrx='N') "
+ "WHERE s.M_Product_ID=?";
if (m_M_Warehouse_ID != 0) if (m_M_Warehouse_ID != 0)
sql += " AND l.M_Warehouse_ID=?"; sql += " AND l.M_Warehouse_ID=?";
if (m_M_AttributeSetInstance_ID > 0) if (m_M_AttributeSetInstance_ID > 0)
sql += " AND s.M_AttributeSetInstance_ID=?"; sql += " AND s.M_AttributeSetInstance_ID=?";
sql += " AND (s.QtyOnHand<>0 OR r.Qty<>0 OR o.Qty<>0)"; sql += " AND (s.QtyOnHand<>0 OR s.QtyReserved<>0 OR s.QtyOrdered<>0)";
if (!showDetail) if (!showDetail)
sql += " GROUP BY productAttribute(s.M_AttributeSetInstance_ID), w.Name, l.Value"; sql += " GROUP BY productAttribute(s.M_AttributeSetInstance_ID), w.Name, l.Value";
sql += " ORDER BY l.Value"; sql += " ORDER BY l.Value";

View File

@ -566,7 +566,7 @@ public class InvoiceHistory extends CDialog
sql = "SELECT SUM(s.QtyOnHand), SUM(s.QtyReserved), SUM(s.QtyOrdered)," sql = "SELECT SUM(s.QtyOnHand), SUM(s.QtyReserved), SUM(s.QtyOrdered),"
+ " productAttribute(s.M_AttributeSetInstance_ID), 0,"; + " productAttribute(s.M_AttributeSetInstance_ID), 0,";
sql += " w.Name, l.Value " sql += " w.Name, l.Value "
+ "FROM M_StorageOnHand s" + "FROM M_Storage s"
+ " INNER JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)" + " INNER JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)"
+ " INNER JOIN M_Warehouse w ON (l.M_Warehouse_ID=w.M_Warehouse_ID) " + " INNER JOIN M_Warehouse w ON (l.M_Warehouse_ID=w.M_Warehouse_ID) "
+ "WHERE M_Product_ID=?"; + "WHERE M_Product_ID=?";

View File

@ -178,7 +178,7 @@ public class PAttributeInstance extends CDialog
new ColumnInfo(Msg.translate(Env.getCtx(), "ShelfLifeRemainingPct"), "CASE WHEN p.GuaranteeDays > 0 THEN TRUNC(((daysbetween(asi.GuaranteeDate, SYSDATE))/p.GuaranteeDays)*100) ELSE 0 END", Integer.class), new ColumnInfo(Msg.translate(Env.getCtx(), "ShelfLifeRemainingPct"), "CASE WHEN p.GuaranteeDays > 0 THEN TRUNC(((daysbetween(asi.GuaranteeDate, SYSDATE))/p.GuaranteeDays)*100) ELSE 0 END", Integer.class),
}; };
/** From Clause */ /** From Clause */
private static String s_sqlFrom = "M_StorageOnHand s" private static String s_sqlFrom = "M_Storage s"
+ " INNER JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)" + " INNER JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)"
+ " INNER JOIN M_Product p ON (s.M_Product_ID=p.M_Product_ID)" + " INNER JOIN M_Product p ON (s.M_Product_ID=p.M_Product_ID)"
+ " LEFT OUTER JOIN M_AttributeSetInstance asi ON (s.M_AttributeSetInstance_ID=asi.M_AttributeSetInstance_ID)"; + " LEFT OUTER JOIN M_AttributeSetInstance asi ON (s.M_AttributeSetInstance_ID=asi.M_AttributeSetInstance_ID)";

View File

@ -456,7 +456,7 @@ public class VLocator extends JComponent
sql.append(" AND (IsDefault='Y' ") // Default Locator sql.append(" AND (IsDefault='Y' ") // Default Locator
.append("OR EXISTS (SELECT * FROM M_Product p ") // Product Locator .append("OR EXISTS (SELECT * FROM M_Product p ") // Product Locator
.append("WHERE p.M_Locator_ID=M_Locator.M_Locator_ID AND p.M_Product_ID=?)") .append("WHERE p.M_Locator_ID=M_Locator.M_Locator_ID AND p.M_Product_ID=?)")
.append("OR EXISTS (SELECT * FROM M_StorageOnHand s ") // Storage Locator .append("OR EXISTS (SELECT * FROM M_Storage s ") // Storage Locator
.append("WHERE s.M_Locator_ID=M_Locator.M_Locator_ID AND s.M_Product_ID=?))"); .append("WHERE s.M_Locator_ID=M_Locator.M_Locator_ID AND s.M_Product_ID=?))");
String finalSql = MRole.getDefault(Env.getCtx(), false).addAccessSQL( String finalSql = MRole.getDefault(Env.getCtx(), false).addAccessSQL(
sql.toString(), "M_Locator", MRole.SQL_NOTQUALIFIED, MRole.SQL_RO); sql.toString(), "M_Locator", MRole.SQL_NOTQUALIFIED, MRole.SQL_RO);

View File

@ -166,7 +166,7 @@ public class InfoPAttributeInstancePanel extends Window implements EventListener
new ColumnInfo(Msg.translate(Env.getCtx(), "ShelfLifeRemainingPct"), "CASE WHEN p.GuaranteeDays > 0 THEN TRUNC(((daysbetween(asi.GuaranteeDate, SYSDATE))/p.GuaranteeDays)*100) ELSE 0 END", Integer.class), new ColumnInfo(Msg.translate(Env.getCtx(), "ShelfLifeRemainingPct"), "CASE WHEN p.GuaranteeDays > 0 THEN TRUNC(((daysbetween(asi.GuaranteeDate, SYSDATE))/p.GuaranteeDays)*100) ELSE 0 END", Integer.class),
}; };
/** From Clause */ /** From Clause */
private static String s_sqlFrom = "M_StorageOnHand s" private static String s_sqlFrom = "M_Storage s"
+ " INNER JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)" + " INNER JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)"
+ " INNER JOIN M_Product p ON (s.M_Product_ID=p.M_Product_ID)" + " INNER JOIN M_Product p ON (s.M_Product_ID=p.M_Product_ID)"
+ " LEFT OUTER JOIN M_AttributeSetInstance asi ON (s.M_AttributeSetInstance_ID=asi.M_AttributeSetInstance_ID)"; + " LEFT OUTER JOIN M_AttributeSetInstance asi ON (s.M_AttributeSetInstance_ID=asi.M_AttributeSetInstance_ID)";

View File

@ -614,7 +614,7 @@ public class InfoPAttributePanel extends Window implements EventListener
// finish Instance Attributes // finish Instance Attributes
if (sb.length() > 0) if (sb.length() > 0)
{ {
sb.insert(0, " AND EXISTS (SELECT * FROM M_StorageOnHand s" sb.insert(0, " AND EXISTS (SELECT * FROM M_Storage s"
+ " INNER JOIN M_AttributeSetInstance asi ON (s.M_AttributeSetInstance_ID=asi.M_AttributeSetInstance_ID) " + " INNER JOIN M_AttributeSetInstance asi ON (s.M_AttributeSetInstance_ID=asi.M_AttributeSetInstance_ID) "
+ "WHERE s.M_Product_ID=p.M_Product_ID"); + "WHERE s.M_Product_ID=p.M_Product_ID");
sb.append(")"); sb.append(")");

View File

@ -1453,7 +1453,7 @@ public class InfoProductPanel extends InfoPanel implements EventListener
sql = "SELECT SUM(s.QtyOnHand), SUM(s.QtyReserved), SUM(s.QtyOrdered)," sql = "SELECT SUM(s.QtyOnHand), SUM(s.QtyReserved), SUM(s.QtyOrdered),"
+ " productAttribute(s.M_AttributeSetInstance_ID), 0,"; + " productAttribute(s.M_AttributeSetInstance_ID), 0,";
sql += " w.Name, l.Value " sql += " w.Name, l.Value "
+ "FROM M_StorageOnHand s" + "FROM M_Storage s"
+ " INNER JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)" + " INNER JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)"
+ " INNER JOIN M_Warehouse w ON (l.M_Warehouse_ID=w.M_Warehouse_ID) " + " INNER JOIN M_Warehouse w ON (l.M_Warehouse_ID=w.M_Warehouse_ID) "
+ "WHERE M_Product_ID=?"; + "WHERE M_Product_ID=?";

View File

@ -599,7 +599,7 @@ public class InvoiceHistory extends Window implements EventListener
sql = "SELECT SUM(s.QtyOnHand), SUM(s.QtyReserved), SUM(s.QtyOrdered)," sql = "SELECT SUM(s.QtyOnHand), SUM(s.QtyReserved), SUM(s.QtyOrdered),"
+ " productAttribute(s.M_AttributeSetInstance_ID), 0,"; + " productAttribute(s.M_AttributeSetInstance_ID), 0,";
sql += " w.Name, l.Value " sql += " w.Name, l.Value "
+ "FROM M_StorageOnHand s" + "FROM M_Storage s"
+ " INNER JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)" + " INNER JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)"
+ " INNER JOIN M_Warehouse w ON (l.M_Warehouse_ID=w.M_Warehouse_ID) " + " INNER JOIN M_Warehouse w ON (l.M_Warehouse_ID=w.M_Warehouse_ID) "
+ "WHERE M_Product_ID=?"; + "WHERE M_Product_ID=?";

View File

@ -173,7 +173,7 @@ public class WPAttributeInstance extends Window implements EventListener
new ColumnInfo(Msg.translate(Env.getCtx(), "ShelfLifeRemainingPct"), "CASE WHEN p.GuaranteeDays > 0 THEN TRUNC(((daysbetween(asi.GuaranteeDate, SYSDATE))/p.GuaranteeDays)*100) ELSE 0 END", Integer.class), new ColumnInfo(Msg.translate(Env.getCtx(), "ShelfLifeRemainingPct"), "CASE WHEN p.GuaranteeDays > 0 THEN TRUNC(((daysbetween(asi.GuaranteeDate, SYSDATE))/p.GuaranteeDays)*100) ELSE 0 END", Integer.class),
}; };
/** From Clause */ /** From Clause */
private static String s_sqlFrom = "M_StorageOnHand s" private static String s_sqlFrom = "M_Storage s"
+ " INNER JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)" + " INNER JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)"
+ " INNER JOIN M_Product p ON (s.M_Product_ID=p.M_Product_ID)" + " INNER JOIN M_Product p ON (s.M_Product_ID=p.M_Product_ID)"
+ " LEFT OUTER JOIN M_AttributeSetInstance asi ON (s.M_AttributeSetInstance_ID=asi.M_AttributeSetInstance_ID)"; + " LEFT OUTER JOIN M_AttributeSetInstance asi ON (s.M_AttributeSetInstance_ID=asi.M_AttributeSetInstance_ID)";

View File

@ -32,6 +32,7 @@ import org.compiere.model.MMatchPO;
import org.compiere.model.MOrderLine; import org.compiere.model.MOrderLine;
import org.compiere.model.MRole; import org.compiere.model.MRole;
import org.compiere.model.MStorageOnHand; import org.compiere.model.MStorageOnHand;
import org.compiere.model.MStorageReservation;
import org.compiere.process.DocumentEngine; import org.compiere.process.DocumentEngine;
import org.compiere.util.CLogger; import org.compiere.util.CLogger;
import org.compiere.util.DB; import org.compiere.util.DB;
@ -491,11 +492,10 @@ public class Match
success = true; success = true;
// Correct Ordered Qty for Stocked Products (see MOrder.reserveStock / MInOut.processIt) // Correct Ordered Qty for Stocked Products (see MOrder.reserveStock / MInOut.processIt)
if (sLine.getProduct() != null && sLine.getProduct().isStocked()) if (sLine.getProduct() != null && sLine.getProduct().isStocked())
success = MStorageOnHand.add (Env.getCtx(), sLine.getM_Warehouse_ID(), success = MStorageReservation.add (Env.getCtx(), sLine.getM_Warehouse_ID(),
sLine.getM_Locator_ID(),
sLine.getM_Product_ID(), sLine.getM_Product_ID(),
sLine.getM_AttributeSetInstance_ID(), oLine.getM_AttributeSetInstance_ID(), sLine.getM_AttributeSetInstance_ID(), oLine.getM_AttributeSetInstance_ID(),
null, trxName); qty.negate(), false, trxName);
} }
} }
else else