diff --git a/base/src/org/adempiere/process/AllocateSalesOrders.java b/base/src/org/adempiere/process/AllocateSalesOrders.java new file mode 100644 index 0000000000..d1f6bacc92 --- /dev/null +++ b/base/src/org/adempiere/process/AllocateSalesOrders.java @@ -0,0 +1,227 @@ +package org.adempiere.process; + +import java.math.BigDecimal; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Iterator; +import java.util.Properties; +import java.util.Vector; +import java.util.logging.Level; + +import org.compiere.model.MOrderLine; +import org.compiere.model.MOrgInfo; +import org.compiere.model.MProduct; +import org.compiere.model.MClientInfo; +import org.compiere.model.MWarehouse; +import org.compiere.process.ProcessInfoParameter; +import org.compiere.process.SvrProcess; +import org.compiere.util.DB; +import org.compiere.util.Env; + +/** + * This process finds all complete sales orders that has physical items yet to + * deliver and tries to allocate items from the items on hand. + * + * @author daniel.tamm + * + */ +public class AllocateSalesOrders extends SvrProcess { + + private int m_warehouseId; + + public static class StockInfo { + + public int productId; + public BigDecimal qtyOnHand; + public BigDecimal qtyAvailable; + public BigDecimal qtyReserved; + public BigDecimal qtyAllocated; + + public StockInfo() {} + + } + + private static final String + query = "select C_OrderLine.* from C_OrderLine " + + "JOIN C_Order ON C_OrderLine.C_Order_ID=C_Order.C_Order_ID " + + "JOIN M_Product ON C_OrderLine.M_Product_ID=M_Product.M_Product_ID " + + "where C_Order.IsSOTrx='Y' AND C_Order.DocStatus='CO' AND QtyAllocated<(QtyOrdered-QtyDelivered) " + + "AND M_Product.M_Product_ID=? " + + "order by PriorityRule, C_OrderLine.Created "; + + + @Override + protected void prepare() { + + m_warehouseId = 1000000; // TODO: Should be mandatory in the process definition + + ProcessInfoParameter[] para = getParameter(); + for (int i = 0; i < para.length; i++) { + String name = para[i].getParameterName(); + if (para[i].getParameter() == null); + else if (name.equals("M_Warehouse_ID")) { + m_warehouseId = para[i].getParameterAsInt(); + } else { + log.log(Level.SEVERE, "Unknown Parameter: " + name); + } + } + + } + + /** + * Finds all order lines that contains not yet delivered physical items of a specific product. + * + * @param conn An open connection. + * @param productId The product id being allocated + * @return Order lines to allocate products to. + * @throws SQLException + */ + public static Vector getOrderLinesToAllocate(Connection conn, int productId, String trxName) throws SQLException { + Vector result = new Vector(); + Properties ctx = Env.getCtx(); + MOrderLine line; + PreparedStatement ps = conn.prepareStatement(query); + ps.setInt(1, productId); + ResultSet rs = ps.executeQuery(); + while(rs.next()) { + line = new MOrderLine(ctx, rs, trxName); + result.add(line); + } + rs.close(); + ps.close(); + return(result); + } + + /** + * Finds all products that can be allocated. A product can be allocated if there are more items + * on hand than what is already allocated. To be allocated the item must also be in demand + * (reserved < allocated) + * + * @param conn + * @return + * @throws SQLException + */ + public static Vector getProductsToAllocate(Connection conn, int WarehouseID) throws SQLException { + + Vector result = new Vector(); + StockInfo si; + String query1 = "select M_Product_ID, sum(qtyonhand), sum(qtyreserved), sum(m_Product_Stock_v.qtyallocated) " + + "from M_Product_Stock_v " + + "WHERE M_Warehouse_ID=? AND M_Product_ID in " + + "(select DISTINCT C_OrderLine.M_Product_ID FROM C_OrderLine " + + "JOIN C_Order ON C_OrderLine.C_Order_ID=C_Order.C_Order_ID " + + "JOIN M_Product ON C_OrderLine.M_Product_ID=M_Product.M_Product_ID " + + "JOIN M_Product_Stock_v ON C_OrderLine.M_Product_ID=M_Product_Stock_v.M_Product_ID " + + "WHERE " + + "C_Order.IsSOTrx='Y' AND C_Order.DocStatus='CO' AND C_OrderLine.M_Warehouse_ID=? AND " + + "(QtyOrdered-QtyDelivered)>0 AND (QtyOrdered-QtyDelivered)>C_OrderLine.QtyAllocated)" + + "group by M_Product_ID " + + "order by M_Product_ID"; + + PreparedStatement ps = conn.prepareStatement(query1); + ps.setInt(1, WarehouseID); + ps.setInt(2, WarehouseID); + ResultSet rs = ps.executeQuery(); + + while(rs.next()) { + si = new StockInfo(); + si.productId = rs.getInt(1); + si.qtyOnHand = rs.getBigDecimal(2); + si.qtyReserved = rs.getBigDecimal(3); + si.qtyAvailable = si.qtyOnHand.subtract(si.qtyReserved); + si.qtyAllocated = rs.getBigDecimal(4); + result.add(si); + } + rs.close(); + ps.close(); + return(result); + } + + @Override + protected String doIt() throws Exception { + + Connection conn = DB.getConnectionRO(); + Vector products = AllocateSalesOrders.getProductsToAllocate(conn, m_warehouseId); + conn.close(); + Vector lines; + MOrderLine line; + BigDecimal lineAllocate; + BigDecimal toAllocate; + BigDecimal onHand; + BigDecimal allocated; + BigDecimal qtyAvailable; + BigDecimal willAllocate; + StockInfo si; + + // Make sure we have settings that needs allocation + MWarehouse warehouse = new MWarehouse(getCtx(), m_warehouseId, get_TrxName()); + MOrgInfo orgInfo = MOrgInfo.get(getCtx(), warehouse.getAD_Org_ID(), get_TrxName()); + if (!orgInfo.getDeliveryPolicy().equals(MClientInfo.DELIVERY_POLICY_STRICT_ORDER)) { + return "The current delivery policy of the warehouse doesn't use allocation."; + } + + if (products.size()==0) { + log.info("There are no products to allocate."); + return ""; + } + + /** Iterate through all products to allocate */ + for (Iterator it = products.iterator(); it.hasNext();) { + + MProduct product = null; + si = it.next(); + conn = DB.getConnectionRO(); + // Get all lines to allocate + lines = AllocateSalesOrders.getOrderLinesToAllocate(conn, si.productId, get_TrxName()); + conn.close(); + + // Check if there are any lines to allocate + // and create a log. + if (lines.size()>0) { + product = lines.get(0).getProduct(); + line = lines.get(0); + qtyAvailable = si.qtyAvailable; + onHand = si.qtyOnHand; + // TO allocate = Min (qtyOnHand - qtyAllocated, qtyReserved - qtyAllocated) + toAllocate = si.qtyOnHand.subtract(si.qtyAllocated).min(si.qtyReserved.subtract(si.qtyAllocated)); + if (toAllocate.signum()>0) { + log.info("Allocating " + product.getValue() + " : " + product.getName() + " Avail: " + qtyAvailable + " On hand: " + onHand + " To alloc: " + toAllocate); + log.info(lines.size() + " lines to allocate."); + } else { + continue; + } + } else { + continue; + } + + allocated = BigDecimal.ZERO; + + // When we are here we know what product, qty available and we have the lines + // that need to be allocated. + for (Iterator it2 = lines.iterator(); it2.hasNext(); ) { + line = it2.next(); + + // Calculate what to allocate (what we want) + lineAllocate = line.getQtyOrdered().subtract(line.getQtyDelivered()).subtract(line.getQtyAllocated()); + willAllocate = lineAllocate.min(toAllocate); + if (willAllocate.signum()==1) { + willAllocate = line.allocateOnHand(willAllocate, get_TrxName()); + allocated = allocated.add(willAllocate); + toAllocate = toAllocate.subtract(willAllocate); + log.info("Allocated " + willAllocate + " to order " + line.getC_Order().getDocumentNo() + " " + toAllocate + " left to allocate."); + if (toAllocate.equals(BigDecimal.ZERO)) + break; + } else { + log.info("Skipping allocation of order " + line.getC_Order().getDocumentNo()); + continue; + } + } + + } + + return(""); + } + +} diff --git a/base/src/org/adempiere/validator/MaterialReceiptModelValidator.java b/base/src/org/adempiere/validator/MaterialReceiptModelValidator.java new file mode 100644 index 0000000000..c453f5f48c --- /dev/null +++ b/base/src/org/adempiere/validator/MaterialReceiptModelValidator.java @@ -0,0 +1,131 @@ +package org.adempiere.validator; + +import java.math.BigDecimal; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Iterator; +import java.util.Vector; + +import org.adempiere.process.AllocateSalesOrders; +import org.compiere.model.MClient; +import org.compiere.model.MInOut; +import org.compiere.model.MInOutLine; +import org.compiere.model.MOrderLine; +import org.compiere.model.ModelValidationEngine; +import org.compiere.model.ModelValidator; +import org.compiere.model.PO; +import org.compiere.util.CLogger; +import org.compiere.util.Trx; + +/** + * Validator that allocates products as soon as they hit the warehouse. + * Not yet activated but will be in upcoming revisions. + * + * @author Daniel Tamm + * + */ +public class MaterialReceiptModelValidator implements ModelValidator { + + private static final CLogger logger = CLogger.getCLogger(MaterialReceiptModelValidator.class); + + private int ad_Client_ID = -1; + + @Override + public int getAD_Client_ID() { + return ad_Client_ID; + } + + @Override + public void initialize(ModelValidationEngine engine, MClient client) { + + if (client != null) { + ad_Client_ID = client.getAD_Client_ID(); + } + engine.addDocValidate(MInOut.Table_Name, this); + } + + @Override + public String docValidate(PO po, int timing) { + if (timing == ModelValidator.TIMING_AFTER_COMPLETE) { + MInOut io = (MInOut)po; + // We only allocate incoming material + if (!io.isSOTrx()) { + MInOutLine[] lines = io.getLines(); + MInOutLine line; + + try { + String trxName = Trx.createTrxName(); + Trx trx = Trx.get(trxName, true); + Connection conn = trx.getConnection(); + + for (int i=0; i0) { + + Vector lines = AllocateSalesOrders.getOrderLinesToAllocate(conn, iol.getProduct().get_ID(), trxName); + + MOrderLine line; + BigDecimal receivedQty = iol.getMovementQty(); + BigDecimal toAllocate; + BigDecimal willAllocate; + + for (Iterator it = lines.iterator(); it.hasNext(); ) { + line = it.next(); + // Calculate what to allocate (what we want) + toAllocate = line.getQtyOrdered().subtract(line.getQtyDelivered()); + // What we got (minimum of what was received and what we want) + willAllocate = toAllocate.min(receivedQty); + line.setQtyAllocated(willAllocate); + line.saveEx(trxName); + logger.info("Allocated " + willAllocate + " to order " + line.getC_Order().getDocumentNo()); + receivedQty = receivedQty.subtract(willAllocate); + if (receivedQty.equals(BigDecimal.ZERO)) + break; + } + } + + } + + + public String login(final int AD_Org_ID, final int AD_Role_ID, + final int AD_User_ID) { + + logger.info("AD_Org_ID=" + AD_Org_ID + "; AD_Role_ID=" + AD_Role_ID + + "; AD_User_ID=" + AD_User_ID); + + return null; + } + + @Override + public String modelChange(PO po, int type) throws Exception { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/base/src/org/compiere/model/I_AD_ClientInfo.java b/base/src/org/compiere/model/I_AD_ClientInfo.java index 219b018831..789bd1a72a 100644 --- a/base/src/org/compiere/model/I_AD_ClientInfo.java +++ b/base/src/org/compiere/model/I_AD_ClientInfo.java @@ -273,6 +273,21 @@ public interface I_AD_ClientInfo public I_C_UOM getC_UOM_Time() throws RuntimeException; + /** Column name C_UOM_Weight_ID */ + public static final String COLUMNNAME_C_UOM_Weight_ID = "C_UOM_Weight_ID"; + + /** Set UOM for Weight. + * Standard Unit of Measure for Weight + */ + public void setC_UOM_Weight_ID (int C_UOM_Weight_ID); + + /** Get UOM for Weight. + * Standard Unit of Measure for Weight + */ + public int getC_UOM_Weight_ID(); + + public I_C_UOM getC_UOM_Weight() throws RuntimeException; + /** Column name C_UOM_Volume_ID */ public static final String COLUMNNAME_C_UOM_Volume_ID = "C_UOM_Volume_ID"; @@ -288,20 +303,18 @@ public interface I_AD_ClientInfo public I_C_UOM getC_UOM_Volume() throws RuntimeException; - /** Column name C_UOM_Weight_ID */ - public static final String COLUMNNAME_C_UOM_Weight_ID = "C_UOM_Weight_ID"; + /** Column name DeliveryPolicy */ + public static final String COLUMNNAME_DeliveryPolicy = "DeliveryPolicy"; - /** Set UOM for Weight. - * Standard Unit of Measure for Weight + /** Set Delivery Policy. + * Delivery Policy */ - public void setC_UOM_Weight_ID (int C_UOM_Weight_ID); + public void setDeliveryPolicy (String DeliveryPolicy); - /** Get UOM for Weight. - * Standard Unit of Measure for Weight + /** Get Delivery Policy. + * Delivery Policy */ - public int getC_UOM_Weight_ID(); - - public I_C_UOM getC_UOM_Weight() throws RuntimeException; + public String getDeliveryPolicy(); /** Column name IsActive */ public static final String COLUMNNAME_IsActive = "IsActive"; diff --git a/base/src/org/compiere/model/I_AD_OrgInfo.java b/base/src/org/compiere/model/I_AD_OrgInfo.java index 2160b3249d..615a7fbcb5 100644 --- a/base/src/org/compiere/model/I_AD_OrgInfo.java +++ b/base/src/org/compiere/model/I_AD_OrgInfo.java @@ -123,6 +123,19 @@ public interface I_AD_OrgInfo */ public int getCreatedBy(); + /** Column name DeliveryPolicy */ + public static final String COLUMNNAME_DeliveryPolicy = "DeliveryPolicy"; + + /** Set Delivery Policy. + * Delivery Policy + */ + public void setDeliveryPolicy (String DeliveryPolicy); + + /** Get Delivery Policy. + * Delivery Policy + */ + public String getDeliveryPolicy(); + /** Column name DropShip_Warehouse_ID */ public static final String COLUMNNAME_DropShip_Warehouse_ID = "DropShip_Warehouse_ID"; diff --git a/base/src/org/compiere/model/I_C_OrderLine.java b/base/src/org/compiere/model/I_C_OrderLine.java index 25cf34f712..ab7cfc0126 100644 --- a/base/src/org/compiere/model/I_C_OrderLine.java +++ b/base/src/org/compiere/model/I_C_OrderLine.java @@ -602,6 +602,19 @@ public interface I_C_OrderLine */ public boolean isProcessed(); + /** Column name QtyAllocated */ + public static final String COLUMNNAME_QtyAllocated = "QtyAllocated"; + + /** Set Qty Allocated. + * Allocated quantity + */ + public void setQtyAllocated (BigDecimal QtyAllocated); + + /** Get Qty Allocated. + * Allocated quantity + */ + public BigDecimal getQtyAllocated(); + /** Column name QtyDelivered */ public static final String COLUMNNAME_QtyDelivered = "QtyDelivered"; diff --git a/base/src/org/compiere/model/I_M_Storage.java b/base/src/org/compiere/model/I_M_Storage.java index 414c41c2fb..a7c32c83f1 100644 --- a/base/src/org/compiere/model/I_M_Storage.java +++ b/base/src/org/compiere/model/I_M_Storage.java @@ -149,6 +149,19 @@ public interface I_M_Storage public I_M_Product getM_Product() throws RuntimeException; + /** Column name QtyAllocated */ + public static final String COLUMNNAME_QtyAllocated = "QtyAllocated"; + + /** Set Qty Allocated. + * Allocated quantity + */ + public void setQtyAllocated (BigDecimal QtyAllocated); + + /** Get Qty Allocated. + * Allocated quantity + */ + public BigDecimal getQtyAllocated(); + /** Column name QtyOnHand */ public static final String COLUMNNAME_QtyOnHand = "QtyOnHand"; diff --git a/base/src/org/compiere/model/MClientInfo.java b/base/src/org/compiere/model/MClientInfo.java index 99f4cece79..6a403877d5 100644 --- a/base/src/org/compiere/model/MClientInfo.java +++ b/base/src/org/compiere/model/MClientInfo.java @@ -39,7 +39,9 @@ public class MClientInfo extends X_AD_ClientInfo * */ private static final long serialVersionUID = 4861006368856890116L; - + + public static final String DELIVERY_POLICY_STRICT_ORDER = "O"; + public static final String DELIVERY_POLICY_NO_HOLD = "N"; /** * Get Client Info diff --git a/base/src/org/compiere/model/MInOut.java b/base/src/org/compiere/model/MInOut.java index 3d11b3aed5..2dc9975175 100644 --- a/base/src/org/compiere/model/MInOut.java +++ b/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.process.AllocateSalesOrders; import org.compiere.print.ReportEngine; import org.compiere.process.DocAction; import org.compiere.process.DocumentEngine; @@ -64,6 +65,8 @@ public class MInOut extends X_M_InOut implements DocAction /** * Create Shipment From Order * @param order order + * @param oLines order lines to use when creating the shipment. If null all lines + * from the order is used. * @param movementDate optional movement date * @param forceDelivery ignore order delivery rule * @param allAttributeInstances if true, all attribute set instances @@ -72,10 +75,10 @@ public class MInOut extends X_M_InOut implements DocAction * @param trxName transaction * @return Shipment or null */ - public static MInOut createFrom (MOrder order, Timestamp movementDate, - boolean forceDelivery, boolean allAttributeInstances, Timestamp minGuaranteeDate, - boolean complete, String trxName) - { + public static MInOut createFrom (MOrder order, MOrderLine[] oLines, Timestamp movementDate, + boolean forceDelivery, boolean allAttributeInstances, Timestamp minGuaranteeDate, + boolean complete, String trxName) { + if (order == null) throw new IllegalArgumentException("No Order"); // @@ -88,10 +91,16 @@ public class MInOut extends X_M_InOut implements DocAction MInOut retValue = new MInOut (order, 0, movementDate); retValue.setDocAction(complete ? DOCACTION_Complete : DOCACTION_Prepare); - // Check if we can create the lines - MOrderLine[] oLines = order.getLines(true, "M_Product_ID"); + if (oLines == null) { + oLines = order.getLines(true, "M_Product_ID"); + } for (int i = 0; i < oLines.length; i++) { + if (oLines[i].getC_Order_ID()!=order.get_ID()) { + // If the orderline ID and order ID doesn't match, skip the line + continue; + } + // Calculate how much is left to deliver (ordered - delivered) BigDecimal qty = oLines[i].getQtyOrdered().subtract(oLines[i].getQtyDelivered()); // Nothing to deliver if (qty.signum() == 0) @@ -99,6 +108,7 @@ public class MInOut extends X_M_InOut implements DocAction // Stock Info MStorage[] storages = null; MProduct product = oLines[i].getProduct(); + // If the order line is a product (not a charge) and the product is stocked, find the locators if (product != null && product.get_ID() != 0 && product.isStocked()) { String MMPolicy = product.getMMPolicy(); @@ -106,9 +116,11 @@ public class MInOut extends X_M_InOut implements DocAction oLines[i].getM_Product_ID(), oLines[i].getM_AttributeSetInstance_ID(), minGuaranteeDate, MClient.MMPOLICY_FiFo.equals(MMPolicy), true, 0, trxName); } else { + // If the order is a charge or the product is not stocked, don't try to deliver it. continue; } + // Unless the order is force delivery then check delivery rule if (!forceDelivery) { BigDecimal maxQty = Env.ZERO; @@ -119,9 +131,11 @@ public class MInOut extends X_M_InOut implements DocAction if (maxQty.compareTo(qty) < 0) qty = maxQty; } - else if (DELIVERYRULE_CompleteLine.equals(order.getDeliveryRule())) + else if (DELIVERYRULE_CompleteLine.equals(order.getDeliveryRule()) + || DELIVERYRULE_CompleteOrder.equals(order.getDeliveryRule())) { if (maxQty.compareTo(qty) < 0) + // Not enough to deliver the complete line continue; } } @@ -129,9 +143,12 @@ public class MInOut extends X_M_InOut implements DocAction if (retValue.get_ID() == 0) // not saved yet retValue.save(trxName); // Create a line until qty is reached + // There will be one line per storage (if there items are stored in more than one storage) for (int ll = 0; ll < storages.length; ll++) { + // Set lineQty to what's available in the storage BigDecimal lineQty = storages[ll].getQtyOnHand(); + // If lineQty is more than enough, set lineQty to original qty to be delivered. if (lineQty.compareTo(qty) > 0) lineQty = qty; MInOutLine line = new MInOutLine (retValue); @@ -142,6 +159,7 @@ public class MInOut extends X_M_InOut implements DocAction line.setQtyEntered(lineQty .multiply(oLines[i].getQtyEntered()) .divide(oLines[i].getQtyOrdered(), 12, BigDecimal.ROUND_HALF_UP)); + // Set project ID if any line.setC_Project_ID(oLines[i].getC_Project_ID()); line.save(trxName); // Delivered everything ? @@ -158,6 +176,27 @@ public class MInOut extends X_M_InOut implements DocAction return null; return retValue; + + } + + /** + * Create Shipment From Order (using all order lines) + * @param order order + * @param movementDate optional movement date + * @param forceDelivery ignore order delivery rule + * @param allAttributeInstances if true, all attribute set instances + * @param minGuaranteeDate optional minimum guarantee date if all attribute instances + * @param complete complete document (Process if false, Complete if true) + * @param trxName transaction + * @return Shipment or null + */ + public static MInOut createFrom (MOrder order, Timestamp movementDate, + boolean forceDelivery, boolean allAttributeInstances, Timestamp minGuaranteeDate, + boolean complete, String trxName) + { + // Select all lines of the order + MOrderLine[] oLines = order.getLines(true, "M_Product_ID"); + return createFrom(order, oLines, movementDate, forceDelivery, allAttributeInstances, minGuaranteeDate,complete,trxName); } // createFrom /** @@ -1277,6 +1316,10 @@ public class MInOut extends X_M_InOut implements DocAction } log.info("Line=" + sLine.getLine() + " - Qty=" + sLine.getMovementQty()); + + // Check delivery policy + MOrgInfo orgInfo = MOrgInfo.get(getCtx(), sLine.getAD_Org_ID(), get_TrxName()); + boolean isStrictOrder = MClientInfo.DELIVERY_POLICY_STRICT_ORDER.equalsIgnoreCase(orgInfo.getDeliveryPolicy()); // Stock Movement - Counterpart MOrder.reserveStock if (product != null @@ -1311,10 +1354,13 @@ public class MInOut extends X_M_InOut implements DocAction QtyMA = QtyMA.negate(); BigDecimal reservedDiff = Env.ZERO; BigDecimal orderedDiff = Env.ZERO; + BigDecimal allocatedDiff = Env.ZERO; if (sLine.getC_OrderLine_ID() != 0) { - if (isSOTrx()) + if (isSOTrx()) { reservedDiff = ma.getMovementQty().negate(); + allocatedDiff = ma.getMovementQty().negate(); + } else orderedDiff = ma.getMovementQty().negate(); } @@ -1328,6 +1374,7 @@ public class MInOut extends X_M_InOut implements DocAction QtyMA, sameWarehouse ? reservedDiff : Env.ZERO, sameWarehouse ? orderedDiff : Env.ZERO, + sameWarehouse && isStrictOrder ? allocatedDiff : Env.ZERO, get_TrxName())) { m_processMsg = "Cannot correct Inventory (MA)"; @@ -1340,7 +1387,7 @@ public class MInOut extends X_M_InOut implements DocAction wh.getDefaultLocator().getM_Locator_ID(), sLine.getM_Product_ID(), ma.getM_AttributeSetInstance_ID(), reservationAttributeSetInstance_ID, - Env.ZERO, reservedDiff, orderedDiff, get_TrxName())) + Env.ZERO, reservedDiff, orderedDiff, allocatedDiff, get_TrxName())) { m_processMsg = "Cannot correct Inventory (MA) in order warehouse"; return DocAction.STATUS_Invalid; @@ -1364,13 +1411,14 @@ public class MInOut extends X_M_InOut implements DocAction { BigDecimal reservedDiff = sameWarehouse ? QtySO.negate() : Env.ZERO; BigDecimal orderedDiff = sameWarehouse ? QtyPO.negate(): Env.ZERO; + BigDecimal allocatedDiff = isStrictOrder ? reservedDiff : Env.ZERO; // Fallback: Update Storage - see also VMatch.createMatchRecord if (!MStorage.add(getCtx(), getM_Warehouse_ID(), sLine.getM_Locator_ID(), sLine.getM_Product_ID(), sLine.getM_AttributeSetInstance_ID(), reservationAttributeSetInstance_ID, - Qty, reservedDiff, orderedDiff, get_TrxName())) + Qty, reservedDiff, orderedDiff, allocatedDiff, get_TrxName())) { m_processMsg = "Cannot correct Inventory"; return DocAction.STATUS_Invalid; @@ -1382,7 +1430,7 @@ public class MInOut extends X_M_InOut implements DocAction wh.getDefaultLocator().getM_Locator_ID(), sLine.getM_Product_ID(), sLine.getM_AttributeSetInstance_ID(), reservationAttributeSetInstance_ID, - Env.ZERO, QtySO.negate(), QtyPO.negate(), get_TrxName())) + Env.ZERO, QtySO.negate(), QtyPO.negate(), allocatedDiff, get_TrxName())) { m_processMsg = "Cannot correct Inventory"; return DocAction.STATUS_Invalid; @@ -1412,10 +1460,15 @@ public class MInOut extends X_M_InOut implements DocAction if (isSOTrx() // PO is done by Matching || sLine.getM_Product_ID() == 0) // PO Charges, empty lines { - if (isSOTrx()) + if (isSOTrx()) { oLine.setQtyDelivered(oLine.getQtyDelivered().subtract(Qty)); - else + // Adjust allocated on order line + if (isStrictOrder && product.isStocked()) oLine.setQtyAllocated(oLine.getQtyAllocated().add(Qty)); + } else { oLine.setQtyDelivered(oLine.getQtyDelivered().add(Qty)); + // Adjust allocated on order line + if (isStrictOrder && product.isStocked()) oLine.setQtyAllocated(oLine.getQtyAllocated().subtract(Qty)); + } oLine.setDateDelivered(getMovementDate()); // overwrite=last } if (!oLine.save()) diff --git a/base/src/org/compiere/model/MInventory.java b/base/src/org/compiere/model/MInventory.java index 36755c1f24..7036a6b45e 100644 --- a/base/src/org/compiere/model/MInventory.java +++ b/base/src/org/compiere/model/MInventory.java @@ -431,7 +431,7 @@ public class MInventory extends X_M_Inventory implements DocAction line.getM_Locator_ID(), line.getM_Product_ID(), ma.getM_AttributeSetInstance_ID(), 0, - QtyMA.negate(), Env.ZERO, Env.ZERO, get_TrxName())) + QtyMA.negate(), Env.ZERO, Env.ZERO, Env.ZERO, get_TrxName())) { m_processMsg = "Cannot correct Inventory (MA)"; return DocAction.STATUS_Invalid; @@ -489,7 +489,7 @@ public class MInventory extends X_M_Inventory implements DocAction line.getM_Locator_ID(), line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), 0, - qtyDiff, Env.ZERO, Env.ZERO, get_TrxName())) + qtyDiff, Env.ZERO, Env.ZERO, Env.ZERO, get_TrxName())) { m_processMsg = "Cannot correct Inventory (MA)"; return DocAction.STATUS_Invalid; diff --git a/base/src/org/compiere/model/MMovement.java b/base/src/org/compiere/model/MMovement.java index 482c01995c..fc0ee82960 100644 --- a/base/src/org/compiere/model/MMovement.java +++ b/base/src/org/compiere/model/MMovement.java @@ -403,7 +403,7 @@ public class MMovement extends X_M_Movement implements DocAction line.getM_Locator_ID(), line.getM_Product_ID(), ma.getM_AttributeSetInstance_ID(), 0, - ma.getMovementQty().negate(), Env.ZERO , Env.ZERO , get_TrxName())) + ma.getMovementQty().negate(), Env.ZERO , Env.ZERO , Env.ZERO, get_TrxName())) { m_processMsg = "Cannot correct Inventory (MA)"; return DocAction.STATUS_Invalid; @@ -420,7 +420,7 @@ public class MMovement extends X_M_Movement implements DocAction line.getM_LocatorTo_ID(), line.getM_Product_ID(), M_AttributeSetInstanceTo_ID, 0, - ma.getMovementQty(), Env.ZERO , Env.ZERO , get_TrxName())) + ma.getMovementQty(), Env.ZERO , Env.ZERO , Env.ZERO, get_TrxName())) { m_processMsg = "Cannot correct Inventory (MA)"; return DocAction.STATUS_Invalid; @@ -459,7 +459,7 @@ public class MMovement extends X_M_Movement implements DocAction line.getM_Locator_ID(), line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), 0, - line.getMovementQty().negate(), Env.ZERO , Env.ZERO , get_TrxName())) + line.getMovementQty().negate(), Env.ZERO , Env.ZERO , Env.ZERO, get_TrxName())) { m_processMsg = "Cannot correct Inventory (MA)"; return DocAction.STATUS_Invalid; @@ -470,7 +470,7 @@ public class MMovement extends X_M_Movement implements DocAction line.getM_LocatorTo_ID(), line.getM_Product_ID(), line.getM_AttributeSetInstanceTo_ID(), 0, - line.getMovementQty(), Env.ZERO , Env.ZERO , get_TrxName())) + line.getMovementQty(), Env.ZERO , Env.ZERO , Env.ZERO, get_TrxName())) { m_processMsg = "Cannot correct Inventory (MA)"; return DocAction.STATUS_Invalid; diff --git a/base/src/org/compiere/model/MOrder.java b/base/src/org/compiere/model/MOrder.java index 9220b6363d..12b3a339d7 100644 --- a/base/src/org/compiere/model/MOrder.java +++ b/base/src/org/compiere/model/MOrder.java @@ -1391,7 +1391,7 @@ public class MOrder extends X_C_Order implements DocAction * @param lines order lines (ordered by M_Product_ID for deadlock prevention) * @return true if (un) reserved */ - private boolean reserveStock (MDocType dt, MOrderLine[] lines) + public boolean reserveStock (MDocType dt, MOrderLine[] lines) { if (dt == null) dt = MDocType.get(getCtx(), getC_DocType_ID()); @@ -1415,6 +1415,7 @@ public class MOrder extends X_C_Order implements DocAction BigDecimal Volume = Env.ZERO; BigDecimal Weight = Env.ZERO; + BigDecimal diffQtyAllocated = Env.ZERO; // Always check and (un) Reserve Inventory for (int i = 0; i < lines.length; i++) @@ -1480,15 +1481,28 @@ public class MOrder extends X_C_Order implements DocAction M_Locator_ID = wh.getDefaultLocator().getM_Locator_ID(); } } - // Update Storage + // == Update Storage + // reserve but don't allocate if quantity is increased. + // If quantity is decreased, release allocation. + if (reserved.signum()<0) { + // Decreasing reservation, check if allocation should be released + diffQtyAllocated = target.subtract(line.getQtyAllocated()); + diffQtyAllocated = diffQtyAllocated.min(BigDecimal.ZERO); + } else { + diffQtyAllocated = BigDecimal.ZERO; + } if (!MStorage.add(getCtx(), line.getM_Warehouse_ID(), M_Locator_ID, line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), line.getM_AttributeSetInstance_ID(), - Env.ZERO, reserved, ordered, get_TrxName())) + Env.ZERO, reserved, ordered, diffQtyAllocated, get_TrxName())) return false; + // == End of update storage } // stockec // update line line.setQtyReserved(line.getQtyReserved().add(difference)); + if (diffQtyAllocated.signum()!=0) { + line.setQtyAllocated(line.getQtyAllocated().add(diffQtyAllocated)); + } if (!line.save(get_TrxName())) return false; // diff --git a/base/src/org/compiere/model/MOrderLine.java b/base/src/org/compiere/model/MOrderLine.java index 38179ee506..9736b4f9f3 100644 --- a/base/src/org/compiere/model/MOrderLine.java +++ b/base/src/org/compiere/model/MOrderLine.java @@ -27,6 +27,7 @@ import org.compiere.util.CLogger; import org.compiere.util.DB; import org.compiere.util.Env; import org.compiere.util.Msg; +import org.compiere.util.Trx; /** * Order Line Model. @@ -118,6 +119,7 @@ public class MOrderLine extends X_C_OrderLine return retValue; } // getNotReserved + /** Logger */ private static CLogger s_log = CLogger.getCLogger (MOrderLine.class); @@ -573,6 +575,97 @@ public class MOrderLine extends X_C_OrderLine // We can change return true; } // canChangeWarehouse + + /** + * Allocates at most 'toAllocate' number of items. + * If the toAllocate is a negative number material is unallocated. + * + * @return Number allocated + */ + public BigDecimal allocateOnHand(BigDecimal toAllocate, String trxName) { + + if (BigDecimal.ZERO.equals(toAllocate)) + return BigDecimal.ZERO; + + boolean reverse = toAllocate.signum()<0; + + BigDecimal allocated = BigDecimal.ZERO; + MProduct product = getProduct(); + boolean fifo = MClient.MMPOLICY_FiFo.equals(product.getMMPolicy()); + // Find storages + MStorage[] storages = MStorage.getWarehouse( + getCtx(), + getM_Warehouse_ID(), + getM_Product_ID(), + getM_AttributeSetInstance_ID(), + null, // TODO: Min Guarantee date + fifo, // Material policy + !reverse, // Return positive only unless this is a reverse allocation. + 0, // Optional Locator ID (check all locators) + trxName); + + MStorage s; + BigDecimal unallocated; + BigDecimal allocate; + if (!reverse) { + // Normal allocation + // Iterate through the storages and allocate + for (int i = 0; i0) { + allocate = toAllocate.min(unallocated); // Allocate all unallocated or max of what we want to allocate. + allocated = allocated.add(allocate); + // Update storage + s.setQtyAllocated(s.getQtyAllocated().add(allocate)); + s.saveEx(trxName); // Save storage + // Update order line + setQtyAllocated(getQtyAllocated().add(allocate)); + // Adjust toAllocate + toAllocate = toAllocate.subtract(allocate); + // If what we have allocated now is what we want to allocate then + // exit this loop. + if (toAllocate.signum()<=0) { + break; + } + } + } + } else { + // Reverse allocation + BigDecimal currentlyAllocated; + BigDecimal unallocate; + BigDecimal toUnallocate = toAllocate.abs(); + unallocated = BigDecimal.ZERO; + for (int i = 0; i0) { + unallocate = currentlyAllocated.min(toUnallocate); + unallocated = unallocated.add(unallocate); + // Update storage + s.setQtyAllocated(currentlyAllocated.subtract(unallocate)); + s.saveEx(trxName); + // Update order line + setQtyAllocated(getQtyAllocated().subtract(unallocate)); + // Adjust toUnallocate + toUnallocate = toUnallocate.subtract(unallocate); + // If we have unallocated all, exit this loop + if (toUnallocate.signum()<=0) { + break; + } + } + } + allocated = unallocated.negate(); + } + if (allocated.signum()!=0) { + // Save order line + saveEx(trxName); + } + + return(allocated); + } + /** * Get C_Project_ID @@ -936,9 +1029,18 @@ public class MOrderLine extends X_C_OrderLine } if (Env.ZERO.compareTo(getQtyReserved()) != 0) { + // Unreserve + setQty(BigDecimal.ZERO); + ((MOrder)getC_Order()).reserveStock(null, new MOrderLine[]{this}); // For PO should be On Order - log.saveError("DeleteError", Msg.translate(getCtx(), "QtyReserved") + "=" + getQtyReserved()); - return false; + if (!getQtyReserved().equals(BigDecimal.ZERO)) { + log.saveError("DeleteError", Msg.translate(getCtx(), "QtyReserved") + "=" + getQtyReserved()); + return false; + } + } + if (Env.ZERO.compareTo(getQtyAllocated()) != 0) { + // Unallocate + allocateOnHand(getQtyAllocated().negate(), get_TrxName()); } // UnLink All Requisitions diff --git a/base/src/org/compiere/model/MOrgInfo.java b/base/src/org/compiere/model/MOrgInfo.java index 23293be168..27335c351d 100644 --- a/base/src/org/compiere/model/MOrgInfo.java +++ b/base/src/org/compiere/model/MOrgInfo.java @@ -20,6 +20,7 @@ import java.sql.ResultSet; import java.util.Properties; import org.compiere.util.CCache; +import org.compiere.util.Env; /** * Organization Info Model @@ -100,4 +101,19 @@ public class MOrgInfo extends X_AD_OrgInfo setTaxID ("?"); } // MOrgInfo + /** + * Returns the delivery policy of this organization. If no specific organization is set + * the delivery policy of the client is returned + */ + @Override + public String getDeliveryPolicy() { + String orgDeliveryPolicy = super.getDeliveryPolicy(); + if (orgDeliveryPolicy!=null && orgDeliveryPolicy.trim().length()>0) { + return(orgDeliveryPolicy); + } + // Get the client's delivery policy + MClientInfo info = MClientInfo.get(Env.getCtx(), getAD_Client_ID()); + return(info.getDeliveryPolicy()); + } + } diff --git a/base/src/org/compiere/model/MProjectIssue.java b/base/src/org/compiere/model/MProjectIssue.java index 034f7a4189..c06ae29bd8 100644 --- a/base/src/org/compiere/model/MProjectIssue.java +++ b/base/src/org/compiere/model/MProjectIssue.java @@ -165,7 +165,7 @@ public class MProjectIssue extends X_C_ProjectIssue MLocator loc = MLocator.get(getCtx(), getM_Locator_ID()); if (MStorage.add(getCtx(), loc.getM_Warehouse_ID(), getM_Locator_ID(), getM_Product_ID(), getM_AttributeSetInstance_ID(), getM_AttributeSetInstance_ID(), - getMovementQty().negate(), null, null, get_TrxName())) + getMovementQty().negate(), null, null, null, get_TrxName())) { if (mTrx.save(get_TrxName())) { diff --git a/base/src/org/compiere/model/MStorage.java b/base/src/org/compiere/model/MStorage.java index 215d5bbd50..54d1a23b23 100644 --- a/base/src/org/compiere/model/MStorage.java +++ b/base/src/org/compiere/model/MStorage.java @@ -267,7 +267,7 @@ public class MStorage extends X_M_Storage // Specific Attribute Set Instance 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.QtyOnHand,s.QtyReserved,s.QtyOrdered,s.DateLastInventory " + + "s.QtyOnHand,s.QtyReserved,s.QtyOrdered,s.QtyAllocated,s.DateLastInventory " + "FROM M_Storage s" + " INNER JOIN M_Locator l ON (l.M_Locator_ID=s.M_Locator_ID) "; if (M_Locator_ID > 0) @@ -292,7 +292,7 @@ public class MStorage extends X_M_Storage { 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.QtyOnHand,s.QtyReserved,s.QtyOrdered,s.DateLastInventory " + + "s.QtyOnHand,s.QtyReserved,s.QtyOrdered,s.QtyAllocated,s.DateLastInventory " + "FROM M_Storage s" + " INNER JOIN M_Locator l ON (l.M_Locator_ID=s.M_Locator_ID)" + " LEFT OUTER JOIN M_AttributeSetInstance asi ON (s.M_AttributeSetInstance_ID=asi.M_AttributeSetInstance_ID) "; @@ -413,7 +413,10 @@ public class MStorage extends X_M_Storage public static boolean add (Properties ctx, int M_Warehouse_ID, int M_Locator_ID, int M_Product_ID, int M_AttributeSetInstance_ID, int reservationAttributeSetInstance_ID, BigDecimal diffQtyOnHand, - BigDecimal diffQtyReserved, BigDecimal diffQtyOrdered, String trxName) + BigDecimal diffQtyReserved, + BigDecimal diffQtyOrdered, + BigDecimal diffQtyAllocated, + String trxName) { MStorage storage = null; StringBuffer diffText = new StringBuffer("("); @@ -478,6 +481,15 @@ public class MStorage extends X_M_Storage diffText.append(" Ordered=").append(diffQtyOrdered); changed = true; } + if (diffQtyAllocated !=null && diffQtyAllocated.signum() != 0) + { + if (storage0 == null) + storage.setQtyAllocated(storage.getQtyAllocated().add(diffQtyAllocated)); + else + storage0.setQtyAllocated(storage0.getQtyAllocated().add(diffQtyAllocated)); + diffText.append(" Allocated=").append(diffQtyAllocated); + changed = true; + } if (changed) { diffText.append(") -> ").append(storage.toString()); @@ -611,6 +623,47 @@ public class MStorage extends X_M_Storage return retValue; } // getQtyAvailable + /** + * Get Warehouse/Locator on hand Qty. + * The call is accurate only if there is a storage record + * and assumes that the product is stocked + * @param M_Warehouse_ID wh (if the M_Locator_ID!=0 then M_Warehouse_ID is ignored) + * @param M_Locator_ID locator (if 0, the whole warehouse will be evaluated) + * @param M_Product_ID product + * @param M_AttributeSetInstance_ID masi + * @param trxName transaction + * @return qty on hand (QtyOnHand) or null if error + */ + public static BigDecimal getQtyOnHand (int M_Warehouse_ID, int M_Locator_ID, + int M_Product_ID, int M_AttributeSetInstance_ID, String trxName) + { + ArrayList params = new ArrayList(); + StringBuffer sql = new StringBuffer("SELECT COALESCE(SUM(s.QtyOnHand),0)") + .append(" FROM M_Storage s") + .append(" WHERE s.M_Product_ID=?"); + params.add(M_Product_ID); + // Warehouse level + if (M_Locator_ID == 0) { + sql.append(" AND EXISTS (SELECT 1 FROM M_Locator l WHERE s.M_Locator_ID=l.M_Locator_ID AND l.M_Warehouse_ID=?)"); + params.add(M_Warehouse_ID); + } + // Locator level + else { + sql.append(" AND s.M_Locator_ID=?"); + params.add(M_Locator_ID); + } + // With ASI + if (M_AttributeSetInstance_ID != 0) { + sql.append(" AND s.M_AttributeSetInstance_ID=?"); + params.add(M_AttributeSetInstance_ID); + } + // + BigDecimal retValue = DB.getSQLValueBD(trxName, sql.toString(), params); + if (CLogMgt.isLevelFine()) + s_log.fine("M_Warehouse_ID=" + M_Warehouse_ID + ", M_Locator_ID=" + M_Locator_ID + + ",M_Product_ID=" + M_Product_ID + " = " + retValue); + return retValue; + } // getQtyAvailable /************************************************************************** * Persistency Constructor diff --git a/base/src/org/compiere/model/X_AD_ClientInfo.java b/base/src/org/compiere/model/X_AD_ClientInfo.java index 1329ebe956..3d7d286acb 100644 --- a/base/src/org/compiere/model/X_AD_ClientInfo.java +++ b/base/src/org/compiere/model/X_AD_ClientInfo.java @@ -29,7 +29,7 @@ public class X_AD_ClientInfo extends PO implements I_AD_ClientInfo, I_Persistent /** * */ - private static final long serialVersionUID = 20100614L; + private static final long serialVersionUID = 20100622L; /** Standard Constructor */ public X_AD_ClientInfo (Properties ctx, int AD_ClientInfo_ID, String trxName) @@ -433,6 +433,34 @@ public class X_AD_ClientInfo extends PO implements I_AD_ClientInfo, I_Persistent return ii.intValue(); } + public I_C_UOM getC_UOM_Weight() throws RuntimeException + { + return (I_C_UOM)MTable.get(getCtx(), I_C_UOM.Table_Name) + .getPO(getC_UOM_Weight_ID(), get_TrxName()); } + + /** Set UOM for Weight. + @param C_UOM_Weight_ID + Standard Unit of Measure for Weight + */ + public void setC_UOM_Weight_ID (int C_UOM_Weight_ID) + { + if (C_UOM_Weight_ID < 1) + set_Value (COLUMNNAME_C_UOM_Weight_ID, null); + else + set_Value (COLUMNNAME_C_UOM_Weight_ID, Integer.valueOf(C_UOM_Weight_ID)); + } + + /** Get UOM for Weight. + @return Standard Unit of Measure for Weight + */ + public int getC_UOM_Weight_ID () + { + Integer ii = (Integer)get_Value(COLUMNNAME_C_UOM_Weight_ID); + if (ii == null) + return 0; + return ii.intValue(); + } + public I_C_UOM getC_UOM_Volume() throws RuntimeException { return (I_C_UOM)MTable.get(getCtx(), I_C_UOM.Table_Name) @@ -461,32 +489,28 @@ public class X_AD_ClientInfo extends PO implements I_AD_ClientInfo, I_Persistent return ii.intValue(); } - public I_C_UOM getC_UOM_Weight() throws RuntimeException - { - return (I_C_UOM)MTable.get(getCtx(), I_C_UOM.Table_Name) - .getPO(getC_UOM_Weight_ID(), get_TrxName()); } - - /** Set UOM for Weight. - @param C_UOM_Weight_ID - Standard Unit of Measure for Weight + /** DeliveryPolicy AD_Reference_ID=53355 */ + public static final int DELIVERYPOLICY_AD_Reference_ID=53355; + /** No Hold = N */ + public static final String DELIVERYPOLICY_NoHold = "N"; + /** Strict order = O */ + public static final String DELIVERYPOLICY_StrictOrder = "O"; + /** Set Delivery Policy. + @param DeliveryPolicy + Delivery Policy */ - public void setC_UOM_Weight_ID (int C_UOM_Weight_ID) + public void setDeliveryPolicy (String DeliveryPolicy) { - if (C_UOM_Weight_ID < 1) - set_Value (COLUMNNAME_C_UOM_Weight_ID, null); - else - set_Value (COLUMNNAME_C_UOM_Weight_ID, Integer.valueOf(C_UOM_Weight_ID)); + + set_Value (COLUMNNAME_DeliveryPolicy, DeliveryPolicy); } - /** Get UOM for Weight. - @return Standard Unit of Measure for Weight + /** Get Delivery Policy. + @return Delivery Policy */ - public int getC_UOM_Weight_ID () + public String getDeliveryPolicy () { - Integer ii = (Integer)get_Value(COLUMNNAME_C_UOM_Weight_ID); - if (ii == null) - return 0; - return ii.intValue(); + return (String)get_Value(COLUMNNAME_DeliveryPolicy); } /** Set Discount calculated from Line Amounts. diff --git a/base/src/org/compiere/model/X_AD_OrgInfo.java b/base/src/org/compiere/model/X_AD_OrgInfo.java index 6b241cec30..e84cfcbc97 100644 --- a/base/src/org/compiere/model/X_AD_OrgInfo.java +++ b/base/src/org/compiere/model/X_AD_OrgInfo.java @@ -29,7 +29,7 @@ public class X_AD_OrgInfo extends PO implements I_AD_OrgInfo, I_Persistent /** * */ - private static final long serialVersionUID = 20100614L; + private static final long serialVersionUID = 20100622L; /** Standard Constructor */ public X_AD_OrgInfo (Properties ctx, int AD_OrgInfo_ID, String trxName) @@ -156,6 +156,30 @@ public class X_AD_OrgInfo extends PO implements I_AD_OrgInfo, I_Persistent return ii.intValue(); } + /** DeliveryPolicy AD_Reference_ID=53355 */ + public static final int DELIVERYPOLICY_AD_Reference_ID=53355; + /** No Hold = N */ + public static final String DELIVERYPOLICY_NoHold = "N"; + /** Strict order = O */ + public static final String DELIVERYPOLICY_StrictOrder = "O"; + /** Set Delivery Policy. + @param DeliveryPolicy + Delivery Policy + */ + public void setDeliveryPolicy (String DeliveryPolicy) + { + + set_Value (COLUMNNAME_DeliveryPolicy, DeliveryPolicy); + } + + /** Get Delivery Policy. + @return Delivery Policy + */ + public String getDeliveryPolicy () + { + return (String)get_Value(COLUMNNAME_DeliveryPolicy); + } + public I_M_Warehouse getDropShip_Warehouse() throws RuntimeException { return (I_M_Warehouse)MTable.get(getCtx(), I_M_Warehouse.Table_Name) diff --git a/base/src/org/compiere/model/X_C_OrderLine.java b/base/src/org/compiere/model/X_C_OrderLine.java index 48458226c9..1bb4287a71 100644 --- a/base/src/org/compiere/model/X_C_OrderLine.java +++ b/base/src/org/compiere/model/X_C_OrderLine.java @@ -33,7 +33,7 @@ public class X_C_OrderLine extends PO implements I_C_OrderLine, I_Persistent /** * */ - private static final long serialVersionUID = 20100614L; + private static final long serialVersionUID = 20100622L; /** Standard Constructor */ public X_C_OrderLine (Properties ctx, int C_OrderLine_ID, String trxName) @@ -998,6 +998,26 @@ public class X_C_OrderLine extends PO implements I_C_OrderLine, I_Persistent return false; } + /** Set Qty Allocated. + @param QtyAllocated + Allocated quantity + */ + public void setQtyAllocated (BigDecimal QtyAllocated) + { + set_Value (COLUMNNAME_QtyAllocated, QtyAllocated); + } + + /** Get Qty Allocated. + @return Allocated quantity + */ + public BigDecimal getQtyAllocated () + { + BigDecimal bd = (BigDecimal)get_Value(COLUMNNAME_QtyAllocated); + if (bd == null) + return Env.ZERO; + return bd; + } + /** Set Delivered Quantity. @param QtyDelivered Delivered Quantity diff --git a/base/src/org/compiere/model/X_M_Storage.java b/base/src/org/compiere/model/X_M_Storage.java index 7dfbea1b3d..99f2785293 100644 --- a/base/src/org/compiere/model/X_M_Storage.java +++ b/base/src/org/compiere/model/X_M_Storage.java @@ -32,7 +32,7 @@ public class X_M_Storage extends PO implements I_M_Storage, I_Persistent /** * */ - private static final long serialVersionUID = 20100614L; + private static final long serialVersionUID = 20100622L; /** Standard Constructor */ public X_M_Storage (Properties ctx, int M_Storage_ID, String trxName) @@ -178,6 +178,26 @@ public class X_M_Storage extends PO implements I_M_Storage, I_Persistent return ii.intValue(); } + /** Set Qty Allocated. + @param QtyAllocated + Allocated quantity + */ + public void setQtyAllocated (BigDecimal QtyAllocated) + { + set_Value (COLUMNNAME_QtyAllocated, QtyAllocated); + } + + /** Get Qty Allocated. + @return Allocated quantity + */ + public BigDecimal getQtyAllocated () + { + BigDecimal bd = (BigDecimal)get_Value(COLUMNNAME_QtyAllocated); + if (bd == null) + return Env.ZERO; + return bd; + } + /** Set On Hand Quantity. @param QtyOnHand On Hand Quantity diff --git a/base/src/org/compiere/process/InOutGenerate.java b/base/src/org/compiere/process/InOutGenerate.java index 7cb007e56e..abcbbadc8b 100644 --- a/base/src/org/compiere/process/InOutGenerate.java +++ b/base/src/org/compiere/process/InOutGenerate.java @@ -24,13 +24,17 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.logging.Level; +import org.adempiere.process.AllocateSalesOrders; import org.compiere.model.MClient; +import org.compiere.model.MClientInfo; import org.compiere.model.MInOut; import org.compiere.model.MInOutLine; import org.compiere.model.MOrder; import org.compiere.model.MOrderLine; +import org.compiere.model.MOrgInfo; import org.compiere.model.MProduct; import org.compiere.model.MStorage; +import org.compiere.model.MWarehouse; import org.compiere.util.AdempiereUserError; import org.compiere.util.DB; import org.compiere.util.Env; @@ -75,6 +79,11 @@ public class InOutGenerate extends SvrProcess /** The Query sql */ private String m_sql = null; + /** Strict order flag */ + private boolean m_strictOrder = false; + + /** Warehouse */ + private MWarehouse m_warehouse; /** Storages temp space */ private HashMap m_map = new HashMap(); @@ -89,6 +98,7 @@ public class InOutGenerate extends SvrProcess */ protected void prepare() { + ProcessInfoParameter[] para = getParameter(); for (int i = 0; i < para.length; i++) { @@ -125,6 +135,8 @@ public class InOutGenerate extends SvrProcess if (!DocAction.ACTION_Complete.equals(p_docAction)) p_docAction = DocAction.ACTION_Prepare; } + + } // prepare /** @@ -143,6 +155,11 @@ public class InOutGenerate extends SvrProcess if (p_M_Warehouse_ID == 0) throw new AdempiereUserError("@NotFound@ @M_Warehouse_ID@"); + + // Get client info for warehouse + m_warehouse = new MWarehouse(this.getCtx(), p_M_Warehouse_ID, this.get_TrxName()); + MOrgInfo orgInfo = MOrgInfo.get(getCtx(), m_warehouse.getAD_Org_ID(), get_TrxName()); + m_strictOrder = MClientInfo.DELIVERY_POLICY_STRICT_ORDER.equalsIgnoreCase(orgInfo.getDeliveryPolicy()); if (p_Selection) // VInOutGen { @@ -164,6 +181,10 @@ public class InOutGenerate extends SvrProcess // Open Order Lines with Warehouse + " AND EXISTS (SELECT * FROM C_OrderLine ol " + "WHERE ol.M_Warehouse_ID=?"; // #1 + // And the lines have allocated stock + if (m_strictOrder) { + m_sql += " AND (ol.qtyAllocated>0 OR (SELECT IsShippable(ol.M_Product_ID))='N')"; + } if (p_DatePromised != null) m_sql += " AND TRUNC(ol.DatePromised)<=?"; // #2 m_sql += " AND o.C_Order_ID=ol.C_Order_ID AND ol.QtyOrdered<>ol.QtyDelivered)"; @@ -228,7 +249,11 @@ public class InOutGenerate extends SvrProcess String where = " AND M_Warehouse_ID=" + p_M_Warehouse_ID; if (p_DatePromised != null) where += " AND (TRUNC(DatePromised)<=" + DB.TO_DATE(p_DatePromised, true) - + " OR DatePromised IS NULL)"; + + " OR DatePromised IS NULL)"; + // Strict order + if (m_strictOrder) { + where += " AND (QtyAllocated>0 OR (SELECT IsShippable(M_Product_ID))='N')"; + } // Exclude Auto Delivery if not Force if (!MOrder.DELIVERYRULE_Force.equals(order.getDeliveryRule())) where += " AND (C_OrderLine.M_Product_ID IS NULL" @@ -251,6 +276,9 @@ public class InOutGenerate extends SvrProcess BigDecimal onHand = Env.ZERO; BigDecimal toDeliver = line.getQtyOrdered() .subtract(line.getQtyDelivered()); + + BigDecimal qtyAllocated = (BigDecimal)line.getQtyAllocated(); + MProduct product = line.getProduct(); // Nothing to Deliver if (product != null && toDeliver.signum() == 0) @@ -305,13 +333,14 @@ public class InOutGenerate extends SvrProcess MStorage storage = storages[j]; onHand = onHand.add(storage.getQtyOnHand()); } - boolean fullLine = onHand.compareTo(toDeliver) >= 0 - || toDeliver.signum() < 0; + boolean fullLine = m_strictOrder ? (qtyAllocated.compareTo(toDeliver)>=0 || toDeliver.signum() < 0) + : (onHand.compareTo(toDeliver) >= 0 || toDeliver.signum() < 0); // Complete Order if (completeOrder && !fullLine) { - log.fine("Failed CompleteOrder - OnHand=" + onHand + log.fine("Failed CompleteOrder - OnHand=" + onHand + + " Allocated=" + qtyAllocated + " (Unconfirmed=" + unconfirmedShippedQty + "), ToDeliver=" + toDeliver + " - " + line); completeOrder = false; @@ -323,15 +352,15 @@ public class InOutGenerate extends SvrProcess log.fine("CompleteLine - OnHand=" + onHand + " (Unconfirmed=" + unconfirmedShippedQty + ", ToDeliver=" + toDeliver + " - " + line); - // + // createLine (order, line, toDeliver, storages, false); } // Availability else if (MOrder.DELIVERYRULE_Availability.equals(order.getDeliveryRule()) - && (onHand.signum() > 0 + && (onHand.signum() > 0 && ((m_strictOrder && qtyAllocated.signum() > 0) || !m_strictOrder) || toDeliver.signum() < 0)) { - BigDecimal deliver = toDeliver; + BigDecimal deliver = m_strictOrder ? qtyAllocated : toDeliver; if (deliver.compareTo(onHand) > 0) deliver = onHand; log.fine("Available - OnHand=" + onHand @@ -372,7 +401,11 @@ public class InOutGenerate extends SvrProcess if (line.getM_Warehouse_ID() != p_M_Warehouse_ID) continue; MProduct product = line.getProduct(); - BigDecimal toDeliver = line.getQtyOrdered().subtract(line.getQtyDelivered()); + BigDecimal toDeliver; + if (m_strictOrder && product.isStocked()) + toDeliver = (BigDecimal)line.getQtyAllocated(); + else + toDeliver = line.getQtyOrdered().subtract(line.getQtyDelivered()); // MStorage[] storages = null; if (product != null && product.isStocked()) diff --git a/base/src/org/compiere/process/M_Production_Run.java b/base/src/org/compiere/process/M_Production_Run.java index e5634be136..f89d0d2363 100644 --- a/base/src/org/compiere/process/M_Production_Run.java +++ b/base/src/org/compiere/process/M_Production_Run.java @@ -160,6 +160,7 @@ public class M_Production_Run extends SvrProcess { MovementQty, Env.ZERO, Env.ZERO, + Env.ZERO, get_TrxName())) { raiseError("Cannot correct Inventory", ""); diff --git a/base/src/org/compiere/process/StorageCleanup.java b/base/src/org/compiere/process/StorageCleanup.java index bf82717e6a..26991a3894 100644 --- a/base/src/org/compiere/process/StorageCleanup.java +++ b/base/src/org/compiere/process/StorageCleanup.java @@ -17,16 +17,24 @@ package org.compiere.process; import java.math.BigDecimal; +import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; +import java.sql.Statement; import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Vector; import java.util.logging.Level; +import org.compiere.model.MClient; import org.compiere.model.MLocator; import org.compiere.model.MMovement; import org.compiere.model.MMovementLine; +import org.compiere.model.MProduct; import org.compiere.model.MRefList; import org.compiere.model.MStorage; +import org.compiere.model.Query; import org.compiere.util.DB; import org.compiere.util.Env; @@ -98,15 +106,18 @@ public class StorageCleanup extends SvrProcess PreparedStatement pstmt = null; ResultSet rs = null; int lines = 0; + int allocationLines = 0; try { - pstmt = DB.prepareStatement (sql, get_TrxName()); +/** pstmt = DB.prepareStatement (sql, get_TrxName()); pstmt.setInt(1, Env.getAD_Client_ID(getCtx())); rs = pstmt.executeQuery (); while (rs.next ()) { lines += move (new MStorage(getCtx(), rs, get_TrxName())); - } + } */ + // Clean up allocations + allocationLines = cleanupAllocations(); } catch (Exception e) { @@ -114,13 +125,125 @@ public class StorageCleanup extends SvrProcess } finally { - DB.close(rs, pstmt); +// DB.close(rs, pstmt); rs = null; pstmt = null; } - return "#" + lines; + return "#" + lines + ", # " + allocationLines + " cleared allocations."; } // doIt + /** + * This method cleans up allocations in storages. Currently it only cleans up allocations + * of products where no product is reserved. If there are storages with more than 0 items + * allocated of a specific product and nothing is reserved then the storages' allocation + * is set to zero. + */ + private int cleanupAllocations() throws Exception { + + // TODO: Fix this method. It must be able to both clean up allocations that should + // be zero and storages that are "over allocated". + + // Find all allocations that should be zero + + String query = "SELECT M_Product_ID, M_Warehouse_ID FROM M_Product_Stock_v WHERE " + + "(QtyAllocated>0 and " + + "(select sum(QtyReserved) from M_Product_Stock_v WHERE M_Product_ID=M_Product_ID and M_Warehouse_ID=M_Warehouse_ID)<=0) " + + "OR qtyAllocated<0"; + Statement stmt = DB.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, get_TrxName()); + ResultSet rs = null; + List storages; + int productId, warehouseId; + MProduct product; + MStorage storage; + int adjustmentCounter = 0; +/** + rs = stmt.executeQuery(query); + + while(rs.next()) { + productId = rs.getInt(1); + warehouseId = rs.getInt(2); + product = new MProduct(getCtx(),productId,get_TrxName()); + storages = new Query(getCtx(), MStorage.Table_Name, "M_Product_ID=? and (select M_Warehouse_ID from M_Locator where M_Storage.M_Locator_ID=M_Locator_ID) in (?) and QtyAllocated<>0", get_TrxName()) + .setParameters(new Object[]{productId, warehouseId}) + .list(); + // Iterate through storages + for (Iterator it = storages.iterator(); it.hasNext();) { + storage = it.next(); + storage.setQtyAllocated(Env.ZERO); + storage.saveEx(get_TrxName()); + log.info("Ajusted allocation for product " + product.getValue() + " " + product.getName()); + adjustmentCounter++; + } + } + + + stmt.close(); + rs.close(); +*/ + BigDecimal allocatedStorage; + BigDecimal allocatedOrder; + BigDecimal diff; + BigDecimal tmpQty; +// stmt = DB.createStatement(); + + // Find all storages that have more items allocated than what is allocated on order level + query = "SELECT M_Product_ID, M_Warehouse_ID, QtyAllocated, get_allocated_on_order(M_Product_ID, M_Warehouse_ID) FROM M_Product_Stock_v WHERE " + + "QtyAllocated>(select get_allocated_on_order(M_Product_ID, M_Warehouse_ID))"; + + rs = stmt.executeQuery(query); + String where = "M_Product_ID=? and (select M_Warehouse_ID from M_Locator where M_Storage.M_Locator_ID=M_Locator_ID) in (?) and QtyAllocated<>0"; + String q2 = "select * from M_Storage where " + where; + ResultSet rs2; + PreparedStatement ps; + while(rs.next()) { + productId = rs.getInt(1); + warehouseId = rs.getInt(2); + allocatedStorage = rs.getBigDecimal(3); + allocatedOrder = rs.getBigDecimal(4); + diff = allocatedStorage.subtract(allocatedOrder); + product = new MProduct(getCtx(),productId,get_TrxName()); + + storages = new Query(getCtx(), MStorage.Table_Name, where , get_TrxName()) + .setParameters(new Object[]{productId, warehouseId}) + .list(); + if (storages.size()==0) { + Connection conn = DB.getConnectionRO(); + ps = conn.prepareStatement(q2); + ps.setInt(1, productId); + ps.setInt(2, warehouseId); + rs2 = ps.executeQuery(); + storages = new Vector(); + while(rs2.next()) { + storage = new MStorage(getCtx(), rs2, get_TrxName()); + storages.add(storage); + } + rs2.close(); + ps.close(); + conn.close(); + } + + // Iterate through storages + for (Iterator it = storages.iterator(); it.hasNext() && diff.signum()>0;) { + storage = it.next(); + tmpQty = storage.getQtyAllocated(); + tmpQty = tmpQty.min(diff); + if (tmpQty.signum()!=0) { + storage.setQtyAllocated(storage.getQtyAllocated().subtract(tmpQty)); + storage.saveEx(get_TrxName()); + // Decrease difference + diff = diff.subtract(tmpQty); + log.info("Ajusted allocation for product " + product.getValue() + " " + product.getName() + " Changed: " + tmpQty); + adjustmentCounter++; + } + } + + } + + DB.close(rs, stmt); + return(adjustmentCounter); + } + + /** * Move stock to location * @param target target storage @@ -183,6 +306,7 @@ public class StorageCleanup extends SvrProcess /** * Eliminate Reserved/Ordered + * Allocation is left untouched. * @param target target Storage */ private void eliminateReservation(MStorage target) @@ -217,12 +341,12 @@ public class StorageCleanup extends SvrProcess if (MStorage.add(getCtx(), target.getM_Warehouse_ID(), target.getM_Locator_ID(), target.getM_Product_ID(), target.getM_AttributeSetInstance_ID(), target.getM_AttributeSetInstance_ID(), - Env.ZERO, reserved.negate(), ordered.negate(), get_TrxName())) + Env.ZERO, reserved.negate(), ordered.negate(), Env.ZERO, get_TrxName())) { if (MStorage.add(getCtx(), storage0.getM_Warehouse_ID(), storage0.getM_Locator_ID(), storage0.getM_Product_ID(), storage0.getM_AttributeSetInstance_ID(), storage0.getM_AttributeSetInstance_ID(), - Env.ZERO, reserved, ordered, get_TrxName())) + Env.ZERO, reserved, ordered, Env.ZERO, get_TrxName())) log.info("Reserved=" + reserved + ",Ordered=" + ordered); else log.warning("Failed Storage0 Update"); diff --git a/base/src/org/eevolution/model/MDDOrder.java b/base/src/org/eevolution/model/MDDOrder.java index 037d05db1d..74d82c45fc 100644 --- a/base/src/org/eevolution/model/MDDOrder.java +++ b/base/src/org/eevolution/model/MDDOrder.java @@ -689,17 +689,13 @@ public class MDDOrder extends X_DD_Order implements DocAction { if (is_ValueChanged(columnName)) { - final String whereClause = I_DD_Order.COLUMNNAME_DD_Order_ID + "=?"; - List lines = new Query (getCtx(), I_DD_OrderLine.Table_Name, whereClause, get_TrxName()) - .setParameters(getDD_Order_ID()) - .list(); - - for (MDDOrderLine line : lines) - { - line.set_ValueOfColumn(columnName, get_Value(columnName)); - line.saveEx(); - log.fine(columnName + " Lines -> #" + get_Value(columnName)); - } + String sql = "UPDATE DD_OrderLine ol" + + " SET " + columnName + " =" + + "(SELECT " + columnName + + " FROM DD_Order o WHERE ol.DD_Order_ID=o.DD_Order_ID) " + + "WHERE DD_Order_ID=" + getDD_Order_ID(); + int no = DB.executeUpdate(sql, get_TrxName()); + log.fine(columnName + " Lines -> #" + no); } } // afterSaveSync @@ -843,7 +839,8 @@ public class MDDOrder extends X_DD_Order implements DocAction /** - * Reserve Inventory. + * Reserve Inventory. + * No allocation is done. * Counterpart: MMovement.completeIt() * @param lines distribution order lines (ordered by M_Product_ID for deadlock prevention) * @return true if (un) reserved @@ -886,7 +883,7 @@ public class MDDOrder extends X_DD_Order implements DocAction if (!MStorage.add(getCtx(), locator_to.getM_Warehouse_ID(), locator_to.getM_Locator_ID(), line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), line.getM_AttributeSetInstance_ID(), - Env.ZERO, Env.ZERO , reserved_ordered , get_TrxName())) + Env.ZERO, Env.ZERO , reserved_ordered , Env.ZERO, get_TrxName())) { throw new AdempiereException(); } @@ -894,7 +891,7 @@ public class MDDOrder extends X_DD_Order implements DocAction if (!MStorage.add(getCtx(), locator_from.getM_Warehouse_ID(), locator_from.getM_Locator_ID(), line.getM_Product_ID(), line.getM_AttributeSetInstanceTo_ID(), line.getM_AttributeSetInstance_ID(), - Env.ZERO, reserved_ordered, Env.ZERO , get_TrxName())) + Env.ZERO, reserved_ordered, Env.ZERO , Env.ZERO, get_TrxName())) { throw new AdempiereException(); } diff --git a/client/src/org/compiere/apps/form/Match.java b/client/src/org/compiere/apps/form/Match.java index 88be94851f..fd860d8dd6 100644 --- a/client/src/org/compiere/apps/form/Match.java +++ b/client/src/org/compiere/apps/form/Match.java @@ -469,7 +469,7 @@ public class Match sLine.getM_Locator_ID(), sLine.getM_Product_ID(), sLine.getM_AttributeSetInstance_ID(), oLine.getM_AttributeSetInstance_ID(), - null, null, qty.negate(), trxName); + null, null, qty.negate(), Env.ZERO, trxName); } } else diff --git a/db/ddlutils/oracle/functions/Is_InOut_Candidate_Order.sql b/db/ddlutils/oracle/functions/Is_InOut_Candidate_Order.sql new file mode 100644 index 0000000000..e3f0db21bc --- /dev/null +++ b/db/ddlutils/oracle/functions/Is_InOut_Candidate_Order.sql @@ -0,0 +1,51 @@ +/************************************************************************ + * Function Is_InOut_Candidate_Order - Return Y or N depending if + * this order can be shipped or not. + * Delivery Policy, Shipping rule etc is considered. + * Author: Daniel Tamm (usrdno) +************************************************************************/ +CREATE OR REPLACE FUNCTION is_inout_candidate_order +( + p_order_id IN NUMBER +) + RETURN CHAR AS + + v_lines_ready NUMBER; + v_lines_total NUMBER; + v_deliveryRule CHAR; +BEGIN + + -- Get order info + -- Only orders that are complete, not delivered, delivery rule anything else than manual and is a sales order + -- can be inout candidates + select DeliveryRule INTO v_deliveryRule FROM C_Order WHERE + c_order_id=p_order_id AND + docstatus = 'CO' AND + isdelivered = 'N' AND + deliveryrule <> 'M' AND + (c_doctype_id IN ( SELECT c_doctype.c_doctype_id FROM c_doctype + WHERE c_doctype.docbasetype = 'SOO' AND c_doctype.docsubtypeso NOT IN('ON','OB','WR'))); + + IF v_deliveryRule IS NULL THEN + RETURN 'N'; + END IF; + + IF v_deliveryRule='F' THEN RETURN 'Y'; END IF; -- Force + + -- Check lines + SELECT sum(is_inout_candidate_orderline(c_orderline_id)), sum(1) + INTO v_lines_ready, v_lines_total + FROM c_orderline where c_order_id=p_order_id; + + CASE v_deliveryRule + WHEN 'L' THEN -- Complete line + IF v_lines_ready > 0 THEN RETURN 'Y'; END IF; + WHEN 'A' THEN -- Availability + IF v_lines_ready > 0 THEN RETURN 'Y'; END IF; + WHEN 'O' THEN -- Complete order + IF v_lines_ready = v_lines_total THEN RETURN 'Y'; END IF; + END CASE; + + return 'N'; +END; +/ diff --git a/db/ddlutils/oracle/functions/Is_InOut_Candidate_OrderLine.sql b/db/ddlutils/oracle/functions/Is_InOut_Candidate_OrderLine.sql new file mode 100644 index 0000000000..b3b883694f --- /dev/null +++ b/db/ddlutils/oracle/functions/Is_InOut_Candidate_OrderLine.sql @@ -0,0 +1,128 @@ +/************************************************************************ + * Function Is_InOut_Candidate_OrderLine - Return Y or N depending if + * order line can be shipped or not. + * Delivery Policy, Shipping rule etc is considered. + * Author: Daniel Tamm (usrdno) +************************************************************************/ +CREATE OR REPLACE FUNCTION is_inout_candidate_orderline +( + c_order_line_id IN NUMBER +) + RETURN NUMBER AS + + v_qtyordered NUMBER; + v_qtydelivered NUMBER; + v_qtyallocated NUMBER; + v_qtyonhand NUMBER; + v_qtytodeliver NUMBER; + v_qtyreserved NUMBER; + v_order_id NUMBER; + v_inoutExists NUMBER; + v_warehouse_id NUMBER; + v_product_id NUMBER; + v_orderReady NUMBER; + v_isShippable CHAR; + v_deliveryRule CHAR; + v_deliveryPolicy CHAR; + v_return CHAR; + +BEGIN + SELECT qtyordered, qtydelivered, qtyallocated, qtyreserved, c_order_id, + get_delivery_policy(m_warehouse_id), isshippable(m_product_id), + m_warehouse_id, m_product_id + INTO + v_qtyordered, v_qtydelivered, v_qtyallocated, v_qtyreserved, v_order_id, + v_deliveryPolicy, v_isShippable, + v_warehouse_id, v_product_id + FROM + C_OrderLine where C_OrderLine_ID=c_order_line_id; + + -- If all is already delivered then it's not a candidate + IF v_qtyordered = v_qtydelivered THEN + -- RAISE NOTICE 'All is delivered'; + RETURN 0; + END IF; + + -- Non shippable (ie non physical items) are always inout candidate + IF v_isShippable='N' THEN + -- RAISE NOTICE 'Non physical item, always deliverable'; + RETURN 1; + END IF; + + SELECT 1 INTO v_inoutExists FROM m_inoutline iol + JOIN m_inout io ON iol.m_inout_id = io.m_inout_id + WHERE iol.c_orderline_id = c_order_line_id AND (io.docstatus IN ('IP', 'WC', 'IN')); + + -- If an in-out line is in progress this is not a candidate + IF v_inoutExists = 1 THEN + -- RAISE NOTICE 'Already being shipped'; + RETURN 0; + END IF; + + -- Check delivery rule + SELECT DeliveryRule INTO + v_deliveryRule + FROM + C_Order where C_Order_ID=v_order_id; + + IF v_deliveryRule='F' THEN + -- RAISE NOTICE 'Delivery rule = Force'; + RETURN 1; + END IF; -- Force + + v_qtytodeliver := v_qtyordered - v_qtydelivered; + IF v_qtytodeliver = 0 THEN + -- RAISE NOTICE 'Nothing to deliver'; + RETURN 0; + END IF; + + IF v_DeliveryPolicy = 'O' THEN -- Deliver in strict order, compare with qty allocated + BEGIN + -- RAISE NOTICE 'Delivery policy = Strict order'; + + CASE v_deliveryRule + WHEN 'L' THEN -- Complete line + IF v_qtytodeliver = v_qtyallocated THEN + -- RAISE NOTICE 'Quantity to deliver = qty allocated'; + RETURN 1; + END IF; + WHEN 'O' THEN -- Complete order + IF v_qtytodeliver > v_qtyallocated THEN + -- RAISE NOTICE 'Not enough allocated for complete order'; + RETURN 0; + END IF; + WHEN 'A' THEN -- Availability + IF v_qtyallocated > 0 THEN + -- RAISE NOTICE 'Something to deliver'; + RETURN 1; + END IF; + END CASE; + -- RAISE NOTICE 'No inout candidate'; + RETURN 0; + + END; + END IF; + + IF v_DeliveryPolicy = 'N' THEN -- No hold, only compare with on hand + BEGIN + -- RAISE NOTICE 'Delivery policy = No hold'; + SELECT qtyonhand INTO + v_qtyonhand + FROM m_product_stock_v + WHERE M_Product_ID=v_product_id AND M_Warehouse_ID=v_warehouse_id; + + CASE v_deliveryRule + WHEN 'L' THEN -- Complete line + IF (v_qtytodeliver = v_qtyreserved AND v_qtytodeliver <= v_qtyonhand) THEN RETURN 1; END IF; + WHEN 'O' THEN -- Complete order + IF v_qtytodeliver < v_qtyreserved OR v_qtytodeliver >= v_qtyonhand THEN RETURN 0; END IF; + WHEN 'A' THEN -- Availability + IF v_qtyonhand > 0 THEN RETURN 1; END IF; + END CASE; + END; + END IF; + + -- RAISE NOTICE 'Default answer, something to deliver'; + return 1; +END; +/ diff --git a/db/ddlutils/oracle/functions/get_Allocated_On_Order.sql b/db/ddlutils/oracle/functions/get_Allocated_On_Order.sql new file mode 100644 index 0000000000..0671a6124d --- /dev/null +++ b/db/ddlutils/oracle/functions/get_Allocated_On_Order.sql @@ -0,0 +1,24 @@ +/************************************************************************ + * Function Get_Allocated_On_Order - Return number of allocated products + * of the specific product in the given warehouse. + * Author: Daniel Tamm (usrdno) +************************************************************************/ +CREATE OR REPLACE FUNCTION get_allocated_on_order +( + p_product_id IN NUMBER, + p_warehouse_id IN NUMBER) + RETURN NUMBER AS + + v_sum NUMBER; + +BEGIN + -- Get Product Attribute Set Instance + SELECT sum(qtyallocated) into v_sum from C_OrderLine ol + JOIN C_Order o on (o.C_Order_ID=ol.C_Order_ID) + WHERE + M_Product_ID=p_product_id AND + COALESCE(ol.M_Warehouse_ID, o.M_Warehouse_ID)=p_warehouse_id; + + RETURN v_sum; +END get_allocated_on_order; +/ diff --git a/db/ddlutils/oracle/functions/get_Delivery_Policy.sql b/db/ddlutils/oracle/functions/get_Delivery_Policy.sql new file mode 100644 index 0000000000..4a2232af0d --- /dev/null +++ b/db/ddlutils/oracle/functions/get_Delivery_Policy.sql @@ -0,0 +1,27 @@ +/************************************************************************ + * Function Get_Delivery_Policy - Return delivery policy of warehouse + * Author: Daniel Tamm (usrdno) +************************************************************************/ +CREATE OR REPLACE FUNCTION get_delivery_policy +( + warehouse_id IN NUMBER +) +RETURN CHAR AS + v_orgId NUMBER; + v_clientId NUMBER; + v_return CHAR; +BEGIN + SELECT ad_client_id, ad_org_id INTO + v_clientId, v_orgId FROM + M_Warehouse WHERE M_Warehouse_ID=warehouse_id; + + SELECT COALESCE(ad_orginfo.deliverypolicy, ad_clientinfo.deliverypolicy) INTO + v_return + FROM AD_ClientInfo + JOIN AD_OrgInfo ON (AD_ClientInfo.AD_Client_ID=AD_OrgInfo.AD_Client_ID) + WHERE AD_ClientInfo.AD_Client_ID = v_clientId AND + AD_OrgInfo.AD_Org_ID = v_orgId; + + return v_return; +END get_delivery_policy; +/ diff --git a/db/ddlutils/oracle/functions/isShippable.sql b/db/ddlutils/oracle/functions/isShippable.sql new file mode 100644 index 0000000000..57f52dbfbc --- /dev/null +++ b/db/ddlutils/oracle/functions/isShippable.sql @@ -0,0 +1,32 @@ +/************************************************************************ + * Function IsShippable - Return Y or N depending if this is a physical + * 'shippable' product or not. + * Author: Daniel Tamm (usrdno) +************************************************************************/ +CREATE OR REPLACE FUNCTION isshippable +( + product_id IN NUMBER +) + RETURN CHAR AS + v_IsStocked CHAR; + v_IsBom CHAR; + v_ProductType CHAR; + v_return CHAR; +BEGIN + IF product_id = NULL THEN + return 'N'; + END IF; + + SELECT IsStocked, IsBom, ProductType + INTO v_IsStocked, v_IsBom, v_ProductType + FROM M_Product WHERE M_Product_ID=product_id; + + IF (v_IsStocked='Y' AND v_ProductType='I' AND v_IsBom='N') THEN + v_return := 'Y'; + ELSE + v_return := 'N'; + END IF; + + return v_return; +END isshippable; +/ diff --git a/db/ddlutils/oracle/views/M_INOUT_CANDIDATE_V.sql b/db/ddlutils/oracle/views/M_INOUT_CANDIDATE_V.sql index 501c6d4a50..596e38b88d 100644 --- a/db/ddlutils/oracle/views/M_INOUT_CANDIDATE_V.sql +++ b/db/ddlutils/oracle/views/M_INOUT_CANDIDATE_V.sql @@ -1,37 +1,26 @@ -CREATE OR REPLACE VIEW M_INOUT_CANDIDATE_V -(AD_CLIENT_ID, AD_ORG_ID, C_BPARTNER_ID, C_ORDER_ID, DOCUMENTNO, - DATEORDERED, C_DOCTYPE_ID, POREFERENCE, DESCRIPTION, SALESREP_ID, - M_WAREHOUSE_ID, TOTALLINES) -AS -SELECT - o.AD_Client_ID, o.AD_Org_ID, o.C_BPartner_ID, o.C_Order_ID, - o.DocumentNo, o.DateOrdered, o.C_DocType_ID, - o.POReference, o.Description, o.SalesRep_ID, - l.M_Warehouse_ID, - SUM((l.QtyOrdered-l.QtyDelivered)*l.PriceActual) AS TotalLines -FROM C_Order o - INNER JOIN C_OrderLine l ON (o.C_Order_ID=l.C_Order_ID) -WHERE (o.DocStatus = 'CO' AND o.IsDelivered='N') -- Status must be CO - not CL/RE - -- not Offers and open Walkin-Receipts - AND o.C_DocType_ID IN (SELECT C_DocType_ID FROM C_DocType - WHERE DocBaseType='SOO' AND DocSubTypeSO NOT IN ('ON','OB','WR')) - -- Delivery Rule - not manual - AND o.DeliveryRule<>'M' - AND (l.M_Product_ID IS NULL OR EXISTS - (SELECT * FROM M_Product p - WHERE l.M_Product_ID=p.M_Product_ID AND p.IsExcludeAutoDelivery='N')) - -- we need to ship - AND l.QtyOrdered <> l.QtyDelivered - AND o.IsDropShip='N' - AND (l.M_Product_ID IS NOT NULL OR l.C_Charge_ID IS NOT NULL) - -- Not confirmed shipment - AND NOT EXISTS (SELECT * FROM M_InOutLine iol - INNER JOIN M_InOut io ON (iol.M_InOut_ID=io.M_InOut_ID) - WHERE iol.C_OrderLine_ID=l.C_OrderLine_ID AND io.DocStatus IN ('IP','WC')) - -- -GROUP BY o.AD_Client_ID, o.AD_Org_ID, o.C_BPartner_ID, o.C_Order_ID, - o.DocumentNo, o.DateOrdered, o.C_DocType_ID, - o.POReference, o.Description, o.SalesRep_ID, l.M_Warehouse_ID; - - +CREATE OR REPLACE VIEW m_inout_candidate_v AS +SELECT + o.ad_client_id, + o.ad_org_id, + o.c_bpartner_id, + o.c_order_id, + o.documentno, + o.dateordered, + o.c_doctype_id, + o.poreference, + o.description, + o.salesrep_id, + l.m_warehouse_id, + sum((l.qtyordered - l.qtydelivered) * l.priceactual) AS totallines + + FROM c_order o + JOIN c_orderline l ON o.c_order_id = l.c_order_id + WHERE + (l.m_product_id IS NULL OR (EXISTS ( SELECT 1 + FROM m_product p + WHERE l.m_product_id = p.m_product_id AND p.isexcludeautodelivery = 'N'))) AND + (l.m_product_id IS NOT NULL OR l.c_charge_id IS NOT NULL) AND + is_inout_candidate_order(o.c_order_id) = 'Y' + + GROUP BY o.ad_client_id, o.ad_org_id, o.c_bpartner_id, o.c_order_id, o.documentno, o.dateordered, o.c_doctype_id, o.poreference, o.description, o.salesrep_id, l.m_warehouse_id; diff --git a/db/ddlutils/oracle/views/M_PRODUCT_STOCK_V.sql b/db/ddlutils/oracle/views/M_PRODUCT_STOCK_V.sql index 38489270e1..d747f8173f 100644 --- a/db/ddlutils/oracle/views/M_PRODUCT_STOCK_V.sql +++ b/db/ddlutils/oracle/views/M_PRODUCT_STOCK_V.sql @@ -1,13 +1,17 @@ ---create views -CREATE OR REPLACE VIEW M_PRODUCT_STOCK_V -AS -SELECT -ms.IsActive, ms.Created, ms.CreatedBy, ms.Updated, ms.UpdatedBy, -mp.VALUE, mp.help, (ms.qtyonhand - ms.qtyreserved) AS qtyavailable, ms.qtyonhand, -ms.qtyreserved, mp.description, mw.NAME AS warehouse, mw.m_warehouse_id, mw.ad_client_id, -mw.ad_org_id, mp.documentnote -FROM M_STORAGE ms -JOIN M_PRODUCT mp ON ms.m_product_id = mp.m_product_id -JOIN M_LOCATOR ml ON ms.m_locator_id = ml.m_locator_id -JOIN M_WAREHOUSE mw ON ml.m_warehouse_id = mw.m_warehouse_id -ORDER BY mw.NAME; +CREATE OR REPLACE VIEW m_product_stock_v AS + SELECT ms.isactive, ms.created, ms.createdby, ms.updated, ms.updatedby, + ms.m_product_id, mp.value, mp.name, mp.help, + ms.qtyonhand - ms.qtyreserved AS qtyavailable, + ms.qtyonhand, ms.qtyreserved, + ms.qtyallocated, + mp.description, + mw.name AS warehouse, + mw.m_warehouse_id, + mw.ad_client_id, + mw.ad_org_id, + mp.documentnote + FROM m_storage ms + JOIN m_product mp ON ms.m_product_id = mp.m_product_id + JOIN m_locator ml ON ms.m_locator_id = ml.m_locator_id + JOIN m_warehouse mw ON ml.m_warehouse_id = mw.m_warehouse_id + ORDER BY mw.name; \ No newline at end of file diff --git a/db/ddlutils/postgresql/functions/Is_InOut_Candidate_Order.sql b/db/ddlutils/postgresql/functions/Is_InOut_Candidate_Order.sql new file mode 100644 index 0000000000..cb4faa36da --- /dev/null +++ b/db/ddlutils/postgresql/functions/Is_InOut_Candidate_Order.sql @@ -0,0 +1,49 @@ +/************************************************************************ + * Function Is_InOut_Candidate_Order - Return Y or N depending if + * this order can be shipped or not. + * Delivery Policy, Shipping rule etc is considered. + * Author: Daniel Tamm (usrdno) +************************************************************************/ +CREATE OR REPLACE FUNCTION is_inout_candidate_order(p_order_id numeric) + RETURNS character AS +$BODY$ +DECLARE + v_lines_ready numeric; + v_lines_total numeric; + v_deliveryRule character(1); +BEGIN + + -- Get order info + -- Only orders that are complete, not delivered, delivery rule anything else than manual and is a sales order + -- can be inout candidates + select DeliveryRule INTO v_deliveryRule FROM C_Order WHERE + c_order_id=p_order_id AND + docstatus = 'CO'::bpchar AND + isdelivered = 'N'::bpchar AND + deliveryrule <> 'M'::bpchar AND + (c_doctype_id IN ( SELECT c_doctype.c_doctype_id FROM c_doctype + WHERE c_doctype.docbasetype = 'SOO'::bpchar AND (c_doctype.docsubtypeso <> ALL (ARRAY['ON'::bpchar, 'OB'::bpchar, 'WR'::bpchar])))); + + IF v_deliveryRule IS NULL THEN + RETURN 'N'; + END IF; + + IF v_deliveryRule='F' THEN RETURN 'Y'; END IF; -- Force + + -- Check lines + SELECT sum(is_inout_candidate_orderline(c_orderline_id)), sum(1) + INTO v_lines_ready, v_lines_total + FROM c_orderline where c_order_id=p_order_id; + + CASE v_deliveryRule + WHEN 'L','A' THEN -- Complete line and Availability + IF v_lines_ready > 0 THEN RETURN 'Y'; END IF; + WHEN 'O' THEN -- Complete order + IF v_lines_ready = v_lines_total THEN RETURN 'Y'; END IF; + END CASE; + + return 'N'; +END +$BODY$ + LANGUAGE 'plpgsql' VOLATILE + COST 100; diff --git a/db/ddlutils/postgresql/functions/Is_InOut_Candidate_OrderLine.sql b/db/ddlutils/postgresql/functions/Is_InOut_Candidate_OrderLine.sql new file mode 100644 index 0000000000..9f75124ec2 --- /dev/null +++ b/db/ddlutils/postgresql/functions/Is_InOut_Candidate_OrderLine.sql @@ -0,0 +1,127 @@ +/************************************************************************ + * Function Is_InOut_Candidate_OrderLine - Return Y or N depending if + * order line can be shipped or not. + * Delivery Policy, Shipping rule etc is considered. + * Author: Daniel Tamm (usrdno) +************************************************************************/ +CREATE OR REPLACE FUNCTION is_inout_candidate_orderline(c_order_line_id numeric) + RETURNS numeric AS +$BODY$ +DECLARE + v_qtyordered numeric; + v_qtydelivered numeric; + v_qtyallocated numeric; + v_qtyonhand numeric; + v_qtytodeliver numeric; + v_qtyreserved numeric; + v_order_id numeric; + v_inoutExists numeric; + v_warehouse_id numeric; + v_product_id numeric; + v_orderReady numeric; + v_isShippable character(1); + v_deliveryRule character(1); + v_deliveryPolicy character(1); + v_return character(1); +BEGIN + SELECT qtyordered, qtydelivered, qtyallocated, qtyreserved, c_order_id, + get_delivery_policy(m_warehouse_id), isshippable(m_product_id), + m_warehouse_id, m_product_id + INTO + v_qtyordered, v_qtydelivered, v_qtyallocated, v_qtyreserved, v_order_id, + v_deliveryPolicy, v_isShippable, + v_warehouse_id, v_product_id + FROM + C_OrderLine where C_OrderLine_ID=c_order_line_id; + + -- If all is already delivered then it's not a candidate + IF v_qtyordered = v_qtydelivered THEN + -- RAISE NOTICE 'All is delivered'; + RETURN 0; + END IF; + + -- Non shippable (ie non physical items) are always inout candidate + IF v_isShippable='N' THEN + -- RAISE NOTICE 'Non physical item, always deliverable'; + RETURN 1; + END IF; + + SELECT 1 INTO v_inoutExists FROM m_inoutline iol + JOIN m_inout io ON iol.m_inout_id = io.m_inout_id + WHERE iol.c_orderline_id = c_order_line_id AND (io.docstatus = ANY (ARRAY['IP'::bpchar, 'WC'::bpchar, 'IN'::bpchar])); + + -- If an in-out line is in progress this is not a candidate + IF v_inoutExists = 1 THEN + -- RAISE NOTICE 'Already being shipped'; + RETURN 0; + END IF; + + -- Check delivery rule + SELECT DeliveryRule INTO + v_deliveryRule + FROM + C_Order where C_Order_ID=v_order_id; + + IF v_deliveryRule='F' THEN + -- RAISE NOTICE 'Delivery rule = Force'; + RETURN 1; + END IF; -- Force + + v_qtytodeliver := v_qtyordered - v_qtydelivered; + IF v_qtytodeliver = 0 THEN + -- RAISE NOTICE 'Nothing to deliver'; + RETURN 0; + END IF; + + IF v_DeliveryPolicy = 'O' THEN -- Deliver in strict order, compare with qty allocated + BEGIN + -- RAISE NOTICE 'Delivery policy = Strict order'; + + CASE v_deliveryRule + WHEN 'L' THEN -- Complete line + IF v_qtytodeliver = v_qtyallocated THEN + -- RAISE NOTICE 'Quantity to deliver = qty allocated'; + RETURN 1; + END IF; + WHEN 'O' THEN -- Complete order + IF v_qtytodeliver > v_qtyallocated THEN + -- RAISE NOTICE 'Not enough allocated for complete order'; + RETURN 0; + END IF; + WHEN 'A' THEN -- Availability + IF v_qtyallocated > 0 THEN + -- RAISE NOTICE 'Something to deliver'; + RETURN 1; + END IF; + END CASE; + -- RAISE NOTICE 'No inout candidate'; + RETURN 0; + + END; + END IF; + + IF v_DeliveryPolicy = 'N' THEN -- No hold, only compare with on hand + BEGIN + -- RAISE NOTICE 'Delivery policy = No hold'; + SELECT qtyonhand INTO + v_qtyonhand + FROM m_product_stock_v + WHERE M_Product_ID=v_product_id AND M_Warehouse_ID=v_warehouse_id; + + CASE v_deliveryRule + WHEN 'L' THEN -- Complete line + IF (v_qtytodeliver = v_qtyreserved AND v_qtytodeliver <= v_qtyonhand) THEN RETURN 1; END IF; + WHEN 'O' THEN -- Complete order + IF v_qtytodeliver < v_qtyreserved OR v_qtytodeliver >= v_qtyonhand THEN RETURN 0; END IF; + WHEN 'A' THEN -- Availability + IF v_qtyonhand > 0 THEN RETURN 1; END IF; + END CASE; + END; + END IF; + + -- RAISE NOTICE 'Default answer, something to deliver'; + return 1; +END +$BODY$ + LANGUAGE 'plpgsql' VOLATILE + COST 100; diff --git a/db/ddlutils/postgresql/functions/get_Allocated_On_Order.sql b/db/ddlutils/postgresql/functions/get_Allocated_On_Order.sql new file mode 100644 index 0000000000..01e141457e --- /dev/null +++ b/db/ddlutils/postgresql/functions/get_Allocated_On_Order.sql @@ -0,0 +1,26 @@ +/************************************************************************ + * Function Get_Allocated_On_Order - Return number of allocated products + * of the specific product in the given warehouse. + * Author: Daniel Tamm (usrdno) +************************************************************************/ +CREATE OR REPLACE FUNCTION get_allocated_on_order(p_product_id numeric, p_warehouse_id numeric) + RETURNS numeric AS +$BODY$ + +DECLARE + + v_sum numeric; + +BEGIN + -- Get Product Attribute Set Instance + SELECT sum(qtyallocated) into v_sum from C_OrderLine ol + JOIN C_Order o on (o.C_Order_ID=ol.C_Order_ID) + WHERE + M_Product_ID=p_product_id AND + COALESCE(ol.M_Warehouse_ID, o.M_Warehouse_ID)=p_warehouse_id; + + RETURN v_sum; +END; +$BODY$ + LANGUAGE 'plpgsql' VOLATILE + COST 100; diff --git a/db/ddlutils/postgresql/functions/get_Delivery_Policy.sql b/db/ddlutils/postgresql/functions/get_Delivery_Policy.sql new file mode 100644 index 0000000000..904288673f --- /dev/null +++ b/db/ddlutils/postgresql/functions/get_Delivery_Policy.sql @@ -0,0 +1,29 @@ +/************************************************************************ + * Function Get_Delivery_Policy - Return delivery policy of warehouse + * Author: Daniel Tamm (usrdno) +************************************************************************/ +CREATE OR REPLACE FUNCTION get_delivery_policy(warehouse_id numeric) + RETURNS character AS +$BODY$ +DECLARE + v_orgId numeric; + v_clientId numeric; + v_return character(1); +BEGIN + SELECT ad_client_id, ad_org_id INTO + v_clientId, v_orgId FROM + M_Warehouse WHERE M_Warehouse_ID=warehouse_id; + + SELECT COALESCE(ad_orginfo.deliverypolicy, ad_clientinfo.deliverypolicy) INTO + v_return + FROM AD_ClientInfo + JOIN AD_OrgInfo ON (AD_ClientInfo.AD_Client_ID=AD_OrgInfo.AD_Client_ID) + WHERE AD_ClientInfo.AD_Client_ID = v_clientId AND + AD_OrgInfo.AD_Org_ID = v_orgId; + + return v_return; +END; +$BODY$ + + LANGUAGE 'plpgsql' VOLATILE + COST 100; diff --git a/db/ddlutils/postgresql/functions/isShippable.sql b/db/ddlutils/postgresql/functions/isShippable.sql new file mode 100644 index 0000000000..70a335869b --- /dev/null +++ b/db/ddlutils/postgresql/functions/isShippable.sql @@ -0,0 +1,33 @@ +/************************************************************************ + * Function IsShippable - Return Y or N depending if this is a physical + * 'shippable' product or not. + * Author: Daniel Tamm (usrdno) +************************************************************************/ +CREATE OR REPLACE FUNCTION isshippable(product_id numeric) + RETURNS character AS +$BODY$ +DECLARE + v_IsStocked character(1); + v_IsBom character(1); + v_ProductType character(1); + v_return character(1); +BEGIN + IF product_id = NULL THEN + return 'N'; + END IF; + + SELECT IsStocked, IsBom, ProductType + INTO v_IsStocked, v_IsBom, v_ProductType + FROM M_Product WHERE M_Product_ID=product_id; + + IF (v_IsStocked='Y' AND v_ProductType='I' AND v_IsBom='N') THEN + v_return := 'Y'; + ELSE + v_return := 'N'; + END IF; + + return v_return; +END; +$BODY$ + LANGUAGE 'plpgsql' VOLATILE + COST 100; diff --git a/db/ddlutils/postgresql/views/M_INOUT_CANDIDATE_V.sql b/db/ddlutils/postgresql/views/M_INOUT_CANDIDATE_V.sql index 501c6d4a50..0ea8eb7478 100644 --- a/db/ddlutils/postgresql/views/M_INOUT_CANDIDATE_V.sql +++ b/db/ddlutils/postgresql/views/M_INOUT_CANDIDATE_V.sql @@ -1,37 +1,28 @@ -CREATE OR REPLACE VIEW M_INOUT_CANDIDATE_V -(AD_CLIENT_ID, AD_ORG_ID, C_BPARTNER_ID, C_ORDER_ID, DOCUMENTNO, - DATEORDERED, C_DOCTYPE_ID, POREFERENCE, DESCRIPTION, SALESREP_ID, - M_WAREHOUSE_ID, TOTALLINES) -AS -SELECT - o.AD_Client_ID, o.AD_Org_ID, o.C_BPartner_ID, o.C_Order_ID, - o.DocumentNo, o.DateOrdered, o.C_DocType_ID, - o.POReference, o.Description, o.SalesRep_ID, - l.M_Warehouse_ID, - SUM((l.QtyOrdered-l.QtyDelivered)*l.PriceActual) AS TotalLines -FROM C_Order o - INNER JOIN C_OrderLine l ON (o.C_Order_ID=l.C_Order_ID) -WHERE (o.DocStatus = 'CO' AND o.IsDelivered='N') -- Status must be CO - not CL/RE - -- not Offers and open Walkin-Receipts - AND o.C_DocType_ID IN (SELECT C_DocType_ID FROM C_DocType - WHERE DocBaseType='SOO' AND DocSubTypeSO NOT IN ('ON','OB','WR')) - -- Delivery Rule - not manual - AND o.DeliveryRule<>'M' - AND (l.M_Product_ID IS NULL OR EXISTS - (SELECT * FROM M_Product p - WHERE l.M_Product_ID=p.M_Product_ID AND p.IsExcludeAutoDelivery='N')) - -- we need to ship - AND l.QtyOrdered <> l.QtyDelivered - AND o.IsDropShip='N' - AND (l.M_Product_ID IS NOT NULL OR l.C_Charge_ID IS NOT NULL) - -- Not confirmed shipment - AND NOT EXISTS (SELECT * FROM M_InOutLine iol - INNER JOIN M_InOut io ON (iol.M_InOut_ID=io.M_InOut_ID) - WHERE iol.C_OrderLine_ID=l.C_OrderLine_ID AND io.DocStatus IN ('IP','WC')) - -- -GROUP BY o.AD_Client_ID, o.AD_Org_ID, o.C_BPartner_ID, o.C_Order_ID, - o.DocumentNo, o.DateOrdered, o.C_DocType_ID, - o.POReference, o.Description, o.SalesRep_ID, l.M_Warehouse_ID; - +CREATE OR REPLACE VIEW m_inout_candidate_v AS +SELECT + o.ad_client_id, + o.ad_org_id, + o.c_bpartner_id, + o.c_order_id, + o.documentno, + o.dateordered, + o.c_doctype_id, + o.poreference, + o.description, + o.salesrep_id, + l.m_warehouse_id, + sum((l.qtyordered - l.qtydelivered) * l.priceactual) AS totallines + + FROM c_order o + JOIN c_orderline l ON o.c_order_id = l.c_order_id + WHERE + (l.m_product_id IS NULL OR (EXISTS ( SELECT 1 + FROM m_product p + WHERE l.m_product_id = p.m_product_id AND p.isexcludeautodelivery = 'N'::bpchar))) AND + (l.m_product_id IS NOT NULL OR l.c_charge_id IS NOT NULL) AND + is_inout_candidate_order(o.c_order_id) = 'Y' + + GROUP BY o.ad_client_id, o.ad_org_id, o.c_bpartner_id, o.c_order_id, o.documentno, o.dateordered, o.c_doctype_id, o.poreference, o.description, o.salesrep_id, l.m_warehouse_id; +ALTER TABLE m_inout_candidate_v OWNER TO adempiere; diff --git a/db/ddlutils/postgresql/views/M_PRODUCT_STOCK_V.sql b/db/ddlutils/postgresql/views/M_PRODUCT_STOCK_V.sql index 38489270e1..05379ce3c2 100644 --- a/db/ddlutils/postgresql/views/M_PRODUCT_STOCK_V.sql +++ b/db/ddlutils/postgresql/views/M_PRODUCT_STOCK_V.sql @@ -1,13 +1,18 @@ ---create views -CREATE OR REPLACE VIEW M_PRODUCT_STOCK_V -AS -SELECT -ms.IsActive, ms.Created, ms.CreatedBy, ms.Updated, ms.UpdatedBy, -mp.VALUE, mp.help, (ms.qtyonhand - ms.qtyreserved) AS qtyavailable, ms.qtyonhand, -ms.qtyreserved, mp.description, mw.NAME AS warehouse, mw.m_warehouse_id, mw.ad_client_id, -mw.ad_org_id, mp.documentnote -FROM M_STORAGE ms -JOIN M_PRODUCT mp ON ms.m_product_id = mp.m_product_id -JOIN M_LOCATOR ml ON ms.m_locator_id = ml.m_locator_id -JOIN M_WAREHOUSE mw ON ml.m_warehouse_id = mw.m_warehouse_id -ORDER BY mw.NAME; +DROP VIEW IF EXISTS m_product_stock_v; +CREATE OR REPLACE VIEW m_product_stock_v AS + SELECT ms.isactive, ms.created, ms.createdby, ms.updated, ms.updatedby, + ms.m_product_id, mp.value, mp.name, mp.help, + ms.qtyonhand - ms.qtyreserved AS qtyavailable, + ms.qtyonhand, ms.qtyreserved, + ms.qtyallocated, + mp.description, + mw.name AS warehouse, + mw.m_warehouse_id, + mw.ad_client_id, + mw.ad_org_id, + mp.documentnote + FROM m_storage ms + JOIN m_product mp ON ms.m_product_id = mp.m_product_id + JOIN m_locator ml ON ms.m_locator_id = ml.m_locator_id + JOIN m_warehouse mw ON ml.m_warehouse_id = mw.m_warehouse_id + ORDER BY mw.name; diff --git a/migration/360lts-release/oracle/742_FR3004020_DeliveryPolicyOracle.sql b/migration/360lts-release/oracle/742_FR3004020_DeliveryPolicyOracle.sql new file mode 100644 index 0000000000..2c39aaf766 --- /dev/null +++ b/migration/360lts-release/oracle/742_FR3004020_DeliveryPolicyOracle.sql @@ -0,0 +1,353 @@ +-- 2010-maj-19 14:36:56 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Element (AD_Client_ID,AD_Element_ID,AD_Org_ID,ColumnName,Created,CreatedBy,Description,EntityType,Help,IsActive,Name,PrintName,Updated,UpdatedBy) VALUES (0,54156,0,'QtyAllocated',TO_DATE('2010-05-19 14:36:56','YYYY-MM-DD HH24:MI:SS'),100,'Allocated quantity','D','Allocated quantity is the quantity that is actually reserved for a specific customer. The customer "owns" this quantity. The allocated quantity can never be more than what''s in stock (as opposed to resevedQty).','Y','Qty Allocated','Qty Allocated',TO_DATE('2010-05-19 14:36:56','YYYY-MM-DD HH24:MI:SS'),100) +; + +-- 2010-maj-19 14:36:56 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Element_Trl (AD_Language,AD_Element_ID, Description,Help,Name,PO_Description,PO_Help,PO_Name,PO_PrintName,PrintName, IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy) SELECT l.AD_Language,t.AD_Element_ID, t.Description,t.Help,t.Name,t.PO_Description,t.PO_Help,t.PO_Name,t.PO_PrintName,t.PrintName, 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy FROM AD_Language l, AD_Element t WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.AD_Element_ID=54156 AND NOT EXISTS (SELECT * FROM AD_Element_Trl tt WHERE tt.AD_Language=l.AD_Language AND tt.AD_Element_ID=t.AD_Element_ID) +; + +-- 2010-maj-19 14:42:55 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Element (AD_Client_ID,AD_Element_ID,AD_Org_ID,ColumnName,Created,CreatedBy,Description,EntityType,Help,IsActive,Name,PrintName,Updated,UpdatedBy) VALUES (0,54157,0,'DeliveryPolicy',TO_DATE('2010-05-19 14:42:55','YYYY-MM-DD HH24:MI:SS'),100,'Delivery Policy','D','The delivery policy determines how outbound orders will be allocated. +The default delivery policy is to deliver fulfilled orders as soon as possible even if it means +other non fulfilled orders also needs the items being delivered on the fulfilled orders.','Y','Delivery Policy','Delivery Policy',TO_DATE('2010-05-19 14:42:55','YYYY-MM-DD HH24:MI:SS'),100) +; + +-- 2010-maj-19 14:42:55 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Element_Trl (AD_Language,AD_Element_ID, Description,Help,Name,PO_Description,PO_Help,PO_Name,PO_PrintName,PrintName, IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy) SELECT l.AD_Language,t.AD_Element_ID, t.Description,t.Help,t.Name,t.PO_Description,t.PO_Help,t.PO_Name,t.PO_PrintName,t.PrintName, 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy FROM AD_Language l, AD_Element t WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.AD_Element_ID=54157 AND NOT EXISTS (SELECT * FROM AD_Element_Trl tt WHERE tt.AD_Language=l.AD_Language AND tt.AD_Element_ID=t.AD_Element_ID) +; + +-- 2010-maj-19 14:43:59 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Reference (AD_Client_ID,AD_Org_ID,AD_Reference_ID,Created,CreatedBy,Description,EntityType,IsActive,IsOrderByValue,Name,Updated,UpdatedBy,ValidationType) VALUES (0,0,53355,TO_DATE('2010-05-19 14:43:59','YYYY-MM-DD HH24:MI:SS'),100,'List of Delivery Policies','D','Y','N','DeliveryPolicies',TO_DATE('2010-05-19 14:43:59','YYYY-MM-DD HH24:MI:SS'),100,'L') +; + +-- 2010-maj-19 14:43:59 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Reference_Trl (AD_Language,AD_Reference_ID, Description,Help,Name, IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy) SELECT l.AD_Language,t.AD_Reference_ID, t.Description,t.Help,t.Name, 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy FROM AD_Language l, AD_Reference t WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.AD_Reference_ID=53355 AND NOT EXISTS (SELECT * FROM AD_Reference_Trl tt WHERE tt.AD_Language=l.AD_Language AND tt.AD_Reference_ID=t.AD_Reference_ID) +; + +-- 2010-maj-19 14:44:40 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Ref_List (AD_Client_ID,AD_Org_ID,AD_Reference_ID,AD_Ref_List_ID,Created,CreatedBy,Description,EntityType,IsActive,Name,Updated,UpdatedBy,Value) VALUES (0,0,53355,53581,TO_DATE('2010-05-19 14:44:40','YYYY-MM-DD HH24:MI:SS'),100,'Default delivery policy - deliver as soon as orders are fulfilled according to delivery rule','D','Y','No Hold',TO_DATE('2010-05-19 14:44:40','YYYY-MM-DD HH24:MI:SS'),100,'N') +; + +-- 2010-maj-19 14:44:40 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Ref_List_Trl (AD_Language,AD_Ref_List_ID, Description,Name, IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy) SELECT l.AD_Language,t.AD_Ref_List_ID, t.Description,t.Name, 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy FROM AD_Language l, AD_Ref_List t WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.AD_Ref_List_ID=53581 AND NOT EXISTS (SELECT * FROM AD_Ref_List_Trl tt WHERE tt.AD_Language=l.AD_Language AND tt.AD_Ref_List_ID=t.AD_Ref_List_ID) +; + +-- 2010-maj-19 14:45:59 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Ref_List (AD_Client_ID,AD_Org_ID,AD_Reference_ID,AD_Ref_List_ID,Created,CreatedBy,Description,EntityType,IsActive,Name,Updated,UpdatedBy,Value) VALUES (0,0,53355,53582,TO_DATE('2010-05-19 14:45:59','YYYY-MM-DD HH24:MI:SS'),100,'Allocate items to orders and fulfill them in strict order.','D','Y','Strict order',TO_DATE('2010-05-19 14:45:59','YYYY-MM-DD HH24:MI:SS'),100,'O') +; + +-- 2010-maj-19 14:45:59 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Ref_List_Trl (AD_Language,AD_Ref_List_ID, Description,Name, IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy) SELECT l.AD_Language,t.AD_Ref_List_ID, t.Description,t.Name, 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy FROM AD_Language l, AD_Ref_List t WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.AD_Ref_List_ID=53582 AND NOT EXISTS (SELECT * FROM AD_Ref_List_Trl tt WHERE tt.AD_Language=l.AD_Language AND tt.AD_Ref_List_ID=t.AD_Ref_List_ID) +; + +-- 2010-maj-19 14:47:45 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Column (AD_Client_ID,AD_Column_ID,AD_Element_ID,AD_Org_ID,AD_Reference_ID,AD_Table_ID,ColumnName,Created,CreatedBy,DefaultValue,Description,EntityType,FieldLength,Help,IsActive,IsAllowLogging,IsAlwaysUpdateable,IsAutocomplete,IsEncrypted,IsIdentifier,IsKey,IsMandatory,IsParent,IsSelectionColumn,IsSyncDatabase,IsTranslated,IsUpdateable,Name,SeqNo,Updated,UpdatedBy,Version) VALUES (0,59188,54156,0,29,260,'QtyAllocated',TO_DATE('2010-05-19 14:47:45','YYYY-MM-DD HH24:MI:SS'),100,'0','Allocated quantity','D',22,'Allocated quantity is the quantity that is actually reserved for a specific customer. The customer "owns" this quantity. The allocated quantity can never be more than what''s in stock (as opposed to resevedQty).','Y','Y','N','N','N','N','N','N','N','N','N','N','Y','Qty Allocated',0,TO_DATE('2010-05-19 14:47:45','YYYY-MM-DD HH24:MI:SS'),100,1.000000000000) +; + +-- 2010-maj-19 14:47:45 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Column_Trl (AD_Language,AD_Column_ID, Name, IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy) SELECT l.AD_Language,t.AD_Column_ID, t.Name, 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy FROM AD_Language l, AD_Column t WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.AD_Column_ID=59188 AND NOT EXISTS (SELECT * FROM AD_Column_Trl tt WHERE tt.AD_Language=l.AD_Language AND tt.AD_Column_ID=t.AD_Column_ID) +; + +-- 2010-maj-19 14:48:05 CEST +-- FR 3002040 - Delivery Policy +ALTER TABLE C_OrderLine ADD QtyAllocated NUMBER DEFAULT 0 +; + +-- 2010-maj-19 14:49:07 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Column (AD_Client_ID,AD_Column_ID,AD_Element_ID,AD_Org_ID,AD_Reference_ID,AD_Table_ID,ColumnName,Created,CreatedBy,Description,EntityType,FieldLength,Help,IsActive,IsAllowLogging,IsAlwaysUpdateable,IsAutocomplete,IsEncrypted,IsIdentifier,IsKey,IsMandatory,IsParent,IsSelectionColumn,IsSyncDatabase,IsTranslated,IsUpdateable,Name,SeqNo,Updated,UpdatedBy,Version) VALUES (0,59189,54156,0,29,250,'QtyAllocated',TO_DATE('2010-05-19 14:49:07','YYYY-MM-DD HH24:MI:SS'),100,'Allocated quantity','D',22,'Allocated quantity is the quantity that is actually reserved for a specific customer. The customer "owns" this quantity. The allocated quantity can never be more than what''s in stock (as opposed to resevedQty).','Y','Y','N','N','N','N','N','N','N','N','N','N','Y','Qty Allocated',0,TO_DATE('2010-05-19 14:49:07','YYYY-MM-DD HH24:MI:SS'),100,1.000000000000) +; + +-- 2010-maj-19 14:49:07 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Column_Trl (AD_Language,AD_Column_ID, Name, IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy) SELECT l.AD_Language,t.AD_Column_ID, t.Name, 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy FROM AD_Language l, AD_Column t WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.AD_Column_ID=59189 AND NOT EXISTS (SELECT * FROM AD_Column_Trl tt WHERE tt.AD_Language=l.AD_Language AND tt.AD_Column_ID=t.AD_Column_ID) +; + +-- 2010-maj-19 14:49:18 CEST +-- FR 3002040 - Delivery Policy +ALTER TABLE M_Storage ADD QtyAllocated NUMBER DEFAULT 0 +; + +-- 2010-maj-19 14:49:28 CEST +-- FR 3002040 - Delivery Policy +UPDATE AD_Column SET DefaultValue='0',Updated=TO_DATE('2010-05-19 14:49:28','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Column_ID=59189 +; + + +-- 2010-maj-19 14:54:08 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Field (AD_Client_ID,AD_Column_ID,AD_FieldGroup_ID,AD_Field_ID,AD_Org_ID,AD_Tab_ID,Created,CreatedBy,Description,DisplayLength,DisplayLogic,EntityType,Help,IsActive,IsCentrallyMaintained,IsDisplayed,IsEncrypted,IsFieldOnly,IsHeading,IsReadOnly,IsSameLine,Name,SeqNo,Updated,UpdatedBy) VALUES (0,59188,102,58877,0,187,TO_DATE('2010-05-19 14:54:08','YYYY-MM-DD HH24:MI:SS'),100,'Allocated quantity',26,'@OrderType@=''OB'' | @OrderType@=''SO'' | @Processed@=''Y''','D','Allocated quantity is the quantity that is actually reserved for a specific customer. The customer "owns" this quantity. The allocated quantity can never be more than what''s in stock (as opposed to resevedQty).','Y','Y','Y','N','N','N','Y','N','Qty Allocated',190,TO_DATE('2010-05-19 14:54:08','YYYY-MM-DD HH24:MI:SS'),100) +; + +-- 2010-maj-19 14:54:08 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Field_Trl (AD_Language,AD_Field_ID, Description,Help,Name, IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy) SELECT l.AD_Language,t.AD_Field_ID, t.Description,t.Help,t.Name, 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy FROM AD_Language l, AD_Field t WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.AD_Field_ID=58877 AND NOT EXISTS (SELECT * FROM AD_Field_Trl tt WHERE tt.AD_Language=l.AD_Language AND tt.AD_Field_ID=t.AD_Field_ID) +; + +-- 2010-maj-19 14:54:35 CEST +-- FR 3002040 - Delivery Policy +UPDATE AD_Field SET SeqNo=210,IsDisplayed='Y' WHERE AD_Field_ID=58877 +; + +-- 2010-maj-19 14:54:35 CEST +-- FR 3002040 - Delivery Policy +UPDATE AD_Field SET SeqNo=220,IsDisplayed='Y' WHERE AD_Field_ID=1135 +; + +-- 2010-maj-19 14:54:35 CEST +-- FR 3002040 - Delivery Policy +UPDATE AD_Field SET SeqNo=230,IsDisplayed='Y' WHERE AD_Field_ID=10829 +; + +-- 2010-maj-19 14:54:35 CEST +-- FR 3002040 - Delivery Policy +UPDATE AD_Field SET SeqNo=240,IsDisplayed='Y' WHERE AD_Field_ID=1138 +; + +-- 2010-maj-19 14:54:35 CEST +-- FR 3002040 - Delivery Policy +UPDATE AD_Field SET SeqNo=250,IsDisplayed='Y' WHERE AD_Field_ID=1137 +; + +-- 2010-maj-19 14:54:35 CEST +-- FR 3002040 - Delivery Policy +UPDATE AD_Field SET SeqNo=260,IsDisplayed='Y' WHERE AD_Field_ID=2115 +; + +-- 2010-maj-19 14:54:35 CEST +-- FR 3002040 - Delivery Policy +UPDATE AD_Field SET SeqNo=270,IsDisplayed='Y' WHERE AD_Field_ID=1141 +; + +-- 2010-maj-19 14:54:35 CEST +-- FR 3002040 - Delivery Policy +UPDATE AD_Field SET SeqNo=280,IsDisplayed='Y' WHERE AD_Field_ID=3124 +; + +-- 2010-maj-19 14:54:35 CEST +-- FR 3002040 - Delivery Policy +UPDATE AD_Field SET SeqNo=290,IsDisplayed='Y' WHERE AD_Field_ID=12745 +; + +-- 2010-maj-19 14:54:35 CEST +-- FR 3002040 - Delivery Policy +UPDATE AD_Field SET SeqNo=300,IsDisplayed='Y' WHERE AD_Field_ID=13644 +; + +-- 2010-maj-19 14:54:35 CEST +-- FR 3002040 - Delivery Policy +UPDATE AD_Field SET SeqNo=310,IsDisplayed='Y' WHERE AD_Field_ID=13645 +; + +-- 2010-maj-19 14:54:35 CEST +-- FR 3002040 - Delivery Policy +UPDATE AD_Field SET SeqNo=320,IsDisplayed='Y' WHERE AD_Field_ID=13691 +; + +-- 2010-maj-19 14:54:35 CEST +-- FR 3002040 - Delivery Policy +UPDATE AD_Field SET SeqNo=330,IsDisplayed='Y' WHERE AD_Field_ID=13650 +; + +-- 2010-maj-19 14:54:35 CEST +-- FR 3002040 - Delivery Policy +UPDATE AD_Field SET SeqNo=340,IsDisplayed='Y' WHERE AD_Field_ID=13651 +; + +-- 2010-maj-19 14:54:35 CEST +-- FR 3002040 - Delivery Policy +UPDATE AD_Field SET SeqNo=350,IsDisplayed='Y' WHERE AD_Field_ID=2880 +; + +-- 2010-maj-19 14:54:35 CEST +-- FR 3002040 - Delivery Policy +UPDATE AD_Field SET SeqNo=360,IsDisplayed='Y' WHERE AD_Field_ID=12744 +; + +-- 2010-maj-19 14:54:35 CEST +-- FR 3002040 - Delivery Policy +UPDATE AD_Field SET SeqNo=370,IsDisplayed='Y' WHERE AD_Field_ID=10332 +; + +-- 2010-maj-19 14:55:07 CEST +-- FR 3002040 - Delivery Policy +UPDATE AD_Field SET Name='Allocated Quantity',Updated=TO_DATE('2010-05-19 14:55:07','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=58877 +; + +-- 2010-maj-19 14:55:07 CEST +-- FR 3002040 - Delivery Policy +UPDATE AD_Field_Trl SET IsTranslated='N' WHERE AD_Field_ID=58877 +; + +-- 2010-maj-19 14:56:52 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Field (AD_Client_ID,AD_Column_ID,AD_Field_ID,AD_Org_ID,AD_Tab_ID,Created,CreatedBy,Description,DisplayLength,EntityType,Help,IsActive,IsCentrallyMaintained,IsDisplayed,IsEncrypted,IsFieldOnly,IsHeading,IsReadOnly,IsSameLine,Name,SeqNo,SortNo,Updated,UpdatedBy) VALUES (0,59189,58878,0,179,TO_DATE('2010-05-19 14:56:52','YYYY-MM-DD HH24:MI:SS'),100,'Allocated quantity',26,'D','Allocated quantity is the quantity that is actually reserved for a specific customer. The customer "owns" this quantity. The allocated quantity can never be more than what''s in stock (as opposed to resevedQty).','Y','Y','Y','N','N','N','N','N','Qty Allocated',110,0,TO_DATE('2010-05-19 14:56:52','YYYY-MM-DD HH24:MI:SS'),100) +; + +-- 2010-maj-19 14:56:52 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Field_Trl (AD_Language,AD_Field_ID, Description,Help,Name, IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy) SELECT l.AD_Language,t.AD_Field_ID, t.Description,t.Help,t.Name, 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy FROM AD_Language l, AD_Field t WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.AD_Field_ID=58878 AND NOT EXISTS (SELECT * FROM AD_Field_Trl tt WHERE tt.AD_Language=l.AD_Language AND tt.AD_Field_ID=t.AD_Field_ID) +; + +-- 2010-maj-19 14:57:28 CEST +-- FR 3002040 - Delivery Policy +UPDATE AD_Field SET Name='Allocated Quantity',Updated=TO_DATE('2010-05-19 14:57:28','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=58878 +; + +-- 2010-maj-19 14:57:28 CEST +-- FR 3002040 - Delivery Policy +UPDATE AD_Field_Trl SET IsTranslated='N' WHERE AD_Field_ID=58878 +; + +-- 2010-maj-19 15:02:03 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Column (AD_Client_ID,AD_Column_ID,AD_Element_ID,AD_Org_ID,AD_Reference_ID,AD_Reference_Value_ID,AD_Table_ID,ColumnName,Created,CreatedBy,Description,EntityType,FieldLength,Help,IsActive,IsAllowLogging,IsAlwaysUpdateable,IsAutocomplete,IsEncrypted,IsIdentifier,IsKey,IsMandatory,IsParent,IsSelectionColumn,IsSyncDatabase,IsTranslated,IsUpdateable,Name,SeqNo,Updated,UpdatedBy,Version) VALUES (0,59190,54157,0,17,53355,228,'DeliveryPolicy',TO_DATE('2010-05-19 15:02:03','YYYY-MM-DD HH24:MI:SS'),100,'Delivery Policy','D',1,'The delivery policy determines how outbound orders will be allocated. +The default delivery policy is to deliver fulfilled orders as soon as possible even if it means +other non fulfilled orders also needs the items being delivered on the fulfilled orders.','Y','Y','N','N','N','N','N','N','N','N','N','N','Y','Delivery Policy',0,TO_DATE('2010-05-19 15:02:03','YYYY-MM-DD HH24:MI:SS'),100,0) +; + +-- 2010-maj-19 15:02:03 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Column_Trl (AD_Language,AD_Column_ID, Name, IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy) SELECT l.AD_Language,t.AD_Column_ID, t.Name, 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy FROM AD_Language l, AD_Column t WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.AD_Column_ID=59190 AND NOT EXISTS (SELECT * FROM AD_Column_Trl tt WHERE tt.AD_Language=l.AD_Language AND tt.AD_Column_ID=t.AD_Column_ID) +; + +-- 2010-maj-19 15:02:11 CEST +-- FR 3002040 - Delivery Policy +UPDATE AD_Column SET Version=1.000000000000,Updated=TO_DATE('2010-05-19 15:02:11','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Column_ID=59190 +; + +-- 2010-maj-19 15:02:18 CEST +-- FR 3002040 - Delivery Policy +ALTER TABLE AD_OrgInfo ADD DeliveryPolicy CHAR(1) DEFAULT NULL +; + +-- 2010-maj-19 15:06:47 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Column (AD_Client_ID,AD_Column_ID,AD_Element_ID,AD_Org_ID,AD_Reference_ID,AD_Reference_Value_ID,AD_Table_ID,ColumnName,Created,CreatedBy,DefaultValue,Description,EntityType,FieldLength,Help,IsActive,IsAllowLogging,IsAlwaysUpdateable,IsAutocomplete,IsEncrypted,IsIdentifier,IsKey,IsMandatory,IsParent,IsSelectionColumn,IsSyncDatabase,IsTranslated,IsUpdateable,Name,SeqNo,Updated,UpdatedBy,Version) VALUES (0,59191,54157,0,17,53355,227,'DeliveryPolicy',TO_DATE('2010-05-19 15:06:46','YYYY-MM-DD HH24:MI:SS'),100,'N','Delivery Policy','D',1,'The delivery policy determines how outbound orders will be allocated. +The default delivery policy is to deliver fulfilled orders as soon as possible even if it means +other non fulfilled orders also needs the items being delivered on the fulfilled orders.','Y','Y','N','N','N','N','N','N','N','N','N','N','Y','Delivery Policy',0,TO_DATE('2010-05-19 15:06:46','YYYY-MM-DD HH24:MI:SS'),100,1.000000000000) +; + +-- 2010-maj-19 15:06:47 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Column_Trl (AD_Language,AD_Column_ID, Name, IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy) SELECT l.AD_Language,t.AD_Column_ID, t.Name, 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy FROM AD_Language l, AD_Column t WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.AD_Column_ID=59191 AND NOT EXISTS (SELECT * FROM AD_Column_Trl tt WHERE tt.AD_Language=l.AD_Language AND tt.AD_Column_ID=t.AD_Column_ID) +; + +-- 2010-maj-19 15:06:53 CEST +-- FR 3002040 - Delivery Policy +ALTER TABLE AD_ClientInfo ADD DeliveryPolicy CHAR(1) DEFAULT 'N' +; + +-- Add field to Client Info Window + +-- 2010-maj-19 15:10:10 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Field (AD_Client_ID,AD_Column_ID,AD_FieldGroup_ID,AD_Field_ID,AD_Org_ID,AD_Tab_ID,Created,CreatedBy,DefaultValue,Description,DisplayLength,EntityType,Help,IsActive,IsCentrallyMaintained,IsDisplayed,IsEncrypted,IsFieldOnly,IsHeading,IsReadOnly,IsSameLine,Name,SeqNo,Updated,UpdatedBy) VALUES (0,59191,118,58879,0,169,TO_DATE('2010-05-19 15:10:10','YYYY-MM-DD HH24:MI:SS'),100,'N','Delivery Policy',14,'U','The delivery policy determines how outbound orders will be allocated. +The default delivery policy is to deliver fulfilled orders as soon as possible even if it means +other non fulfilled orders also needs the items being delivered on the fulfilled orders.','Y','Y','Y','N','N','N','N','Y','Delivery Policy',155,TO_DATE('2010-05-19 15:10:10','YYYY-MM-DD HH24:MI:SS'),100) +; + +-- 2010-maj-19 15:10:10 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Field_Trl (AD_Language,AD_Field_ID, Description,Help,Name, IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy) SELECT l.AD_Language,t.AD_Field_ID, t.Description,t.Help,t.Name, 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy FROM AD_Language l, AD_Field t WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.AD_Field_ID=58879 AND NOT EXISTS (SELECT * FROM AD_Field_Trl tt WHERE tt.AD_Language=l.AD_Language AND tt.AD_Field_ID=t.AD_Field_ID) +; + +-- 2010-maj-19 15:12:01 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Field (AD_Client_ID,AD_Column_ID,AD_Field_ID,AD_Org_ID,AD_Tab_ID,Created,CreatedBy,Description,DisplayLength,EntityType,Help,IsActive,IsCentrallyMaintained,IsDisplayed,IsEncrypted,IsFieldOnly,IsHeading,IsReadOnly,IsSameLine,Name,SeqNo,SortNo,Updated,UpdatedBy) VALUES (0,59190,58880,0,170,TO_DATE('2010-05-19 15:12:01','YYYY-MM-DD HH24:MI:SS'),100,'Delivery Policy',0,'D','The delivery policy determines how outbound orders will be allocated. +The default delivery policy is to deliver fulfilled orders as soon as possible even if it means +other non fulfilled orders also needs the items being delivered on the fulfilled orders.','Y','Y','Y','N','N','N','N','N','Delivery Policy',85,0,TO_DATE('2010-05-19 15:12:01','YYYY-MM-DD HH24:MI:SS'),100) +; + +-- 2010-maj-19 15:12:01 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Field_Trl (AD_Language,AD_Field_ID, Description,Help,Name, IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy) SELECT l.AD_Language,t.AD_Field_ID, t.Description,t.Help,t.Name, 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy FROM AD_Language l, AD_Field t WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.AD_Field_ID=58880 AND NOT EXISTS (SELECT * FROM AD_Field_Trl tt WHERE tt.AD_Language=l.AD_Language AND tt.AD_Field_ID=t.AD_Field_ID) +; + +-- 2010-maj-19 15:12:11 CEST +-- FR 3002040 - Delivery Policy +UPDATE AD_Field SET DisplayLength=22,Updated=TO_DATE('2010-05-19 15:12:11','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=58880 +; + +-- Jun 22, 2010 1:47:33 PM CEST +-- FR 3004020 Delivery Policy +INSERT INTO AD_Process (AccessLevel,AD_Client_ID,AD_Org_ID,AD_Process_ID,Classname,CopyFromProcess,Created,CreatedBy,Description,EntityType,Help,IsActive,IsBetaFunctionality,IsDirectPrint,IsReport,IsServerProcess,Name,ShowHelp,Statistic_Count,Statistic_Seconds,Updated,UpdatedBy,Value) VALUES ('3',0,0,53217,'org.adempiere.process.AllocateSalesOrders','N',TO_DATE('2010-06-22 13:47:31','YYYY-MM-DD HH24:MI:SS'),100,'Allocate available on hand stock to sales orders','D','This process is only necessary for delivery policy = Strict order','Y','N','N','N','N','Allocate Sales Orders','Y',0,0,TO_DATE('2010-06-22 13:47:31','YYYY-MM-DD HH24:MI:SS'),100,'AllocateSalesOrders') +; + +-- Jun 22, 2010 1:47:33 PM CEST +-- FR 3004020 Delivery Policy +INSERT INTO AD_Process_Trl (AD_Language,AD_Process_ID, Description,Help,Name, IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy) SELECT l.AD_Language,t.AD_Process_ID, t.Description,t.Help,t.Name, 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy FROM AD_Language l, AD_Process t WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.AD_Process_ID=53217 AND NOT EXISTS (SELECT * FROM AD_Process_Trl tt WHERE tt.AD_Language=l.AD_Language AND tt.AD_Process_ID=t.AD_Process_ID) +; + +-- Jun 22, 2010 1:48:24 PM CEST +-- FR 3004020 Delivery Policy +INSERT INTO AD_Process_Para (AD_Client_ID,AD_Element_ID,AD_Org_ID,AD_Process_ID,AD_Process_Para_ID,AD_Reference_ID,ColumnName,Created,CreatedBy,Description,EntityType,FieldLength,IsActive,IsCentrallyMaintained,IsMandatory,IsRange,Name,SeqNo,Updated,UpdatedBy) VALUES (0,459,0,53217,53422,18,'M_Warehouse_ID',TO_DATE('2010-06-22 13:48:23','YYYY-MM-DD HH24:MI:SS'),100,'Warehouse where allocation should be executed.','D',0,'Y','Y','N','N','Warehouse',10,TO_DATE('2010-06-22 13:48:23','YYYY-MM-DD HH24:MI:SS'),100) +; + +-- Jun 22, 2010 1:48:24 PM CEST +-- FR 3004020 Delivery Policy +INSERT INTO AD_Process_Para_Trl (AD_Language,AD_Process_Para_ID, Description,Help,Name, IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy) SELECT l.AD_Language,t.AD_Process_Para_ID, t.Description,t.Help,t.Name, 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy FROM AD_Language l, AD_Process_Para t WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.AD_Process_Para_ID=53422 AND NOT EXISTS (SELECT * FROM AD_Process_Para_Trl tt WHERE tt.AD_Language=l.AD_Language AND tt.AD_Process_Para_ID=t.AD_Process_Para_ID) +; + +-- Jun 22, 2010 1:49:05 PM CEST +-- FR 3004020 Delivery Policy +INSERT INTO AD_Menu (Action,AD_Client_ID,AD_Menu_ID,AD_Org_ID,AD_Process_ID,Created,CreatedBy,Description,EntityType,IsActive,IsCentrallyMaintained,IsReadOnly,IsSOTrx,IsSummary,Name,Updated,UpdatedBy) VALUES ('P',0,53283,0,53217,TO_DATE('2010-06-22 13:49:04','YYYY-MM-DD HH24:MI:SS'),100,'Allocate available on hand stock to sales orders','D','Y','Y','N','N','N','Allocate Sales Orders',TO_DATE('2010-06-22 13:49:04','YYYY-MM-DD HH24:MI:SS'),100) +; + +-- Jun 22, 2010 1:49:05 PM CEST +-- FR 3004020 Delivery Policy +INSERT INTO AD_Menu_Trl (AD_Language,AD_Menu_ID, Description,Name, IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy) SELECT l.AD_Language,t.AD_Menu_ID, t.Description,t.Name, 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy FROM AD_Language l, AD_Menu t WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.AD_Menu_ID=53283 AND NOT EXISTS (SELECT * FROM AD_Menu_Trl tt WHERE tt.AD_Language=l.AD_Language AND tt.AD_Menu_ID=t.AD_Menu_ID) +; + +-- Jun 22, 2010 1:49:05 PM CEST +-- FR 3004020 Delivery Policy +INSERT INTO AD_TreeNodeMM (AD_Client_ID,AD_Org_ID, IsActive,Created,CreatedBy,Updated,UpdatedBy, AD_Tree_ID, Node_ID, Parent_ID, SeqNo) SELECT t.AD_Client_ID, 0, 'Y', SysDate, 100, SysDate, 100,t.AD_Tree_ID, 53283, 0, 999 FROM AD_Tree t WHERE t.AD_Client_ID=0 AND t.IsActive='Y' AND t.IsAllNodes='Y' AND t.TreeType='MM' AND NOT EXISTS (SELECT * FROM AD_TreeNodeMM e WHERE e.AD_Tree_ID=t.AD_Tree_ID AND Node_ID=53283) +; + +-- Jun 22, 2010 1:49:15 PM CEST +-- FR 3004020 Delivery Policy +UPDATE AD_TreeNodeMM SET Parent_ID=459, SeqNo=0, Updated=SysDate WHERE AD_Tree_ID=10 AND Node_ID=53283 +; + +-- Jun 22, 2010 1:49:15 PM CEST +-- FR 3004020 Delivery Policy +UPDATE AD_TreeNodeMM SET Parent_ID=459, SeqNo=1, Updated=SysDate WHERE AD_Tree_ID=10 AND Node_ID=346 +; + +-- Jun 22, 2010 1:49:15 PM CEST +-- FR 3004020 Delivery Policy +UPDATE AD_TreeNodeMM SET Parent_ID=459, SeqNo=2, Updated=SysDate WHERE AD_Tree_ID=10 AND Node_ID=53132 +; + +-- Jun 22, 2010 1:49:15 PM CEST +-- FR 3004020 Delivery Policy +UPDATE AD_TreeNodeMM SET Parent_ID=459, SeqNo=3, Updated=SysDate WHERE AD_Tree_ID=10 AND Node_ID=193 +; + +-- Jun 22, 2010 1:49:15 PM CEST +-- FR 3004020 Delivery Policy +UPDATE AD_TreeNodeMM SET Parent_ID=459, SeqNo=4, Updated=SysDate WHERE AD_Tree_ID=10 AND Node_ID=180 +; + +-- Jun 22, 2010 1:49:15 PM CEST +-- FR 3004020 Delivery Policy +UPDATE AD_TreeNodeMM SET Parent_ID=459, SeqNo=5, Updated=SysDate WHERE AD_Tree_ID=10 AND Node_ID=494 +; + +-- Jun 22, 2010 1:49:15 PM CEST +-- FR 3004020 Delivery Policy +UPDATE AD_TreeNodeMM SET Parent_ID=459, SeqNo=6, Updated=SysDate WHERE AD_Tree_ID=10 AND Node_ID=444 +; + diff --git a/migration/360lts-release/oracle/743_FR3004020_DeliveryPolicyOracle.sql b/migration/360lts-release/oracle/743_FR3004020_DeliveryPolicyOracle.sql new file mode 100644 index 0000000000..b017dbde50 --- /dev/null +++ b/migration/360lts-release/oracle/743_FR3004020_DeliveryPolicyOracle.sql @@ -0,0 +1,296 @@ +-- Adjust, alter view M_Product_Stock_v +CREATE OR REPLACE VIEW m_product_stock_v AS + SELECT ms.isactive, ms.created, ms.createdby, ms.updated, ms.updatedby, + ms.m_product_id, mp.value, mp.name, mp.help, + ms.qtyonhand - ms.qtyreserved AS qtyavailable, + ms.qtyonhand, ms.qtyreserved, + ms.qtyallocated, + mp.description, + mw.name AS warehouse, + mw.m_warehouse_id, + mw.ad_client_id, + mw.ad_org_id, + mp.documentnote + FROM m_storage ms + JOIN m_product mp ON ms.m_product_id = mp.m_product_id + JOIN m_locator ml ON ms.m_locator_id = ml.m_locator_id + JOIN m_warehouse mw ON ml.m_warehouse_id = mw.m_warehouse_id + ORDER BY mw.name; + + +-- Create function isShippable +CREATE OR REPLACE FUNCTION isshippable +( + product_id IN NUMBER +) + RETURN CHAR AS + v_IsStocked CHAR; + v_IsBom CHAR; + v_ProductType CHAR; + v_return CHAR; +BEGIN + IF product_id = NULL THEN + return 'N'; + END IF; + + SELECT IsStocked, IsBom, ProductType + INTO v_IsStocked, v_IsBom, v_ProductType + FROM M_Product WHERE M_Product_ID=product_id; + + IF (v_IsStocked='Y' AND v_ProductType='I' AND v_IsBom='N') THEN + v_return := 'Y'; + ELSE + v_return := 'N'; + END IF; + + return v_return; +END isshippable; +/ + +-- Function to get delivery policy + +CREATE OR REPLACE FUNCTION get_delivery_policy +( + warehouse_id IN NUMBER +) +RETURN CHAR AS + v_orgId NUMBER; + v_clientId NUMBER; + v_return CHAR; +BEGIN + SELECT ad_client_id, ad_org_id INTO + v_clientId, v_orgId FROM + M_Warehouse WHERE M_Warehouse_ID=warehouse_id; + + SELECT COALESCE(ad_orginfo.deliverypolicy, ad_clientinfo.deliverypolicy) INTO + v_return + FROM AD_ClientInfo + JOIN AD_OrgInfo ON (AD_ClientInfo.AD_Client_ID=AD_OrgInfo.AD_Client_ID) + WHERE AD_ClientInfo.AD_Client_ID = v_clientId AND + AD_OrgInfo.AD_Org_ID = v_orgId; + + return v_return; +END get_delivery_policy; +/ + +-- Get allocated on order + +CREATE OR REPLACE FUNCTION get_allocated_on_order +( + p_product_id IN NUMBER, + p_warehouse_id IN NUMBER) + RETURN NUMBER AS + + v_sum NUMBER; + +BEGIN + -- Get Product Attribute Set Instance + SELECT sum(qtyallocated) into v_sum from C_OrderLine ol + JOIN C_Order o on (o.C_Order_ID=ol.C_Order_ID) + WHERE + M_Product_ID=p_product_id AND + COALESCE(ol.M_Warehouse_ID, o.M_Warehouse_ID)=p_warehouse_id; + + RETURN v_sum; +END get_allocated_on_order; +/ + +-- IN OUT CANDIDATE ORDERLINE + +CREATE OR REPLACE FUNCTION is_inout_candidate_orderline +( + c_order_line_id IN NUMBER +) + RETURN NUMBER AS + + v_qtyordered NUMBER; + v_qtydelivered NUMBER; + v_qtyallocated NUMBER; + v_qtyonhand NUMBER; + v_qtytodeliver NUMBER; + v_qtyreserved NUMBER; + v_order_id NUMBER; + v_inoutExists NUMBER; + v_warehouse_id NUMBER; + v_product_id NUMBER; + v_orderReady NUMBER; + v_isShippable CHAR; + v_deliveryRule CHAR; + v_deliveryPolicy CHAR; + v_return CHAR; + +BEGIN + SELECT qtyordered, qtydelivered, qtyallocated, qtyreserved, c_order_id, + get_delivery_policy(m_warehouse_id), isshippable(m_product_id), + m_warehouse_id, m_product_id + INTO + v_qtyordered, v_qtydelivered, v_qtyallocated, v_qtyreserved, v_order_id, + v_deliveryPolicy, v_isShippable, + v_warehouse_id, v_product_id + FROM + C_OrderLine where C_OrderLine_ID=c_order_line_id; + + -- If all is already delivered then it's not a candidate + IF v_qtyordered = v_qtydelivered THEN + -- RAISE NOTICE 'All is delivered'; + RETURN 0; + END IF; + + -- Non shippable (ie non physical items) are always inout candidate + IF v_isShippable='N' THEN + -- RAISE NOTICE 'Non physical item, always deliverable'; + RETURN 1; + END IF; + + SELECT 1 INTO v_inoutExists FROM m_inoutline iol + JOIN m_inout io ON iol.m_inout_id = io.m_inout_id + WHERE iol.c_orderline_id = c_order_line_id AND (io.docstatus IN ('IP', 'WC', 'IN')); + + -- If an in-out line is in progress this is not a candidate + IF v_inoutExists = 1 THEN + -- RAISE NOTICE 'Already being shipped'; + RETURN 0; + END IF; + + -- Check delivery rule + SELECT DeliveryRule INTO + v_deliveryRule + FROM + C_Order where C_Order_ID=v_order_id; + + IF v_deliveryRule='F' THEN + -- RAISE NOTICE 'Delivery rule = Force'; + RETURN 1; + END IF; -- Force + + v_qtytodeliver := v_qtyordered - v_qtydelivered; + IF v_qtytodeliver = 0 THEN + -- RAISE NOTICE 'Nothing to deliver'; + RETURN 0; + END IF; + + IF v_DeliveryPolicy = 'O' THEN -- Deliver in strict order, compare with qty allocated + BEGIN + -- RAISE NOTICE 'Delivery policy = Strict order'; + + CASE v_deliveryRule + WHEN 'L' THEN -- Complete line + IF v_qtytodeliver = v_qtyallocated THEN + -- RAISE NOTICE 'Quantity to deliver = qty allocated'; + RETURN 1; + END IF; + WHEN 'O' THEN -- Complete order + IF v_qtytodeliver > v_qtyallocated THEN + -- RAISE NOTICE 'Not enough allocated for complete order'; + RETURN 0; + END IF; + WHEN 'A' THEN -- Availability + IF v_qtyallocated > 0 THEN + -- RAISE NOTICE 'Something to deliver'; + RETURN 1; + END IF; + END CASE; + -- RAISE NOTICE 'No inout candidate'; + RETURN 0; + END; + END IF; + + IF v_DeliveryPolicy = 'N' THEN -- No hold, only compare with on hand + BEGIN + -- RAISE NOTICE 'Delivery policy = No hold'; + SELECT qtyonhand INTO + v_qtyonhand + FROM m_product_stock_v + WHERE M_Product_ID=v_product_id AND M_Warehouse_ID=v_warehouse_id; + + CASE v_deliveryRule + WHEN 'L' THEN -- Complete line + IF (v_qtytodeliver = v_qtyreserved AND v_qtytodeliver <= v_qtyonhand) THEN RETURN 1; END IF; + WHEN 'O' THEN -- Complete order + IF v_qtytodeliver < v_qtyreserved OR v_qtytodeliver >= v_qtyonhand THEN RETURN 0; END IF; + WHEN 'A' THEN -- Availability + IF v_qtyonhand > 0 THEN RETURN 1; END IF; + END CASE; + END; + END IF; + + -- RAISE NOTICE 'Default answer, something to deliver'; + return 1; +END; +/ + +-- INOUT CANDIDATE ORDER + +CREATE OR REPLACE FUNCTION is_inout_candidate_order +( + p_order_id IN NUMBER +) + RETURN CHAR AS + + v_lines_ready NUMBER; + v_lines_total NUMBER; + v_deliveryRule CHAR; +BEGIN + + -- Get order info + -- Only orders that are complete, not delivered, delivery rule anything else than manual and is a sales order + -- can be inout candidates + select DeliveryRule INTO v_deliveryRule FROM C_Order WHERE + c_order_id=p_order_id AND + docstatus = 'CO' AND + isdelivered = 'N' AND + deliveryrule <> 'M' AND + (c_doctype_id IN ( SELECT c_doctype.c_doctype_id FROM c_doctype + WHERE c_doctype.docbasetype = 'SOO' AND c_doctype.docsubtypeso NOT IN('ON','OB','WR'))); + + IF v_deliveryRule IS NULL THEN + RETURN 'N'; + END IF; + + IF v_deliveryRule='F' THEN RETURN 'Y'; END IF; -- Force + + -- Check lines + SELECT sum(is_inout_candidate_orderline(c_orderline_id)), sum(1) + INTO v_lines_ready, v_lines_total + FROM c_orderline where c_order_id=p_order_id; + + CASE v_deliveryRule + WHEN 'L' THEN -- Complete line + IF v_lines_ready > 0 THEN RETURN 'Y'; END IF; + WHEN 'A' THEN -- Availability + IF v_lines_ready > 0 THEN RETURN 'Y'; END IF; + WHEN 'O' THEN -- Complete order + IF v_lines_ready = v_lines_total THEN RETURN 'Y'; END IF; + END CASE; + + return 'N'; +END; +/ + +-- INOUT CANDIDATE + +CREATE OR REPLACE VIEW m_inout_candidate_v AS +SELECT + o.ad_client_id, + o.ad_org_id, + o.c_bpartner_id, + o.c_order_id, + o.documentno, + o.dateordered, + o.c_doctype_id, + o.poreference, + o.description, + o.salesrep_id, + l.m_warehouse_id, + sum((l.qtyordered - l.qtydelivered) * l.priceactual) AS totallines + + FROM c_order o + JOIN c_orderline l ON o.c_order_id = l.c_order_id + WHERE + (l.m_product_id IS NULL OR (EXISTS ( SELECT 1 + FROM m_product p + WHERE l.m_product_id = p.m_product_id AND p.isexcludeautodelivery = 'N'))) AND + (l.m_product_id IS NOT NULL OR l.c_charge_id IS NOT NULL) AND + is_inout_candidate_order(o.c_order_id) = 'Y' + + GROUP BY o.ad_client_id, o.ad_org_id, o.c_bpartner_id, o.c_order_id, o.documentno, o.dateordered, o.c_doctype_id, o.poreference, o.description, o.salesrep_id, l.m_warehouse_id; diff --git a/migration/360lts-release/postgresql/742_FR3004020_DeliveryPolicyPostgresql.sql b/migration/360lts-release/postgresql/742_FR3004020_DeliveryPolicyPostgresql.sql new file mode 100644 index 0000000000..4b503d49e3 --- /dev/null +++ b/migration/360lts-release/postgresql/742_FR3004020_DeliveryPolicyPostgresql.sql @@ -0,0 +1,360 @@ +-- 2010-maj-19 14:36:56 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Element (AD_Client_ID,AD_Element_ID,AD_Org_ID,ColumnName,Created,CreatedBy,Description,EntityType,Help,IsActive,Name,PrintName,Updated,UpdatedBy) VALUES (0,54156,0,'QtyAllocated',TO_TIMESTAMP('2010-05-19 14:36:56','YYYY-MM-DD HH24:MI:SS'),100,'Allocated quantity','D','Allocated quantity is the quantity that is actually reserved for a specific customer. The customer "owns" this quantity. The allocated quantity can never be more than what''s in stock (as opposed to resevedQty).','Y','Qty Allocated','Qty Allocated',TO_TIMESTAMP('2010-05-19 14:36:56','YYYY-MM-DD HH24:MI:SS'),100) +; + +-- 2010-maj-19 14:36:56 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Element_Trl (AD_Language,AD_Element_ID, Description,Help,Name,PO_Description,PO_Help,PO_Name,PO_PrintName,PrintName, IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy) SELECT l.AD_Language,t.AD_Element_ID, t.Description,t.Help,t.Name,t.PO_Description,t.PO_Help,t.PO_Name,t.PO_PrintName,t.PrintName, 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy FROM AD_Language l, AD_Element t WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.AD_Element_ID=54156 AND NOT EXISTS (SELECT * FROM AD_Element_Trl tt WHERE tt.AD_Language=l.AD_Language AND tt.AD_Element_ID=t.AD_Element_ID) +; + +-- 2010-maj-19 14:42:55 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Element (AD_Client_ID,AD_Element_ID,AD_Org_ID,ColumnName,Created,CreatedBy,Description,EntityType,Help,IsActive,Name,PrintName,Updated,UpdatedBy) VALUES (0,54157,0,'DeliveryPolicy',TO_TIMESTAMP('2010-05-19 14:42:55','YYYY-MM-DD HH24:MI:SS'),100,'Delivery Policy','D','The delivery policy determines how outbound orders will be allocated. +The default delivery policy is to deliver fulfilled orders as soon as possible even if it means +other non fulfilled orders also needs the items being delivered on the fulfilled orders.','Y','Delivery Policy','Delivery Policy',TO_TIMESTAMP('2010-05-19 14:42:55','YYYY-MM-DD HH24:MI:SS'),100) +; + +-- 2010-maj-19 14:42:55 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Element_Trl (AD_Language,AD_Element_ID, Description,Help,Name,PO_Description,PO_Help,PO_Name,PO_PrintName,PrintName, IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy) SELECT l.AD_Language,t.AD_Element_ID, t.Description,t.Help,t.Name,t.PO_Description,t.PO_Help,t.PO_Name,t.PO_PrintName,t.PrintName, 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy FROM AD_Language l, AD_Element t WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.AD_Element_ID=54157 AND NOT EXISTS (SELECT * FROM AD_Element_Trl tt WHERE tt.AD_Language=l.AD_Language AND tt.AD_Element_ID=t.AD_Element_ID) +; + +-- 2010-maj-19 14:43:59 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Reference (AD_Client_ID,AD_Org_ID,AD_Reference_ID,Created,CreatedBy,Description,EntityType,IsActive,IsOrderByValue,Name,Updated,UpdatedBy,ValidationType) VALUES (0,0,53355,TO_TIMESTAMP('2010-05-19 14:43:59','YYYY-MM-DD HH24:MI:SS'),100,'List of Delivery Policies','D','Y','N','DeliveryPolicies',TO_TIMESTAMP('2010-05-19 14:43:59','YYYY-MM-DD HH24:MI:SS'),100,'L') +; + +-- 2010-maj-19 14:43:59 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Reference_Trl (AD_Language,AD_Reference_ID, Description,Help,Name, IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy) SELECT l.AD_Language,t.AD_Reference_ID, t.Description,t.Help,t.Name, 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy FROM AD_Language l, AD_Reference t WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.AD_Reference_ID=53355 AND NOT EXISTS (SELECT * FROM AD_Reference_Trl tt WHERE tt.AD_Language=l.AD_Language AND tt.AD_Reference_ID=t.AD_Reference_ID) +; + +-- 2010-maj-19 14:44:40 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Ref_List (AD_Client_ID,AD_Org_ID,AD_Reference_ID,AD_Ref_List_ID,Created,CreatedBy,Description,EntityType,IsActive,Name,Updated,UpdatedBy,Value) VALUES (0,0,53355,53581,TO_TIMESTAMP('2010-05-19 14:44:40','YYYY-MM-DD HH24:MI:SS'),100,'Default delivery policy - deliver as soon as orders are fulfilled according to delivery rule','D','Y','No Hold',TO_TIMESTAMP('2010-05-19 14:44:40','YYYY-MM-DD HH24:MI:SS'),100,'N') +; + +-- 2010-maj-19 14:44:40 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Ref_List_Trl (AD_Language,AD_Ref_List_ID, Description,Name, IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy) SELECT l.AD_Language,t.AD_Ref_List_ID, t.Description,t.Name, 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy FROM AD_Language l, AD_Ref_List t WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.AD_Ref_List_ID=53581 AND NOT EXISTS (SELECT * FROM AD_Ref_List_Trl tt WHERE tt.AD_Language=l.AD_Language AND tt.AD_Ref_List_ID=t.AD_Ref_List_ID) +; + +-- 2010-maj-19 14:45:59 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Ref_List (AD_Client_ID,AD_Org_ID,AD_Reference_ID,AD_Ref_List_ID,Created,CreatedBy,Description,EntityType,IsActive,Name,Updated,UpdatedBy,Value) VALUES (0,0,53355,53582,TO_TIMESTAMP('2010-05-19 14:45:59','YYYY-MM-DD HH24:MI:SS'),100,'Allocate items to orders and fulfill them in strict order.','D','Y','Strict order',TO_TIMESTAMP('2010-05-19 14:45:59','YYYY-MM-DD HH24:MI:SS'),100,'O') +; + +-- 2010-maj-19 14:45:59 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Ref_List_Trl (AD_Language,AD_Ref_List_ID, Description,Name, IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy) SELECT l.AD_Language,t.AD_Ref_List_ID, t.Description,t.Name, 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy FROM AD_Language l, AD_Ref_List t WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.AD_Ref_List_ID=53582 AND NOT EXISTS (SELECT * FROM AD_Ref_List_Trl tt WHERE tt.AD_Language=l.AD_Language AND tt.AD_Ref_List_ID=t.AD_Ref_List_ID) +; + +-- 2010-maj-19 14:47:45 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Column (AD_Client_ID,AD_Column_ID,AD_Element_ID,AD_Org_ID,AD_Reference_ID,AD_Table_ID,ColumnName,Created,CreatedBy,DefaultValue,Description,EntityType,FieldLength,Help,IsActive,IsAllowLogging,IsAlwaysUpdateable,IsAutocomplete,IsEncrypted,IsIdentifier,IsKey,IsMandatory,IsParent,IsSelectionColumn,IsSyncDatabase,IsTranslated,IsUpdateable,Name,SeqNo,Updated,UpdatedBy,Version) VALUES (0,59188,54156,0,29,260,'QtyAllocated',TO_TIMESTAMP('2010-05-19 14:47:45','YYYY-MM-DD HH24:MI:SS'),100,'0','Allocated quantity','D',22,'Allocated quantity is the quantity that is actually reserved for a specific customer. The customer "owns" this quantity. The allocated quantity can never be more than what''s in stock (as opposed to resevedQty).','Y','Y','N','N','N','N','N','N','N','N','N','N','Y','Qty Allocated',0,TO_TIMESTAMP('2010-05-19 14:47:45','YYYY-MM-DD HH24:MI:SS'),100,1.000000000000) +; + +-- 2010-maj-19 14:47:45 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Column_Trl (AD_Language,AD_Column_ID, Name, IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy) SELECT l.AD_Language,t.AD_Column_ID, t.Name, 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy FROM AD_Language l, AD_Column t WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.AD_Column_ID=59188 AND NOT EXISTS (SELECT * FROM AD_Column_Trl tt WHERE tt.AD_Language=l.AD_Language AND tt.AD_Column_ID=t.AD_Column_ID) +; + +-- 2010-maj-19 14:48:05 CEST +-- FR 3002040 - Delivery Policy +ALTER TABLE C_OrderLine ADD COLUMN QtyAllocated NUMERIC DEFAULT '0' +; + +-- 2010-maj-19 14:49:07 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Column (AD_Client_ID,AD_Column_ID,AD_Element_ID,AD_Org_ID,AD_Reference_ID,AD_Table_ID,ColumnName,Created,CreatedBy,Description,EntityType,FieldLength,Help,IsActive,IsAllowLogging,IsAlwaysUpdateable,IsAutocomplete,IsEncrypted,IsIdentifier,IsKey,IsMandatory,IsParent,IsSelectionColumn,IsSyncDatabase,IsTranslated,IsUpdateable,Name,SeqNo,Updated,UpdatedBy,Version) VALUES (0,59189,54156,0,29,250,'QtyAllocated',TO_TIMESTAMP('2010-05-19 14:49:07','YYYY-MM-DD HH24:MI:SS'),100,'Allocated quantity','D',22,'Allocated quantity is the quantity that is actually reserved for a specific customer. The customer "owns" this quantity. The allocated quantity can never be more than what''s in stock (as opposed to resevedQty).','Y','Y','N','N','N','N','N','N','N','N','N','N','Y','Qty Allocated',0,TO_TIMESTAMP('2010-05-19 14:49:07','YYYY-MM-DD HH24:MI:SS'),100,1.000000000000) +; + +-- 2010-maj-19 14:49:07 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Column_Trl (AD_Language,AD_Column_ID, Name, IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy) SELECT l.AD_Language,t.AD_Column_ID, t.Name, 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy FROM AD_Language l, AD_Column t WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.AD_Column_ID=59189 AND NOT EXISTS (SELECT * FROM AD_Column_Trl tt WHERE tt.AD_Language=l.AD_Language AND tt.AD_Column_ID=t.AD_Column_ID) +; + +-- 2010-maj-19 14:49:18 CEST +-- FR 3002040 - Delivery Policy +ALTER TABLE M_Storage ADD COLUMN QtyAllocated NUMERIC DEFAULT '0' +; + +-- 2010-maj-19 14:49:28 CEST +-- FR 3002040 - Delivery Policy +UPDATE AD_Column SET DefaultValue='0',Updated=TO_TIMESTAMP('2010-05-19 14:49:28','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Column_ID=59189 +; + +-- Add field QtyAllocated to Sales Order Line + +-- 2010-maj-19 14:54:08 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Field (AD_Client_ID,AD_Column_ID,AD_FieldGroup_ID,AD_Field_ID,AD_Org_ID,AD_Tab_ID,Created,CreatedBy,Description,DisplayLength,DisplayLogic,EntityType,Help,IsActive,IsCentrallyMaintained,IsDisplayed,IsEncrypted,IsFieldOnly,IsHeading,IsReadOnly,IsSameLine,Name,SeqNo,Updated,UpdatedBy) VALUES (0,59188,102,58877,0,187,TO_TIMESTAMP('2010-05-19 14:54:08','YYYY-MM-DD HH24:MI:SS'),100,'Allocated quantity',26,'@OrderType@=''OB'' | @OrderType@=''SO'' | @Processed@=''Y''','D','Allocated quantity is the quantity that is actually reserved for a specific customer. The customer "owns" this quantity. The allocated quantity can never be more than what''s in stock (as opposed to resevedQty).','Y','Y','Y','N','N','N','Y','N','Qty Allocated',190,TO_TIMESTAMP('2010-05-19 14:54:08','YYYY-MM-DD HH24:MI:SS'),100) +; + +-- 2010-maj-19 14:54:08 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Field_Trl (AD_Language,AD_Field_ID, Description,Help,Name, IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy) SELECT l.AD_Language,t.AD_Field_ID, t.Description,t.Help,t.Name, 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy FROM AD_Language l, AD_Field t WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.AD_Field_ID=58877 AND NOT EXISTS (SELECT * FROM AD_Field_Trl tt WHERE tt.AD_Language=l.AD_Language AND tt.AD_Field_ID=t.AD_Field_ID) +; + +-- 2010-maj-19 14:54:35 CEST +-- FR 3002040 - Delivery Policy +UPDATE AD_Field SET SeqNo=210,IsDisplayed='Y' WHERE AD_Field_ID=58877 +; + +-- 2010-maj-19 14:54:35 CEST +-- FR 3002040 - Delivery Policy +UPDATE AD_Field SET SeqNo=220,IsDisplayed='Y' WHERE AD_Field_ID=1135 +; + +-- 2010-maj-19 14:54:35 CEST +-- FR 3002040 - Delivery Policy +UPDATE AD_Field SET SeqNo=230,IsDisplayed='Y' WHERE AD_Field_ID=10829 +; + +-- 2010-maj-19 14:54:35 CEST +-- FR 3002040 - Delivery Policy +UPDATE AD_Field SET SeqNo=240,IsDisplayed='Y' WHERE AD_Field_ID=1138 +; + +-- 2010-maj-19 14:54:35 CEST +-- FR 3002040 - Delivery Policy +UPDATE AD_Field SET SeqNo=250,IsDisplayed='Y' WHERE AD_Field_ID=1137 +; + +-- 2010-maj-19 14:54:35 CEST +-- FR 3002040 - Delivery Policy +UPDATE AD_Field SET SeqNo=260,IsDisplayed='Y' WHERE AD_Field_ID=2115 +; + +-- 2010-maj-19 14:54:35 CEST +-- FR 3002040 - Delivery Policy +UPDATE AD_Field SET SeqNo=270,IsDisplayed='Y' WHERE AD_Field_ID=1141 +; + +-- 2010-maj-19 14:54:35 CEST +-- FR 3002040 - Delivery Policy +UPDATE AD_Field SET SeqNo=280,IsDisplayed='Y' WHERE AD_Field_ID=3124 +; + +-- 2010-maj-19 14:54:35 CEST +-- FR 3002040 - Delivery Policy +UPDATE AD_Field SET SeqNo=290,IsDisplayed='Y' WHERE AD_Field_ID=12745 +; + +-- 2010-maj-19 14:54:35 CEST +-- FR 3002040 - Delivery Policy +UPDATE AD_Field SET SeqNo=300,IsDisplayed='Y' WHERE AD_Field_ID=13644 +; + +-- 2010-maj-19 14:54:35 CEST +-- FR 3002040 - Delivery Policy +UPDATE AD_Field SET SeqNo=310,IsDisplayed='Y' WHERE AD_Field_ID=13645 +; + +-- 2010-maj-19 14:54:35 CEST +-- FR 3002040 - Delivery Policy +UPDATE AD_Field SET SeqNo=320,IsDisplayed='Y' WHERE AD_Field_ID=13691 +; + +-- 2010-maj-19 14:54:35 CEST +-- FR 3002040 - Delivery Policy +UPDATE AD_Field SET SeqNo=330,IsDisplayed='Y' WHERE AD_Field_ID=13650 +; + +-- 2010-maj-19 14:54:35 CEST +-- FR 3002040 - Delivery Policy +UPDATE AD_Field SET SeqNo=340,IsDisplayed='Y' WHERE AD_Field_ID=13651 +; + +-- 2010-maj-19 14:54:35 CEST +-- FR 3002040 - Delivery Policy +UPDATE AD_Field SET SeqNo=350,IsDisplayed='Y' WHERE AD_Field_ID=2880 +; + +-- 2010-maj-19 14:54:35 CEST +-- FR 3002040 - Delivery Policy +UPDATE AD_Field SET SeqNo=360,IsDisplayed='Y' WHERE AD_Field_ID=12744 +; + +-- 2010-maj-19 14:54:35 CEST +-- FR 3002040 - Delivery Policy +UPDATE AD_Field SET SeqNo=370,IsDisplayed='Y' WHERE AD_Field_ID=10332 +; + +-- 2010-maj-19 14:55:07 CEST +-- FR 3002040 - Delivery Policy +UPDATE AD_Field SET Name='Allocated Quantity',Updated=TO_TIMESTAMP('2010-05-19 14:55:07','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=58877 +; + +-- 2010-maj-19 14:55:07 CEST +-- FR 3002040 - Delivery Policy +UPDATE AD_Field_Trl SET IsTranslated='N' WHERE AD_Field_ID=58877 +; + +-- Add field QtyAllocated to MStorage + +-- 2010-maj-19 14:56:52 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Field (AD_Client_ID,AD_Column_ID,AD_Field_ID,AD_Org_ID,AD_Tab_ID,Created,CreatedBy,Description,DisplayLength,EntityType,Help,IsActive,IsCentrallyMaintained,IsDisplayed,IsEncrypted,IsFieldOnly,IsHeading,IsReadOnly,IsSameLine,Name,SeqNo,SortNo,Updated,UpdatedBy) VALUES (0,59189,58878,0,179,TO_TIMESTAMP('2010-05-19 14:56:52','YYYY-MM-DD HH24:MI:SS'),100,'Allocated quantity',26,'D','Allocated quantity is the quantity that is actually reserved for a specific customer. The customer "owns" this quantity. The allocated quantity can never be more than what''s in stock (as opposed to resevedQty).','Y','Y','Y','N','N','N','N','N','Qty Allocated',110,0,TO_TIMESTAMP('2010-05-19 14:56:52','YYYY-MM-DD HH24:MI:SS'),100) +; + +-- 2010-maj-19 14:56:52 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Field_Trl (AD_Language,AD_Field_ID, Description,Help,Name, IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy) SELECT l.AD_Language,t.AD_Field_ID, t.Description,t.Help,t.Name, 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy FROM AD_Language l, AD_Field t WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.AD_Field_ID=58878 AND NOT EXISTS (SELECT * FROM AD_Field_Trl tt WHERE tt.AD_Language=l.AD_Language AND tt.AD_Field_ID=t.AD_Field_ID) +; + +-- 2010-maj-19 14:57:28 CEST +-- FR 3002040 - Delivery Policy +UPDATE AD_Field SET Name='Allocated Quantity',Updated=TO_TIMESTAMP('2010-05-19 14:57:28','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=58878 +; + +-- 2010-maj-19 14:57:28 CEST +-- FR 3002040 - Delivery Policy +UPDATE AD_Field_Trl SET IsTranslated='N' WHERE AD_Field_ID=58878 +; + +-- Add column Delivery Policy to OrganizationInfo + +-- 2010-maj-19 15:02:03 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Column (AD_Client_ID,AD_Column_ID,AD_Element_ID,AD_Org_ID,AD_Reference_ID,AD_Reference_Value_ID,AD_Table_ID,ColumnName,Created,CreatedBy,Description,EntityType,FieldLength,Help,IsActive,IsAllowLogging,IsAlwaysUpdateable,IsAutocomplete,IsEncrypted,IsIdentifier,IsKey,IsMandatory,IsParent,IsSelectionColumn,IsSyncDatabase,IsTranslated,IsUpdateable,Name,SeqNo,Updated,UpdatedBy,Version) VALUES (0,59190,54157,0,17,53355,228,'DeliveryPolicy',TO_TIMESTAMP('2010-05-19 15:02:03','YYYY-MM-DD HH24:MI:SS'),100,'Delivery Policy','D',1,'The delivery policy determines how outbound orders will be allocated. +The default delivery policy is to deliver fulfilled orders as soon as possible even if it means +other non fulfilled orders also needs the items being delivered on the fulfilled orders.','Y','Y','N','N','N','N','N','N','N','N','N','N','Y','Delivery Policy',0,TO_TIMESTAMP('2010-05-19 15:02:03','YYYY-MM-DD HH24:MI:SS'),100,0) +; + +-- 2010-maj-19 15:02:03 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Column_Trl (AD_Language,AD_Column_ID, Name, IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy) SELECT l.AD_Language,t.AD_Column_ID, t.Name, 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy FROM AD_Language l, AD_Column t WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.AD_Column_ID=59190 AND NOT EXISTS (SELECT * FROM AD_Column_Trl tt WHERE tt.AD_Language=l.AD_Language AND tt.AD_Column_ID=t.AD_Column_ID) +; + +-- 2010-maj-19 15:02:11 CEST +-- FR 3002040 - Delivery Policy +UPDATE AD_Column SET Version=1.000000000000,Updated=TO_TIMESTAMP('2010-05-19 15:02:11','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Column_ID=59190 +; + +-- 2010-maj-19 15:02:18 CEST +-- FR 3002040 - Delivery Policy +ALTER TABLE AD_OrgInfo ADD COLUMN DeliveryPolicy CHAR(1) DEFAULT NULL +; + +-- 2010-maj-19 15:06:47 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Column (AD_Client_ID,AD_Column_ID,AD_Element_ID,AD_Org_ID,AD_Reference_ID,AD_Reference_Value_ID,AD_Table_ID,ColumnName,Created,CreatedBy,DefaultValue,Description,EntityType,FieldLength,Help,IsActive,IsAllowLogging,IsAlwaysUpdateable,IsAutocomplete,IsEncrypted,IsIdentifier,IsKey,IsMandatory,IsParent,IsSelectionColumn,IsSyncDatabase,IsTranslated,IsUpdateable,Name,SeqNo,Updated,UpdatedBy,Version) VALUES (0,59191,54157,0,17,53355,227,'DeliveryPolicy',TO_TIMESTAMP('2010-05-19 15:06:46','YYYY-MM-DD HH24:MI:SS'),100,'N','Delivery Policy','D',1,'The delivery policy determines how outbound orders will be allocated. +The default delivery policy is to deliver fulfilled orders as soon as possible even if it means +other non fulfilled orders also needs the items being delivered on the fulfilled orders.','Y','Y','N','N','N','N','N','N','N','N','N','N','Y','Delivery Policy',0,TO_TIMESTAMP('2010-05-19 15:06:46','YYYY-MM-DD HH24:MI:SS'),100,1.000000000000) +; + +-- 2010-maj-19 15:06:47 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Column_Trl (AD_Language,AD_Column_ID, Name, IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy) SELECT l.AD_Language,t.AD_Column_ID, t.Name, 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy FROM AD_Language l, AD_Column t WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.AD_Column_ID=59191 AND NOT EXISTS (SELECT * FROM AD_Column_Trl tt WHERE tt.AD_Language=l.AD_Language AND tt.AD_Column_ID=t.AD_Column_ID) +; + +-- 2010-maj-19 15:06:53 CEST +-- FR 3002040 - Delivery Policy +ALTER TABLE AD_ClientInfo ADD COLUMN DeliveryPolicy CHAR(1) DEFAULT 'N' +; + +-- Add field to Client Info Window + +-- 2010-maj-19 15:10:10 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Field (AD_Client_ID,AD_Column_ID,AD_FieldGroup_ID,AD_Field_ID,AD_Org_ID,AD_Tab_ID,Created,CreatedBy,DefaultValue,Description,DisplayLength,EntityType,Help,IsActive,IsCentrallyMaintained,IsDisplayed,IsEncrypted,IsFieldOnly,IsHeading,IsReadOnly,IsSameLine,Name,SeqNo,Updated,UpdatedBy) VALUES (0,59191,118,58879,0,169,TO_TIMESTAMP('2010-05-19 15:10:10','YYYY-MM-DD HH24:MI:SS'),100,'N','Delivery Policy',14,'U','The delivery policy determines how outbound orders will be allocated. +The default delivery policy is to deliver fulfilled orders as soon as possible even if it means +other non fulfilled orders also needs the items being delivered on the fulfilled orders.','Y','Y','Y','N','N','N','N','Y','Delivery Policy',155,TO_TIMESTAMP('2010-05-19 15:10:10','YYYY-MM-DD HH24:MI:SS'),100) +; + +-- 2010-maj-19 15:10:10 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Field_Trl (AD_Language,AD_Field_ID, Description,Help,Name, IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy) SELECT l.AD_Language,t.AD_Field_ID, t.Description,t.Help,t.Name, 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy FROM AD_Language l, AD_Field t WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.AD_Field_ID=58879 AND NOT EXISTS (SELECT * FROM AD_Field_Trl tt WHERE tt.AD_Language=l.AD_Language AND tt.AD_Field_ID=t.AD_Field_ID) +; + +-- Add DeliveryPolicy field to Organization Window + +-- 2010-maj-19 15:12:01 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Field (AD_Client_ID,AD_Column_ID,AD_Field_ID,AD_Org_ID,AD_Tab_ID,Created,CreatedBy,Description,DisplayLength,EntityType,Help,IsActive,IsCentrallyMaintained,IsDisplayed,IsEncrypted,IsFieldOnly,IsHeading,IsReadOnly,IsSameLine,Name,SeqNo,SortNo,Updated,UpdatedBy) VALUES (0,59190,58880,0,170,TO_TIMESTAMP('2010-05-19 15:12:01','YYYY-MM-DD HH24:MI:SS'),100,'Delivery Policy',0,'D','The delivery policy determines how outbound orders will be allocated. +The default delivery policy is to deliver fulfilled orders as soon as possible even if it means +other non fulfilled orders also needs the items being delivered on the fulfilled orders.','Y','Y','Y','N','N','N','N','N','Delivery Policy',85,0,TO_TIMESTAMP('2010-05-19 15:12:01','YYYY-MM-DD HH24:MI:SS'),100) +; + +-- 2010-maj-19 15:12:01 CEST +-- FR 3002040 - Delivery Policy +INSERT INTO AD_Field_Trl (AD_Language,AD_Field_ID, Description,Help,Name, IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy) SELECT l.AD_Language,t.AD_Field_ID, t.Description,t.Help,t.Name, 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy FROM AD_Language l, AD_Field t WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.AD_Field_ID=58880 AND NOT EXISTS (SELECT * FROM AD_Field_Trl tt WHERE tt.AD_Language=l.AD_Language AND tt.AD_Field_ID=t.AD_Field_ID) +; + +-- 2010-maj-19 15:12:11 CEST +-- FR 3002040 - Delivery Policy +UPDATE AD_Field SET DisplayLength=22,Updated=TO_TIMESTAMP('2010-05-19 15:12:11','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=58880 +; + +-- Jun 22, 2010 1:47:33 PM CEST +-- FR 3004020 Delivery Policy +INSERT INTO AD_Process (AccessLevel,AD_Client_ID,AD_Org_ID,AD_Process_ID,Classname,CopyFromProcess,Created,CreatedBy,Description,EntityType,Help,IsActive,IsBetaFunctionality,IsDirectPrint,IsReport,IsServerProcess,Name,ShowHelp,Statistic_Count,Statistic_Seconds,Updated,UpdatedBy,Value) VALUES ('3',0,0,53217,'org.adempiere.process.AllocateSalesOrders','N',TO_TIMESTAMP('2010-06-22 13:47:31','YYYY-MM-DD HH24:MI:SS'),100,'Allocate available on hand stock to sales orders','D','This process is only necessary for delivery policy = Strict order','Y','N','N','N','N','Allocate Sales Orders','Y',0,0,TO_TIMESTAMP('2010-06-22 13:47:31','YYYY-MM-DD HH24:MI:SS'),100,'AllocateSalesOrders') +; + +-- Jun 22, 2010 1:47:33 PM CEST +-- FR 3004020 Delivery Policy +INSERT INTO AD_Process_Trl (AD_Language,AD_Process_ID, Description,Help,Name, IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy) SELECT l.AD_Language,t.AD_Process_ID, t.Description,t.Help,t.Name, 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy FROM AD_Language l, AD_Process t WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.AD_Process_ID=53217 AND NOT EXISTS (SELECT * FROM AD_Process_Trl tt WHERE tt.AD_Language=l.AD_Language AND tt.AD_Process_ID=t.AD_Process_ID) +; + +-- Jun 22, 2010 1:48:24 PM CEST +-- FR 3004020 Delivery Policy +INSERT INTO AD_Process_Para (AD_Client_ID,AD_Element_ID,AD_Org_ID,AD_Process_ID,AD_Process_Para_ID,AD_Reference_ID,ColumnName,Created,CreatedBy,Description,EntityType,FieldLength,IsActive,IsCentrallyMaintained,IsMandatory,IsRange,Name,SeqNo,Updated,UpdatedBy) VALUES (0,459,0,53217,53422,18,'M_Warehouse_ID',TO_TIMESTAMP('2010-06-22 13:48:23','YYYY-MM-DD HH24:MI:SS'),100,'Warehouse where allocation should be executed.','D',0,'Y','Y','N','N','Warehouse',10,TO_TIMESTAMP('2010-06-22 13:48:23','YYYY-MM-DD HH24:MI:SS'),100) +; + +-- Jun 22, 2010 1:48:24 PM CEST +-- FR 3004020 Delivery Policy +INSERT INTO AD_Process_Para_Trl (AD_Language,AD_Process_Para_ID, Description,Help,Name, IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy) SELECT l.AD_Language,t.AD_Process_Para_ID, t.Description,t.Help,t.Name, 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy FROM AD_Language l, AD_Process_Para t WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.AD_Process_Para_ID=53422 AND NOT EXISTS (SELECT * FROM AD_Process_Para_Trl tt WHERE tt.AD_Language=l.AD_Language AND tt.AD_Process_Para_ID=t.AD_Process_Para_ID) +; + +-- Jun 22, 2010 1:49:05 PM CEST +-- FR 3004020 Delivery Policy +INSERT INTO AD_Menu ("action",AD_Client_ID,AD_Menu_ID,AD_Org_ID,AD_Process_ID,Created,CreatedBy,Description,EntityType,IsActive,IsCentrallyMaintained,IsReadOnly,IsSOTrx,IsSummary,Name,Updated,UpdatedBy) VALUES ('P',0,53283,0,53217,TO_TIMESTAMP('2010-06-22 13:49:04','YYYY-MM-DD HH24:MI:SS'),100,'Allocate available on hand stock to sales orders','D','Y','Y','N','N','N','Allocate Sales Orders',TO_TIMESTAMP('2010-06-22 13:49:04','YYYY-MM-DD HH24:MI:SS'),100) +; + +-- Jun 22, 2010 1:49:05 PM CEST +-- FR 3004020 Delivery Policy +INSERT INTO AD_Menu_Trl (AD_Language,AD_Menu_ID, Description,Name, IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy) SELECT l.AD_Language,t.AD_Menu_ID, t.Description,t.Name, 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy FROM AD_Language l, AD_Menu t WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.AD_Menu_ID=53283 AND NOT EXISTS (SELECT * FROM AD_Menu_Trl tt WHERE tt.AD_Language=l.AD_Language AND tt.AD_Menu_ID=t.AD_Menu_ID) +; + +-- Jun 22, 2010 1:49:05 PM CEST +-- FR 3004020 Delivery Policy +INSERT INTO AD_TreeNodeMM (AD_Client_ID,AD_Org_ID, IsActive,Created,CreatedBy,Updated,UpdatedBy, AD_Tree_ID, Node_ID, Parent_ID, SeqNo) SELECT t.AD_Client_ID, 0, 'Y', CURRENT_TIMESTAMP, 100, CURRENT_TIMESTAMP, 100,t.AD_Tree_ID, 53283, 0, 999 FROM AD_Tree t WHERE t.AD_Client_ID=0 AND t.IsActive='Y' AND t.IsAllNodes='Y' AND t.TreeType='MM' AND NOT EXISTS (SELECT * FROM AD_TreeNodeMM e WHERE e.AD_Tree_ID=t.AD_Tree_ID AND Node_ID=53283) +; + +-- Jun 22, 2010 1:49:15 PM CEST +-- FR 3004020 Delivery Policy +UPDATE AD_TreeNodeMM SET Parent_ID=459, SeqNo=0, Updated=CURRENT_TIMESTAMP WHERE AD_Tree_ID=10 AND Node_ID=53283 +; + +-- Jun 22, 2010 1:49:15 PM CEST +-- FR 3004020 Delivery Policy +UPDATE AD_TreeNodeMM SET Parent_ID=459, SeqNo=1, Updated=CURRENT_TIMESTAMP WHERE AD_Tree_ID=10 AND Node_ID=346 +; + +-- Jun 22, 2010 1:49:15 PM CEST +-- FR 3004020 Delivery Policy +UPDATE AD_TreeNodeMM SET Parent_ID=459, SeqNo=2, Updated=CURRENT_TIMESTAMP WHERE AD_Tree_ID=10 AND Node_ID=53132 +; + +-- Jun 22, 2010 1:49:15 PM CEST +-- FR 3004020 Delivery Policy +UPDATE AD_TreeNodeMM SET Parent_ID=459, SeqNo=3, Updated=CURRENT_TIMESTAMP WHERE AD_Tree_ID=10 AND Node_ID=193 +; + +-- Jun 22, 2010 1:49:15 PM CEST +-- FR 3004020 Delivery Policy +UPDATE AD_TreeNodeMM SET Parent_ID=459, SeqNo=4, Updated=CURRENT_TIMESTAMP WHERE AD_Tree_ID=10 AND Node_ID=180 +; + +-- Jun 22, 2010 1:49:15 PM CEST +-- FR 3004020 Delivery Policy +UPDATE AD_TreeNodeMM SET Parent_ID=459, SeqNo=5, Updated=CURRENT_TIMESTAMP WHERE AD_Tree_ID=10 AND Node_ID=494 +; + +-- Jun 22, 2010 1:49:15 PM CEST +-- FR 3004020 Delivery Policy +UPDATE AD_TreeNodeMM SET Parent_ID=459, SeqNo=6, Updated=CURRENT_TIMESTAMP WHERE AD_Tree_ID=10 AND Node_ID=444 +; + diff --git a/migration/360lts-release/postgresql/743_FR3004020_DeliveryPolicyPostgresql.sql b/migration/360lts-release/postgresql/743_FR3004020_DeliveryPolicyPostgresql.sql new file mode 100644 index 0000000000..817869f3b7 --- /dev/null +++ b/migration/360lts-release/postgresql/743_FR3004020_DeliveryPolicyPostgresql.sql @@ -0,0 +1,307 @@ +-- Adjust, alter view M_Product_Stock_v +DROP VIEW IF EXISTS m_product_stock_v; +CREATE OR REPLACE VIEW m_product_stock_v AS + SELECT ms.isactive, ms.created, ms.createdby, ms.updated, ms.updatedby, + ms.m_product_id, mp.value, mp.name, mp.help, + ms.qtyonhand - ms.qtyreserved AS qtyavailable, + ms.qtyonhand, ms.qtyreserved, + ms.qtyallocated, + mp.description, + mw.name AS warehouse, + mw.m_warehouse_id, + mw.ad_client_id, + mw.ad_org_id, + mp.documentnote + FROM m_storage ms + JOIN m_product mp ON ms.m_product_id = mp.m_product_id + JOIN m_locator ml ON ms.m_locator_id = ml.m_locator_id + JOIN m_warehouse mw ON ml.m_warehouse_id = mw.m_warehouse_id + ORDER BY mw.name; + +ALTER TABLE m_product_stock_v OWNER TO adempiere; + +-- Create function isShippable +CREATE OR REPLACE FUNCTION isshippable(product_id numeric) + RETURNS character AS +$BODY$ +DECLARE + v_IsStocked character(1); + v_IsBom character(1); + v_ProductType character(1); + v_return character(1); +BEGIN + IF product_id = NULL THEN + return 'N'; + END IF; + + SELECT IsStocked, IsBom, ProductType + INTO v_IsStocked, v_IsBom, v_ProductType + FROM M_Product WHERE M_Product_ID=product_id; + + IF (v_IsStocked='Y' AND v_ProductType='I' AND v_IsBom='N') THEN + v_return := 'Y'; + ELSE + v_return := 'N'; + END IF; + + return v_return; +END; +$BODY$ + LANGUAGE 'plpgsql' VOLATILE + COST 100; +ALTER FUNCTION isshippable(numeric) OWNER TO adempiere; + +-- Function to get delivery policy + +CREATE OR REPLACE FUNCTION get_delivery_policy(warehouse_id numeric) + RETURNS character AS +$BODY$ +DECLARE + v_orgId numeric; + v_clientId numeric; + v_return character(1); +BEGIN + SELECT ad_client_id, ad_org_id INTO + v_clientId, v_orgId FROM + M_Warehouse WHERE M_Warehouse_ID=warehouse_id; + + SELECT COALESCE(ad_orginfo.deliverypolicy, ad_clientinfo.deliverypolicy) INTO + v_return + FROM AD_ClientInfo + JOIN AD_OrgInfo ON (AD_ClientInfo.AD_Client_ID=AD_OrgInfo.AD_Client_ID) + WHERE AD_ClientInfo.AD_Client_ID = v_clientId AND + AD_OrgInfo.AD_Org_ID = v_orgId; + + return v_return; +END; +$BODY$ + + LANGUAGE 'plpgsql' VOLATILE + COST 100; +ALTER FUNCTION get_delivery_policy(numeric) OWNER TO adempiere; + +-- Get allocated on order + +CREATE OR REPLACE FUNCTION get_allocated_on_order(p_product_id numeric, p_warehouse_id numeric) + RETURNS numeric AS +$BODY$ + +DECLARE + + v_sum numeric; + +BEGIN + -- Get Product Attribute Set Instance + SELECT sum(qtyallocated) into v_sum from C_OrderLine ol + JOIN C_Order o on (o.C_Order_ID=ol.C_Order_ID) + WHERE + M_Product_ID=p_product_id AND + COALESCE(ol.M_Warehouse_ID, o.M_Warehouse_ID)=p_warehouse_id; + + RETURN v_sum; +END; +$BODY$ + LANGUAGE 'plpgsql' VOLATILE + COST 100; +ALTER FUNCTION get_allocated_on_order(numeric, numeric) OWNER TO adempiere; + +-- IN OUT CANDIDATE ORDERLINE + +-- DROP FUNCTION is_inout_candidate_orderline(numeric); +CREATE OR REPLACE FUNCTION is_inout_candidate_orderline(c_order_line_id numeric) + RETURNS numeric AS +$BODY$ +DECLARE + v_qtyordered numeric; + v_qtydelivered numeric; + v_qtyallocated numeric; + v_qtyonhand numeric; + v_qtytodeliver numeric; + v_qtyreserved numeric; + v_order_id numeric; + v_inoutExists numeric; + v_warehouse_id numeric; + v_product_id numeric; + v_orderReady numeric; + v_isShippable character(1); + v_deliveryRule character(1); + v_deliveryPolicy character(1); + v_return character(1); +BEGIN + SELECT qtyordered, qtydelivered, qtyallocated, qtyreserved, c_order_id, + get_delivery_policy(m_warehouse_id), isshippable(m_product_id), + m_warehouse_id, m_product_id + INTO + v_qtyordered, v_qtydelivered, v_qtyallocated, v_qtyreserved, v_order_id, + v_deliveryPolicy, v_isShippable, + v_warehouse_id, v_product_id + FROM + C_OrderLine where C_OrderLine_ID=c_order_line_id; + + -- If all is already delivered then it's not a candidate + IF v_qtyordered = v_qtydelivered THEN + -- RAISE NOTICE 'All is delivered'; + RETURN 0; + END IF; + + -- Non shippable (ie non physical items) are always inout candidate + IF v_isShippable='N' THEN + -- RAISE NOTICE 'Non physical item, always deliverable'; + RETURN 1; + END IF; + + SELECT 1 INTO v_inoutExists FROM m_inoutline iol + JOIN m_inout io ON iol.m_inout_id = io.m_inout_id + WHERE iol.c_orderline_id = c_order_line_id AND (io.docstatus = ANY (ARRAY['IP'::bpchar, 'WC'::bpchar, 'IN'::bpchar])); + + -- If an in-out line is in progress this is not a candidate + IF v_inoutExists = 1 THEN + -- RAISE NOTICE 'Already being shipped'; + RETURN 0; + END IF; + + -- Check delivery rule + SELECT DeliveryRule INTO + v_deliveryRule + FROM + C_Order where C_Order_ID=v_order_id; + + IF v_deliveryRule='F' THEN + -- RAISE NOTICE 'Delivery rule = Force'; + RETURN 1; + END IF; -- Force + + v_qtytodeliver := v_qtyordered - v_qtydelivered; + IF v_qtytodeliver = 0 THEN + -- RAISE NOTICE 'Nothing to deliver'; + RETURN 0; + END IF; + + IF v_DeliveryPolicy = 'O' THEN -- Deliver in strict order, compare with qty allocated + BEGIN + -- RAISE NOTICE 'Delivery policy = Strict order'; + + CASE v_deliveryRule + WHEN 'L' THEN -- Complete line + IF v_qtytodeliver = v_qtyallocated THEN + -- RAISE NOTICE 'Quantity to deliver = qty allocated'; + RETURN 1; + END IF; + WHEN 'O' THEN -- Complete order + IF v_qtytodeliver > v_qtyallocated THEN + -- RAISE NOTICE 'Not enough allocated for complete order'; + RETURN 0; + END IF; + WHEN 'A' THEN -- Availability + IF v_qtyallocated > 0 THEN + -- RAISE NOTICE 'Something to deliver'; + RETURN 1; + END IF; + END CASE; + -- RAISE NOTICE 'No inout candidate'; + RETURN 0; + END; + END IF; + + IF v_DeliveryPolicy = 'N' THEN -- No hold, only compare with on hand + BEGIN + -- RAISE NOTICE 'Delivery policy = No hold'; + SELECT qtyonhand INTO + v_qtyonhand + FROM m_product_stock_v + WHERE M_Product_ID=v_product_id AND M_Warehouse_ID=v_warehouse_id; + + CASE v_deliveryRule + WHEN 'L' THEN -- Complete line + IF (v_qtytodeliver = v_qtyreserved AND v_qtytodeliver <= v_qtyonhand) THEN RETURN 1; END IF; + WHEN 'O' THEN -- Complete order + IF v_qtytodeliver < v_qtyreserved OR v_qtytodeliver >= v_qtyonhand THEN RETURN 0; END IF; + WHEN 'A' THEN -- Availability + IF v_qtyonhand > 0 THEN RETURN 1; END IF; + END CASE; + END; + END IF; + + -- RAISE NOTICE 'Default answer, something to deliver'; + return 1; +END +$BODY$ + LANGUAGE 'plpgsql' VOLATILE + COST 100; +ALTER FUNCTION is_inout_candidate_orderline(numeric) OWNER TO adempiere; + +-- INOUT CANDIDATE ORDER + +CREATE OR REPLACE FUNCTION is_inout_candidate_order(p_order_id numeric) + RETURNS character AS +$BODY$ +DECLARE + v_lines_ready numeric; + v_lines_total numeric; + v_deliveryRule character(1); +BEGIN + + -- Get order info + -- Only orders that are complete, not delivered, delivery rule anything else than manual and is a sales order + -- can be inout candidates + select DeliveryRule INTO v_deliveryRule FROM C_Order WHERE + c_order_id=p_order_id AND + docstatus = 'CO'::bpchar AND + isdelivered = 'N'::bpchar AND + deliveryrule <> 'M'::bpchar AND + (c_doctype_id IN ( SELECT c_doctype.c_doctype_id FROM c_doctype + WHERE c_doctype.docbasetype = 'SOO'::bpchar AND (c_doctype.docsubtypeso <> ALL (ARRAY['ON'::bpchar, 'OB'::bpchar, 'WR'::bpchar])))); + + IF v_deliveryRule IS NULL THEN + RETURN 'N'; + END IF; + + IF v_deliveryRule='F' THEN RETURN 'Y'; END IF; -- Force + + -- Check lines + SELECT sum(is_inout_candidate_orderline(c_orderline_id)), sum(1) + INTO v_lines_ready, v_lines_total + FROM c_orderline where c_order_id=p_order_id; + + CASE v_deliveryRule + WHEN 'L','A' THEN -- Complete line and Availability + IF v_lines_ready > 0 THEN RETURN 'Y'; END IF; + WHEN 'O' THEN -- Complete order + IF v_lines_ready = v_lines_total THEN RETURN 'Y'; END IF; + END CASE; + + return 'N'; +END +$BODY$ + LANGUAGE 'plpgsql' VOLATILE + COST 100; +ALTER FUNCTION is_inout_candidate_order(numeric) OWNER TO adempiere; + +-- INOUT CANDIDATE + +CREATE OR REPLACE VIEW m_inout_candidate_v AS +SELECT + o.ad_client_id, + o.ad_org_id, + o.c_bpartner_id, + o.c_order_id, + o.documentno, + o.dateordered, + o.c_doctype_id, + o.poreference, + o.description, + o.salesrep_id, + l.m_warehouse_id, + sum((l.qtyordered - l.qtydelivered) * l.priceactual) AS totallines + + FROM c_order o + JOIN c_orderline l ON o.c_order_id = l.c_order_id + WHERE + (l.m_product_id IS NULL OR (EXISTS ( SELECT 1 + FROM m_product p + WHERE l.m_product_id = p.m_product_id AND p.isexcludeautodelivery = 'N'::bpchar))) AND + (l.m_product_id IS NOT NULL OR l.c_charge_id IS NOT NULL) AND + is_inout_candidate_order(o.c_order_id) = 'Y' + + GROUP BY o.ad_client_id, o.ad_org_id, o.c_bpartner_id, o.c_order_id, o.documentno, o.dateordered, o.c_doctype_id, o.poreference, o.description, o.salesrep_id, l.m_warehouse_id; +ALTER TABLE m_inout_candidate_v OWNER TO adempiere;