From 984f2b35b73a132a48b40ad9d699fba80c688983 Mon Sep 17 00:00:00 2001 From: usrdno Date: Wed, 23 Jun 2010 07:31:43 +0000 Subject: [PATCH] FR3004020 - Delivery Policy. This is a slightly large commit affecting the core functionality of shipments so I'm happy for all eyeballs I can get on this code. I've tested as thorough one person can do. If there are no undiscovered bugs this doesn't change current functionality unless you change the new setting of "Delivery Policy" available on Client Info and Organization Info. See also http://www.adempiere.com/index.php/Delivery_Policy Link to SF Tracker: http://sourceforge.net/support/tracker.php?aid=3004020 --- .../process/AllocateSalesOrders.java | 227 +++++++++++ .../MaterialReceiptModelValidator.java | 131 +++++++ .../org/compiere/model/I_AD_ClientInfo.java | 33 +- base/src/org/compiere/model/I_AD_OrgInfo.java | 13 + .../src/org/compiere/model/I_C_OrderLine.java | 13 + base/src/org/compiere/model/I_M_Storage.java | 13 + base/src/org/compiere/model/MClientInfo.java | 4 +- base/src/org/compiere/model/MInOut.java | 79 +++- base/src/org/compiere/model/MInventory.java | 4 +- base/src/org/compiere/model/MMovement.java | 8 +- base/src/org/compiere/model/MOrder.java | 20 +- base/src/org/compiere/model/MOrderLine.java | 106 +++++- base/src/org/compiere/model/MOrgInfo.java | 16 + .../src/org/compiere/model/MProjectIssue.java | 2 +- base/src/org/compiere/model/MStorage.java | 59 ++- .../org/compiere/model/X_AD_ClientInfo.java | 66 +++- base/src/org/compiere/model/X_AD_OrgInfo.java | 26 +- .../src/org/compiere/model/X_C_OrderLine.java | 22 +- base/src/org/compiere/model/X_M_Storage.java | 22 +- .../org/compiere/process/InOutGenerate.java | 49 ++- .../compiere/process/M_Production_Run.java | 1 + .../org/compiere/process/StorageCleanup.java | 136 ++++++- base/src/org/eevolution/model/MDDOrder.java | 25 +- client/src/org/compiere/apps/form/Match.java | 2 +- .../functions/Is_InOut_Candidate_Order.sql | 51 +++ .../Is_InOut_Candidate_OrderLine.sql | 128 +++++++ .../functions/get_Allocated_On_Order.sql | 24 ++ .../oracle/functions/get_Delivery_Policy.sql | 27 ++ db/ddlutils/oracle/functions/isShippable.sql | 32 ++ .../oracle/views/M_INOUT_CANDIDATE_V.sql | 61 ++- .../oracle/views/M_PRODUCT_STOCK_V.sql | 30 +- .../functions/Is_InOut_Candidate_Order.sql | 49 +++ .../Is_InOut_Candidate_OrderLine.sql | 127 ++++++ .../functions/get_Allocated_On_Order.sql | 26 ++ .../functions/get_Delivery_Policy.sql | 29 ++ .../postgresql/functions/isShippable.sql | 33 ++ .../postgresql/views/M_INOUT_CANDIDATE_V.sql | 61 ++- .../postgresql/views/M_PRODUCT_STOCK_V.sql | 31 +- .../742_FR3004020_DeliveryPolicyOracle.sql | 353 +++++++++++++++++ .../743_FR3004020_DeliveryPolicyOracle.sql | 296 ++++++++++++++ ...742_FR3004020_DeliveryPolicyPostgresql.sql | 360 ++++++++++++++++++ ...743_FR3004020_DeliveryPolicyPostgresql.sql | 307 +++++++++++++++ 42 files changed, 2913 insertions(+), 189 deletions(-) create mode 100644 base/src/org/adempiere/process/AllocateSalesOrders.java create mode 100644 base/src/org/adempiere/validator/MaterialReceiptModelValidator.java create mode 100644 db/ddlutils/oracle/functions/Is_InOut_Candidate_Order.sql create mode 100644 db/ddlutils/oracle/functions/Is_InOut_Candidate_OrderLine.sql create mode 100644 db/ddlutils/oracle/functions/get_Allocated_On_Order.sql create mode 100644 db/ddlutils/oracle/functions/get_Delivery_Policy.sql create mode 100644 db/ddlutils/oracle/functions/isShippable.sql create mode 100644 db/ddlutils/postgresql/functions/Is_InOut_Candidate_Order.sql create mode 100644 db/ddlutils/postgresql/functions/Is_InOut_Candidate_OrderLine.sql create mode 100644 db/ddlutils/postgresql/functions/get_Allocated_On_Order.sql create mode 100644 db/ddlutils/postgresql/functions/get_Delivery_Policy.sql create mode 100644 db/ddlutils/postgresql/functions/isShippable.sql create mode 100644 migration/360lts-release/oracle/742_FR3004020_DeliveryPolicyOracle.sql create mode 100644 migration/360lts-release/oracle/743_FR3004020_DeliveryPolicyOracle.sql create mode 100644 migration/360lts-release/postgresql/742_FR3004020_DeliveryPolicyPostgresql.sql create mode 100644 migration/360lts-release/postgresql/743_FR3004020_DeliveryPolicyPostgresql.sql 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;