From d4927621f11883e25a6dc91914ffed8e91bd4760 Mon Sep 17 00:00:00 2001 From: Heng Sin Low Date: Fri, 16 May 2014 13:31:50 +0800 Subject: [PATCH] IDEMPIERE-1953 Performance problem of BOM* functions ( Part of Ticket 1004000 ). --- .../oracle/functions/BOM_PriceLimit.sql | 4 +- .../oracle/functions/BOM_PriceList.sql | 4 +- db/ddlutils/oracle/functions/BOM_PriceStd.sql | 4 +- .../oracle/functions/BOM_Qty_OnHand.sql | 4 +- .../oracle/functions/BOM_Qty_Ordered.sql | 4 +- .../oracle/functions/BOM_Qty_Reserved.sql | 4 +- .../postgresql/functions/BOM_PriceLimit.sql | 4 +- .../postgresql/functions/BOM_PriceList.sql | 4 +- .../postgresql/functions/BOM_PriceStd.sql | 4 +- .../functions/BOM_Qty_Available.sql | 2 +- .../postgresql/functions/BOM_Qty_OnHand.sql | 7 +- .../postgresql/functions/BOM_Qty_Ordered.sql | 4 +- .../postgresql/functions/BOM_Qty_Reserved.sql | 4 +- .../oracle/201405160521_IDEMPIERE-1953.sql | 556 ++++++++++++++++++ .../201405160521_IDEMPIERE-1953.sql | 478 +++++++++++++++ 15 files changed, 1071 insertions(+), 16 deletions(-) create mode 100644 migration/i2.0/oracle/201405160521_IDEMPIERE-1953.sql create mode 100644 migration/i2.0/postgresql/201405160521_IDEMPIERE-1953.sql diff --git a/db/ddlutils/oracle/functions/BOM_PriceLimit.sql b/db/ddlutils/oracle/functions/BOM_PriceLimit.sql index fa1996fd77..37733c93fe 100644 --- a/db/ddlutils/oracle/functions/BOM_PriceLimit.sql +++ b/db/ddlutils/oracle/functions/BOM_PriceLimit.sql @@ -26,8 +26,10 @@ AS FROM M_PRODUCT_BOM b, M_PRODUCT p WHERE b.M_ProductBOM_ID=p.M_Product_ID AND b.M_Product_ID=Product_ID + AND b.M_ProductBOM_ID != Product_ID AND p.IsBOM='Y' - AND p.IsVerified='Y'; + AND p.IsVerified='Y' + AND b.IsActive='Y'; -- BEGIN -- Try to get price from PriceList directly diff --git a/db/ddlutils/oracle/functions/BOM_PriceList.sql b/db/ddlutils/oracle/functions/BOM_PriceList.sql index 0be86221b8..da274c35d8 100644 --- a/db/ddlutils/oracle/functions/BOM_PriceList.sql +++ b/db/ddlutils/oracle/functions/BOM_PriceList.sql @@ -26,8 +26,10 @@ AS FROM M_PRODUCT_BOM b, M_PRODUCT p WHERE b.M_ProductBOM_ID=p.M_Product_ID AND b.M_Product_ID=Product_ID + AND b.M_ProductBOM_ID != Product_ID AND p.IsBOM='Y' - AND p.IsVerified='Y'; + AND p.IsVerified='Y' + AND b.IsActive='Y'; -- BEGIN -- Try to get price from pricelist directly diff --git a/db/ddlutils/oracle/functions/BOM_PriceStd.sql b/db/ddlutils/oracle/functions/BOM_PriceStd.sql index e946334252..eace5db30a 100644 --- a/db/ddlutils/oracle/functions/BOM_PriceStd.sql +++ b/db/ddlutils/oracle/functions/BOM_PriceStd.sql @@ -26,8 +26,10 @@ AS FROM M_PRODUCT_BOM b, M_PRODUCT p WHERE b.M_ProductBOM_ID=p.M_Product_ID AND b.M_Product_ID=Product_ID + AND b.M_ProductBOM_ID != Product_ID AND p.IsBOM='Y' - AND p.IsVerified='Y'; + AND p.IsVerified='Y' + AND b.IsActive='Y'; -- BEGIN -- Try to get price from pricelist directly diff --git a/db/ddlutils/oracle/functions/BOM_Qty_OnHand.sql b/db/ddlutils/oracle/functions/BOM_Qty_OnHand.sql index aee834d042..8234cad33d 100644 --- a/db/ddlutils/oracle/functions/BOM_Qty_OnHand.sql +++ b/db/ddlutils/oracle/functions/BOM_Qty_OnHand.sql @@ -27,8 +27,10 @@ AS FROM M_PRODUCT_BOM b, M_PRODUCT p WHERE b.M_ProductBOM_ID=p.M_Product_ID AND b.M_Product_ID=Product_ID + AND b.M_ProductBOM_ID != Product_ID AND p.IsBOM='Y' - AND p.IsVerified='Y'; + AND p.IsVerified='Y' + AND b.IsActive='Y'; -- BEGIN -- Check Parameters diff --git a/db/ddlutils/oracle/functions/BOM_Qty_Ordered.sql b/db/ddlutils/oracle/functions/BOM_Qty_Ordered.sql index c0eb2b93ea..3fb7ec0651 100644 --- a/db/ddlutils/oracle/functions/BOM_Qty_Ordered.sql +++ b/db/ddlutils/oracle/functions/BOM_Qty_Ordered.sql @@ -27,8 +27,10 @@ AS FROM M_PRODUCT_BOM b, M_PRODUCT p WHERE b.M_ProductBOM_ID=p.M_Product_ID AND b.M_Product_ID=p_Product_ID + AND b.M_ProductBOM_ID != p_Product_ID AND p.IsBOM='Y' - AND p.IsVerified='Y'; + AND p.IsVerified='Y' + AND b.IsActive='Y'; -- BEGIN -- Check Parameters diff --git a/db/ddlutils/oracle/functions/BOM_Qty_Reserved.sql b/db/ddlutils/oracle/functions/BOM_Qty_Reserved.sql index 3cb43168ac..1d07949d8d 100644 --- a/db/ddlutils/oracle/functions/BOM_Qty_Reserved.sql +++ b/db/ddlutils/oracle/functions/BOM_Qty_Reserved.sql @@ -27,8 +27,10 @@ AS FROM M_PRODUCT_BOM b, M_PRODUCT p WHERE b.M_ProductBOM_ID=p.M_Product_ID AND b.M_Product_ID=p_Product_ID + AND b.M_ProductBOM_ID != p_Product_ID AND p.IsBOM='Y' - AND p.IsVerified='Y'; + AND p.IsVerified='Y' + AND b.IsActive='Y'; -- BEGIN -- Check Parameters diff --git a/db/ddlutils/postgresql/functions/BOM_PriceLimit.sql b/db/ddlutils/postgresql/functions/BOM_PriceLimit.sql index f32ba35945..f9c1b0b4b9 100644 --- a/db/ddlutils/postgresql/functions/BOM_PriceLimit.sql +++ b/db/ddlutils/postgresql/functions/BOM_PriceLimit.sql @@ -19,8 +19,10 @@ BEGIN FROM M_Product_BOM b, M_Product p WHERE b.M_ProductBOM_ID=p.M_Product_ID AND b.M_Product_ID=Product_ID + AND b.M_ProductBOM_ID != Product_ID AND p.IsBOM='Y' AND p.IsVerified='Y' + AND b.IsActive='Y' LOOP v_ProductPrice := bomPriceLimit (bom.M_ProductBOM_ID, PriceList_Version_ID); v_Price := v_Price + (bom.BOMQty * v_ProductPrice); @@ -32,6 +34,6 @@ BEGIN END; $BODY$ -LANGUAGE 'plpgsql' +LANGUAGE 'plpgsql' STABLE ; diff --git a/db/ddlutils/postgresql/functions/BOM_PriceList.sql b/db/ddlutils/postgresql/functions/BOM_PriceList.sql index 58aaef8ec6..8677da115c 100644 --- a/db/ddlutils/postgresql/functions/BOM_PriceList.sql +++ b/db/ddlutils/postgresql/functions/BOM_PriceList.sql @@ -19,8 +19,10 @@ BEGIN FROM M_Product_BOM b, M_Product p WHERE b.M_ProductBOM_ID=p.M_Product_ID AND b.M_Product_ID=Product_ID + AND b.M_ProductBOM_ID != Product_ID AND p.IsBOM='Y' AND p.IsVerified='Y' + AND b.IsActive='Y' LOOP v_ProductPrice := bomPriceList (bom.M_ProductBOM_ID, PriceList_Version_ID); v_Price := v_Price + (bom.BOMQty * v_ProductPrice); @@ -32,6 +34,6 @@ BEGIN END; $BODY$ -LANGUAGE 'plpgsql' +LANGUAGE 'plpgsql' STABLE ; diff --git a/db/ddlutils/postgresql/functions/BOM_PriceStd.sql b/db/ddlutils/postgresql/functions/BOM_PriceStd.sql index a259dccefc..96c3dabf00 100644 --- a/db/ddlutils/postgresql/functions/BOM_PriceStd.sql +++ b/db/ddlutils/postgresql/functions/BOM_PriceStd.sql @@ -19,8 +19,10 @@ BEGIN FROM M_Product_BOM b, M_Product p WHERE b.M_ProductBOM_ID=p.M_Product_ID AND b.M_Product_ID=Product_ID + AND b.M_ProductBOM_ID != Product_ID AND p.IsBOM='Y' AND p.IsVerified='Y' + AND b.IsActive='Y' LOOP v_ProductPrice := bomPriceStd (bom.M_ProductBOM_ID, PriceList_Version_ID); v_Price := v_Price + (bom.BOMQty * v_ProductPrice); @@ -32,6 +34,6 @@ BEGIN END; $BODY$ -LANGUAGE 'plpgsql' +LANGUAGE 'plpgsql' STABLE ; diff --git a/db/ddlutils/postgresql/functions/BOM_Qty_Available.sql b/db/ddlutils/postgresql/functions/BOM_Qty_Available.sql index 19662e14b2..40952ccf98 100644 --- a/db/ddlutils/postgresql/functions/BOM_Qty_Available.sql +++ b/db/ddlutils/postgresql/functions/BOM_Qty_Available.sql @@ -4,6 +4,6 @@ BEGIN RETURN bomQtyOnHand(Product_ID, Warehouse_ID, Locator_ID) - bomQtyReserved(Product_ID, Warehouse_ID, Locator_ID); END; $BODY$ -LANGUAGE 'plpgsql' +LANGUAGE 'plpgsql' STABLE ; diff --git a/db/ddlutils/postgresql/functions/BOM_Qty_OnHand.sql b/db/ddlutils/postgresql/functions/BOM_Qty_OnHand.sql index 9518f30ebe..adeff6ec6f 100644 --- a/db/ddlutils/postgresql/functions/BOM_Qty_OnHand.sql +++ b/db/ddlutils/postgresql/functions/BOM_Qty_OnHand.sql @@ -25,7 +25,6 @@ BEGIN IF (myWarehouse_ID IS NULL) THEN RETURN 0; END IF; --- DBMS_OUTPUT.PUT_LINE(''Warehouse='' || myWarehouse_ID); -- Check, if product exists and if it is stocked BEGIN @@ -51,19 +50,19 @@ BEGIN AND EXISTS (SELECT * FROM M_LOCATOR l WHERE s.M_Locator_ID=l.M_Locator_ID AND l.M_Warehouse_ID=myWarehouse_ID); -- - -- DBMS_OUTPUT.PUT_LINE(''Qty='' || v_ProductQty); RETURN v_ProductQty; END IF; -- Go though BOM --- DBMS_OUTPUT.PUT_LINE(''BOM''); FOR bom IN -- Get BOM Product info SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM, p.IsStocked, p.ProductType FROM M_PRODUCT_BOM b, M_PRODUCT p WHERE b.M_ProductBOM_ID=p.M_Product_ID AND b.M_Product_ID=product_ID + AND b.M_ProductBOM_ID != Product_ID AND p.IsBOM='Y' AND p.IsVerified='Y' + AND b.IsActive='Y' LOOP -- Stocked Items "leaf node" IF (bom.ProductType = 'I' AND bom.IsStocked = 'Y') THEN @@ -107,6 +106,6 @@ BEGIN RETURN 0; END; $BODY$ -LANGUAGE 'plpgsql' +LANGUAGE 'plpgsql' STABLE ; diff --git a/db/ddlutils/postgresql/functions/BOM_Qty_Ordered.sql b/db/ddlutils/postgresql/functions/BOM_Qty_Ordered.sql index b82e7dc67b..79c0fda12e 100644 --- a/db/ddlutils/postgresql/functions/BOM_Qty_Ordered.sql +++ b/db/ddlutils/postgresql/functions/BOM_Qty_Ordered.sql @@ -61,8 +61,10 @@ BEGIN FROM M_PRODUCT_BOM b, M_PRODUCT p WHERE b.M_ProductBOM_ID=p.M_Product_ID AND b.M_Product_ID=p_Product_ID + AND b.M_ProductBOM_ID != p_Product_ID AND p.IsBOM='Y' AND p.IsVerified='Y' + AND b.IsActive='Y' LOOP -- Stocked Items "leaf node" IF (bom.ProductType = 'I' AND bom.IsStocked = 'Y') THEN @@ -114,5 +116,5 @@ BEGIN RETURN 0; END; $BODY$ - LANGUAGE plpgsql VOLATILE; + LANGUAGE plpgsql STABLE; diff --git a/db/ddlutils/postgresql/functions/BOM_Qty_Reserved.sql b/db/ddlutils/postgresql/functions/BOM_Qty_Reserved.sql index 22775b364b..2f2b4f428a 100644 --- a/db/ddlutils/postgresql/functions/BOM_Qty_Reserved.sql +++ b/db/ddlutils/postgresql/functions/BOM_Qty_Reserved.sql @@ -61,8 +61,10 @@ BEGIN FROM M_PRODUCT_BOM b, M_PRODUCT p WHERE b.M_ProductBOM_ID=p.M_Product_ID AND b.M_Product_ID=p_Product_ID + AND b.M_ProductBOM_ID != p_Product_ID AND p.IsBOM='Y' AND p.IsVerified='Y' + AND b.IsActive='Y' LOOP -- Stocked Items "leaf node" IF (bom.ProductType = 'I' AND bom.IsStocked = 'Y') THEN @@ -112,6 +114,6 @@ BEGIN RETURN 0; END; $BODY$ - LANGUAGE plpgsql VOLATILE; + LANGUAGE plpgsql STABLE; diff --git a/migration/i2.0/oracle/201405160521_IDEMPIERE-1953.sql b/migration/i2.0/oracle/201405160521_IDEMPIERE-1953.sql new file mode 100644 index 0000000000..1611acbf7a --- /dev/null +++ b/migration/i2.0/oracle/201405160521_IDEMPIERE-1953.sql @@ -0,0 +1,556 @@ +SET SQLBLANKLINES ON +SET DEFINE OFF + +CREATE OR REPLACE FUNCTION BOMPRICELIMIT +( + Product_ID IN NUMBER, + PriceList_Version_ID IN NUMBER +) +RETURN NUMBER +/************************************************************************* + * The contents of this file are subject to the Compiere License. You may + * obtain a copy of the License at http://www.compiere.org/license.html + * Software is on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See the License for details. Code: Compiere ERP+CRM + * Copyright (C) 1999-2002 Jorg Janke, ComPiere, Inc. All Rights Reserved. + ************************************************************************* + * $Id: BOM_PriceLimit.sql,v 1.1 2006/04/21 17:51:58 jjanke Exp $ + *** + * Title: Return Limit Price of Product/BOM + * Description: + * if not found: 0 + ************************************************************************/ +AS + v_Price NUMBER; + v_ProductPrice NUMBER; + -- Get BOM Product info + CURSOR CUR_BOM IS + SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM + FROM M_PRODUCT_BOM b, M_PRODUCT p + WHERE b.M_ProductBOM_ID=p.M_Product_ID + AND b.M_Product_ID=Product_ID + AND b.M_ProductBOM_ID != Product_ID + AND p.IsBOM='Y' + AND p.IsVerified='Y' + AND b.IsActive='Y'; + -- +BEGIN + -- Try to get price from PriceList directly + SELECT COALESCE (SUM(PriceLimit), 0) + INTO v_Price + FROM M_PRODUCTPRICE + WHERE M_PriceList_Version_ID=PriceList_Version_ID AND M_Product_ID=Product_ID; +-- DBMS_OUTPUT.PUT_LINE('Price=' || v_Price); + + -- No Price - Check if BOM + IF (v_Price = 0) THEN + FOR bom IN CUR_BOM LOOP + v_ProductPrice := Bompricelimit (bom.M_ProductBOM_ID, PriceList_Version_ID); + v_Price := v_Price + (bom.BOMQty * v_ProductPrice); + END LOOP; + END IF; + -- + RETURN v_Price; +END Bompricelimit; +/ + +CREATE OR REPLACE FUNCTION BOMPRICELIST +( + Product_ID IN NUMBER, + PriceList_Version_ID IN NUMBER +) +RETURN NUMBER +/************************************************************************* + * The contents of this file are subject to the Compiere License. You may + * obtain a copy of the License at http://www.compiere.org/license.html + * Software is on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See the License for details. Code: Compiere ERP+CRM + * Copyright (C) 1999-2002 Jorg Janke, ComPiere, Inc. All Rights Reserved. + ************************************************************************* + * $Id: BOM_PriceList.sql,v 1.1 2006/04/21 17:51:58 jjanke Exp $ + *** + * Title: Return List Price of Product/BOM + * Description: + * if not found: 0 + ************************************************************************/ +AS + v_Price NUMBER; + v_ProductPrice NUMBER; + -- Get BOM Product info + CURSOR CUR_BOM IS + SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM + FROM M_PRODUCT_BOM b, M_PRODUCT p + WHERE b.M_ProductBOM_ID=p.M_Product_ID + AND b.M_Product_ID=Product_ID + AND b.M_ProductBOM_ID != Product_ID + AND p.IsBOM='Y' + AND p.IsVerified='Y' + AND b.IsActive='Y'; + -- +BEGIN + -- Try to get price from pricelist directly + SELECT COALESCE (SUM(PriceList), 0) + INTO v_Price + FROM M_PRODUCTPRICE + WHERE M_PriceList_Version_ID=PriceList_Version_ID AND M_Product_ID=Product_ID; +-- DBMS_OUTPUT.PUT_LINE('Price=' || Price); + + -- No Price - Check if BOM + IF (v_Price = 0) THEN + FOR bom IN CUR_BOM LOOP + v_ProductPrice := Bompricelist (bom.M_ProductBOM_ID, PriceList_Version_ID); + v_Price := v_Price + (bom.BOMQty * v_ProductPrice); + -- DBMS_OUTPUT.PUT_LINE('Qry=' || bom.BOMQty || ' @ ' || v_ProductPrice || ', Price=' || v_Price); + END LOOP; -- BOM + END IF; + -- + RETURN v_Price; +END Bompricelist; +/ + +CREATE OR REPLACE FUNCTION BOMPRICESTD +( + Product_ID IN NUMBER, + PriceList_Version_ID IN NUMBER +) +RETURN NUMBER +/************************************************************************* + * The contents of this file are subject to the Compiere License. You may + * obtain a copy of the License at http://www.compiere.org/license.html + * Software is on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See the License for details. Code: Compiere ERP+CRM + * Copyright (C) 1999-2002 Jorg Janke, ComPiere, Inc. All Rights Reserved. + ************************************************************************* + * $Id: BOM_PriceStd.sql,v 1.1 2006/04/21 17:51:58 jjanke Exp $ + *** + * Title: Return Standard Price of Product/BOM + * Description: + * if not found: 0 + ************************************************************************/ +AS + v_Price NUMBER; + v_ProductPrice NUMBER; + -- Get BOM Product info + CURSOR CUR_BOM IS + SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM + FROM M_PRODUCT_BOM b, M_PRODUCT p + WHERE b.M_ProductBOM_ID=p.M_Product_ID + AND b.M_Product_ID=Product_ID + AND b.M_ProductBOM_ID != Product_ID + AND p.IsBOM='Y' + AND p.IsVerified='Y' + AND b.IsActive='Y'; + -- +BEGIN + -- Try to get price from pricelist directly + SELECT COALESCE(SUM(PriceStd), 0) + INTO v_Price + FROM M_PRODUCTPRICE + WHERE M_PriceList_Version_ID=PriceList_Version_ID AND M_Product_ID=Product_ID; +-- DBMS_OUTPUT.PUT_LINE('Price=' || v_Price); + + -- No Price - Check if BOM + IF (v_Price = 0) THEN + FOR bom IN CUR_BOM LOOP + v_ProductPrice := Bompricestd (bom.M_ProductBOM_ID, PriceList_Version_ID); + v_Price := v_Price + (bom.BOMQty * v_ProductPrice); + -- DBMS_OUTPUT.PUT_LINE('Price=' || v_Price); + END LOOP; -- BOM + END IF; + -- + RETURN v_Price; +END Bompricestd; +/ + +CREATE OR REPLACE FUNCTION BOMQTYONHAND +( + Product_ID IN NUMBER, + Warehouse_ID IN NUMBER, + Locator_ID IN NUMBER -- Only used, if warehouse is null +) +RETURN NUMBER +/****************************************************************************** + * ** Compiere Product ** Copyright (c) 1999-2001 Accorto, Inc. USA + * Open Source Software Provided "AS IS" without warranty or liability + * When you use any parts (changed or unchanged), add "Powered by Compiere" to + * your product name; See license details http://www.compiere.org/license.html + ****************************************************************************** + * Return quantity on hand for BOM + */ +AS + myWarehouse_ID NUMBER; + Quantity NUMBER := 99999; -- unlimited + IsBOM CHAR(1); + IsStocked CHAR(1); + ProductType CHAR(1); + ProductQty NUMBER; + StdPrecision NUMBER; + -- Get BOM Product info + CURSOR CUR_BOM IS + SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM, p.IsStocked, p.ProductType + FROM M_PRODUCT_BOM b, M_PRODUCT p + WHERE b.M_ProductBOM_ID=p.M_Product_ID + AND b.M_Product_ID=Product_ID + AND b.M_ProductBOM_ID != Product_ID + AND p.IsBOM='Y' + AND p.IsVerified='Y' + AND b.IsActive='Y'; + -- +BEGIN + -- Check Parameters + myWarehouse_ID := Warehouse_ID; + IF (myWarehouse_ID IS NULL) THEN + IF (Locator_ID IS NULL) THEN + RETURN 0; + ELSE + SELECT SUM(M_Warehouse_ID) INTO myWarehouse_ID + FROM M_LOCATOR + WHERE M_Locator_ID=Locator_ID; + END IF; + END IF; + IF (myWarehouse_ID IS NULL) THEN + RETURN 0; + END IF; +-- DBMS_OUTPUT.PUT_LINE('Warehouse=' || myWarehouse_ID); + + -- Check, if product exists and if it is stocked + BEGIN + SELECT IsBOM, ProductType, IsStocked + INTO IsBOM, ProductType, IsStocked + FROM M_PRODUCT + WHERE M_Product_ID=Product_ID; + -- + EXCEPTION -- not found + WHEN OTHERS THEN + RETURN 0; + END; + -- Unimited capacity if no item + IF (IsBOM='N' AND (ProductType<>'I' OR IsStocked='N')) THEN + RETURN Quantity; + -- Stocked item + ELSIF (IsStocked='Y') THEN + -- Get ProductQty + SELECT NVL(SUM(QtyOnHand), 0) + INTO ProductQty + FROM M_STORAGE s + WHERE M_Product_ID=Product_ID + AND EXISTS (SELECT * FROM M_LOCATOR l WHERE s.M_Locator_ID=l.M_Locator_ID + AND l.M_Warehouse_ID=myWarehouse_ID); + -- + -- DBMS_OUTPUT.PUT_LINE('Qty=' || ProductQty); + RETURN ProductQty; + END IF; + + -- Go though BOM +-- DBMS_OUTPUT.PUT_LINE('BOM'); + FOR bom IN CUR_BOM LOOP + -- Stocked Items "leaf node" + IF (bom.ProductType = 'I' AND bom.IsStocked = 'Y') THEN + -- Get ProductQty + SELECT NVL(SUM(QtyOnHand), 0) + INTO ProductQty + FROM M_STORAGE s + WHERE M_Product_ID=bom.M_ProductBOM_ID + AND EXISTS (SELECT * FROM M_LOCATOR l WHERE s.M_Locator_ID=l.M_Locator_ID + AND l.M_Warehouse_ID=myWarehouse_ID); + -- Get Rounding Precision + SELECT NVL(MAX(u.StdPrecision), 0) + INTO StdPrecision + FROM C_UOM u, M_PRODUCT p + WHERE u.C_UOM_ID=p.C_UOM_ID AND p.M_Product_ID=bom.M_ProductBOM_ID; + -- How much can we make with this product + ProductQty := ROUND (ProductQty/bom.BOMQty, StdPrecision); + -- How much can we make overall + IF (ProductQty < Quantity) THEN + Quantity := ProductQty; + END IF; + -- Another BOM + ELSIF (bom.IsBOM = 'Y') THEN + ProductQty := Bomqtyonhand (bom.M_ProductBOM_ID, myWarehouse_ID, Locator_ID); + -- How much can we make overall + IF (ProductQty < Quantity) THEN + Quantity := ProductQty; + END IF; + END IF; + END LOOP; -- BOM + + IF (Quantity > 0) THEN + -- Get Rounding Precision for Product + SELECT NVL(MAX(u.StdPrecision), 0) + INTO StdPrecision + FROM C_UOM u, M_PRODUCT p + WHERE u.C_UOM_ID=p.C_UOM_ID AND p.M_Product_ID=Product_ID; + -- + RETURN ROUND (Quantity, StdPrecision); + END IF; + RETURN 0; +END Bomqtyonhand; +/ + +CREATE OR REPLACE FUNCTION BOMQTYORDERED +( + p_Product_ID IN NUMBER, + p_Warehouse_ID IN NUMBER, + p_Locator_ID IN NUMBER -- Only used, if warehouse is null +) +RETURN NUMBER +/****************************************************************************** + * ** Compiere Product ** Copyright (c) 1999-2001 Accorto, Inc. USA + * Open Source Software Provided "AS IS" without warranty or liability + * When you use any parts (changed or unchanged), add "Powered by Compiere" to + * your product name; See license details http://www.compiere.org/license.html + ****************************************************************************** + * Return quantity ordered for BOM + */ +AS + v_Warehouse_ID NUMBER; + v_Quantity NUMBER := 99999; -- unlimited + v_IsBOM CHAR(1); + v_IsStocked CHAR(1); + v_ProductType CHAR(1); + v_ProductQty NUMBER; + v_StdPrecision NUMBER; + -- Get BOM Product info + CURSOR CUR_BOM IS + SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM, p.IsStocked, p.ProductType + FROM M_PRODUCT_BOM b, M_PRODUCT p + WHERE b.M_ProductBOM_ID=p.M_Product_ID + AND b.M_Product_ID=p_Product_ID + AND b.M_ProductBOM_ID != p_Product_ID + AND p.IsBOM='Y' + AND p.IsVerified='Y' + AND b.IsActive='Y'; + -- +BEGIN + -- Check Parameters + v_Warehouse_ID := p_Warehouse_ID; + IF (v_Warehouse_ID IS NULL) THEN + IF (p_Locator_ID IS NULL) THEN + RETURN 0; + ELSE + SELECT MAX(M_Warehouse_ID) INTO v_Warehouse_ID + FROM M_LOCATOR + WHERE M_Locator_ID=p_Locator_ID; + END IF; + END IF; + IF (v_Warehouse_ID IS NULL) THEN + RETURN 0; + END IF; +-- DBMS_OUTPUT.PUT_LINE('Warehouse=' || v_Warehouse_ID); + + -- Check, if product exists and if it is stocked + BEGIN + SELECT IsBOM, ProductType, IsStocked + INTO v_IsBOM, v_ProductType, v_IsStocked + FROM M_PRODUCT + WHERE M_Product_ID=p_Product_ID; + -- + EXCEPTION -- not found + WHEN OTHERS THEN + RETURN 0; + END; + + -- No reservation for non-stocked + IF (v_IsBOM='N' AND (v_ProductType<>'I' OR v_IsStocked='N')) THEN + RETURN 0; + -- Stocked item + ELSIF (v_IsStocked='Y') THEN + -- Get ProductQty + SELECT NVL(SUM(Qty), 0) + INTO v_ProductQty + FROM M_StorageReservation + WHERE M_Product_ID=p_Product_ID + AND M_Warehouse_ID=v_Warehouse_ID + AND IsSOTrx='N' + AND IsActive='Y'; + -- + RETURN v_ProductQty; + END IF; + + -- Go though BOM +-- DBMS_OUTPUT.PUT_LINE('BOM'); + FOR bom IN CUR_BOM LOOP + -- Stocked Items "leaf node" + IF (bom.ProductType = 'I' AND bom.IsStocked = 'Y') THEN + -- Get ProductQty + SELECT NVL(SUM(Qty), 0) + INTO v_ProductQty + FROM M_StorageReservation + WHERE M_Product_ID=p_Product_ID + AND M_Warehouse_ID=v_Warehouse_ID + AND IsSOTrx='N' + AND IsActive='Y'; + -- Get Rounding Precision + SELECT NVL(MAX(u.StdPrecision), 0) + INTO v_StdPrecision + FROM C_UOM u, M_PRODUCT p + WHERE u.C_UOM_ID=p.C_UOM_ID AND p.M_Product_ID=bom.M_ProductBOM_ID; + -- How much can we make with this product + v_ProductQty := ROUND (v_ProductQty/bom.BOMQty, v_StdPrecision); + -- How much can we make overall + IF (v_ProductQty < v_Quantity) THEN + v_Quantity := v_ProductQty; + END IF; + -- Another BOM + ELSIF (bom.IsBOM = 'Y') THEN + v_ProductQty := Bomqtyordered (bom.M_ProductBOM_ID, v_Warehouse_ID, p_Locator_ID); + -- How much can we make overall + IF (v_ProductQty < v_Quantity) THEN + v_Quantity := v_ProductQty; + END IF; + END IF; + END LOOP; -- BOM + + -- Unlimited (e.g. only services) + IF (v_Quantity = 99999) THEN + RETURN 0; + END IF; + + IF (v_Quantity > 0) THEN + -- Get Rounding Precision for Product + SELECT NVL(MAX(u.StdPrecision), 0) + INTO v_StdPrecision + FROM C_UOM u, M_PRODUCT p + WHERE u.C_UOM_ID=p.C_UOM_ID AND p.M_Product_ID=p_Product_ID; + -- + RETURN ROUND (v_Quantity, v_StdPrecision); + END IF; + -- + RETURN 0; +END Bomqtyordered; +/ + +CREATE OR REPLACE FUNCTION BOMQTYRESERVED +( + p_Product_ID IN NUMBER, + p_Warehouse_ID IN NUMBER, + p_Locator_ID IN NUMBER -- Only used, if warehouse is null +) +RETURN NUMBER +/****************************************************************************** + * ** Compiere Product ** Copyright (c) 1999-2001 Accorto, Inc. USA + * Open Source Software Provided "AS IS" without warranty or liability + * When you use any parts (changed or unchanged), add "Powered by Compiere" to + * your product name; See license details http://www.compiere.org/license.html + ****************************************************************************** + * Return quantity reserved for BOM + */ +AS + v_Warehouse_ID NUMBER; + v_Quantity NUMBER := 99999; -- unlimited + v_IsBOM CHAR(1); + v_IsStocked CHAR(1); + v_ProductType CHAR(1); + v_ProductQty NUMBER; + v_StdPrecision NUMBER; + -- Get BOM Product info + CURSOR CUR_BOM IS + SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM, p.IsStocked, p.ProductType + FROM M_PRODUCT_BOM b, M_PRODUCT p + WHERE b.M_ProductBOM_ID=p.M_Product_ID + AND b.M_Product_ID=p_Product_ID + AND b.M_ProductBOM_ID != p_Product_ID + AND p.IsBOM='Y' + AND p.IsVerified='Y' + AND b.IsActive='Y'; + -- +BEGIN + -- Check Parameters + v_Warehouse_ID := p_Warehouse_ID; + IF (v_Warehouse_ID IS NULL) THEN + IF (p_Locator_ID IS NULL) THEN + RETURN 0; + ELSE + SELECT MAX(M_Warehouse_ID) INTO v_Warehouse_ID + FROM M_LOCATOR + WHERE M_Locator_ID=p_Locator_ID; + END IF; + END IF; + IF (v_Warehouse_ID IS NULL) THEN + RETURN 0; + END IF; +-- DBMS_OUTPUT.PUT_LINE('Warehouse=' || v_Warehouse_ID); + + -- Check, if product exists and if it is stocked + BEGIN + SELECT IsBOM, ProductType, IsStocked + INTO v_IsBOM, v_ProductType, v_IsStocked + FROM M_PRODUCT + WHERE M_Product_ID=p_Product_ID; + -- + EXCEPTION -- not found + WHEN OTHERS THEN + RETURN 0; + END; + + -- No reservation for non-stocked + IF (v_IsBOM='N' AND (v_ProductType<>'I' OR v_IsStocked='N')) THEN + RETURN 0; + -- Stocked item + ELSIF (v_IsStocked='Y') THEN + -- Get ProductQty + SELECT NVL(SUM(Qty), 0) + INTO v_ProductQty + FROM M_StorageReservation + WHERE M_Product_ID=p_Product_ID + AND M_Warehouse_ID=v_Warehouse_ID + AND IsSOTrx='Y' + AND IsActive='Y'; + -- + RETURN v_ProductQty; + END IF; + + -- Go though BOM +-- DBMS_OUTPUT.PUT_LINE('BOM'); + FOR bom IN CUR_BOM LOOP + -- Stocked Items "leaf node" + IF (bom.ProductType = 'I' AND bom.IsStocked = 'Y') THEN + -- Get ProductQty + SELECT NVL(SUM(Qty), 0) + INTO v_ProductQty + FROM M_StorageReservation + WHERE M_Product_ID=bom.M_ProductBOM_ID + AND M_Warehouse_ID=v_Warehouse_ID + AND IsSOTrx='Y' + AND IsActive='Y'; + -- Get Rounding Precision + SELECT NVL(MAX(u.StdPrecision), 0) + INTO v_StdPrecision + FROM C_UOM u, M_PRODUCT p + WHERE u.C_UOM_ID=p.C_UOM_ID AND p.M_Product_ID=bom.M_ProductBOM_ID; + -- How much can we make with this product + v_ProductQty := ROUND (v_ProductQty/bom.BOMQty, v_StdPrecision); + -- How much can we make overall + IF (v_ProductQty < v_Quantity) THEN + v_Quantity := v_ProductQty; + END IF; + -- Another BOM + ELSIF (bom.IsBOM = 'Y') THEN + v_ProductQty := Bomqtyreserved (bom.M_ProductBOM_ID, v_Warehouse_ID, p_Locator_ID); + -- How much can we make overall + IF (v_ProductQty < v_Quantity) THEN + v_Quantity := v_ProductQty; + END IF; + END IF; + END LOOP; -- BOM + + -- Unlimited (e.g. only services) + IF (v_Quantity = 99999) THEN + RETURN 0; + END IF; + + IF (v_Quantity > 0) THEN + -- Get Rounding Precision for Product + SELECT NVL(MAX(u.StdPrecision), 0) + INTO v_StdPrecision + FROM C_UOM u, M_PRODUCT p + WHERE u.C_UOM_ID=p.C_UOM_ID AND p.M_Product_ID=p_Product_ID; + -- + RETURN ROUND (v_Quantity, v_StdPrecision); + END IF; + RETURN 0; +END Bomqtyreserved; +/ + +SELECT register_migration_script('201405160521_IDEMPIERE-1953.sql') FROM dual +; diff --git a/migration/i2.0/postgresql/201405160521_IDEMPIERE-1953.sql b/migration/i2.0/postgresql/201405160521_IDEMPIERE-1953.sql new file mode 100644 index 0000000000..3fbf47afdd --- /dev/null +++ b/migration/i2.0/postgresql/201405160521_IDEMPIERE-1953.sql @@ -0,0 +1,478 @@ +CREATE OR REPLACE FUNCTION bompricelimit (in product_id numeric, in pricelist_version_id numeric) RETURNS numeric AS +$BODY$ +DECLARE + v_Price NUMERIC; + v_ProductPrice NUMERIC; + bom RECORD; + +BEGIN + -- Try to get price from PriceList directly + SELECT COALESCE (SUM(PriceLimit), 0) + INTO v_Price + FROM M_ProductPrice + WHERE M_PriceList_Version_ID=PriceList_Version_ID AND M_Product_ID=Product_ID; + + -- No Price - Check if BOM + IF (v_Price = 0) THEN + FOR bom IN + SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM + FROM M_Product_BOM b, M_Product p + WHERE b.M_ProductBOM_ID=p.M_Product_ID + AND b.M_Product_ID=Product_ID + AND b.M_ProductBOM_ID != Product_ID + AND p.IsBOM='Y' + AND p.IsVerified='Y' + AND b.IsActive='Y' + LOOP + v_ProductPrice := bomPriceLimit (bom.M_ProductBOM_ID, PriceList_Version_ID); + v_Price := v_Price + (bom.BOMQty * v_ProductPrice); + END LOOP; + END IF; + -- + RETURN v_Price; + +END; + +$BODY$ +LANGUAGE 'plpgsql' STABLE +; + +CREATE OR REPLACE FUNCTION bompricelist (in product_id numeric, in pricelist_version_id numeric) RETURNS numeric AS +$BODY$ +DECLARE + v_Price NUMERIC; + v_ProductPrice NUMERIC; + bom RECORD; + +BEGIN + -- Try to get price from pricelist directly + SELECT COALESCE (SUM(PriceList), 0) + INTO v_Price + FROM M_ProductPrice + WHERE M_PriceList_Version_ID=PriceList_Version_ID AND M_Product_ID=Product_ID; + + -- No Price - Check if BOM + IF (v_Price = 0) THEN + FOR bom IN + SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM + FROM M_Product_BOM b, M_Product p + WHERE b.M_ProductBOM_ID=p.M_Product_ID + AND b.M_Product_ID=Product_ID + AND b.M_ProductBOM_ID != Product_ID + AND p.IsBOM='Y' + AND p.IsVerified='Y' + AND b.IsActive='Y' + LOOP + v_ProductPrice := bomPriceList (bom.M_ProductBOM_ID, PriceList_Version_ID); + v_Price := v_Price + (bom.BOMQty * v_ProductPrice); + END LOOP; + END IF; + -- + RETURN v_Price; + +END; + +$BODY$ +LANGUAGE 'plpgsql' STABLE +; + +CREATE OR REPLACE FUNCTION bompricestd (in product_id numeric, in pricelist_version_id numeric) RETURNS numeric AS +$BODY$ +DECLARE + v_Price NUMERIC; + v_ProductPrice NUMERIC; + bom RECORD; + +BEGIN + -- Try to get price from PriceList directly + SELECT COALESCE(SUM(PriceStd), 0) + INTO v_Price + FROM M_ProductPrice + WHERE M_PriceList_Version_ID=PriceList_Version_ID AND M_Product_ID=Product_ID; + + -- No Price - Check if BOM + IF (v_Price = 0) THEN + FOR bom IN + SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM + FROM M_Product_BOM b, M_Product p + WHERE b.M_ProductBOM_ID=p.M_Product_ID + AND b.M_Product_ID=Product_ID + AND b.M_ProductBOM_ID != Product_ID + AND p.IsBOM='Y' + AND p.IsVerified='Y' + AND b.IsActive='Y' + LOOP + v_ProductPrice := bomPriceStd (bom.M_ProductBOM_ID, PriceList_Version_ID); + v_Price := v_Price + (bom.BOMQty * v_ProductPrice); + END LOOP; + END IF; + -- + RETURN v_Price; + +END; + +$BODY$ +LANGUAGE 'plpgsql' STABLE +; + +CREATE OR REPLACE FUNCTION bomqtyavailable (in product_id numeric, in warehouse_id numeric, in locator_id numeric) RETURNS numeric AS +$BODY$ +BEGIN + RETURN bomQtyOnHand(Product_ID, Warehouse_ID, Locator_ID) - bomQtyReserved(Product_ID, Warehouse_ID, Locator_ID); +END; +$BODY$ +LANGUAGE 'plpgsql' STABLE +; + +CREATE OR REPLACE FUNCTION bomqtyonhand (in product_id numeric, in warehouse_id numeric, in locator_id numeric) RETURNS numeric AS +$BODY$ +DECLARE + myWarehouse_ID numeric; + v_Quantity numeric := 99999; -- unlimited + v_IsBOM CHAR(1); + v_IsStocked CHAR(1); + v_ProductType CHAR(1); + v_ProductQty numeric; + v_StdPrecision int; + bom record; + +BEGIN + -- Check Parameters + myWarehouse_ID := Warehouse_ID; + IF (myWarehouse_ID IS NULL) THEN + IF (Locator_ID IS NULL) THEN + RETURN 0; + ELSE + SELECT SUM(M_Warehouse_ID) INTO myWarehouse_ID + FROM M_LOCATOR + WHERE M_Locator_ID=Locator_ID; + END IF; + END IF; + IF (myWarehouse_ID IS NULL) THEN + RETURN 0; + END IF; + + -- Check, if product exists and if it is stocked + BEGIN + SELECT IsBOM, ProductType, IsStocked + INTO v_IsBOM, v_ProductType, v_IsStocked + FROM M_PRODUCT + WHERE M_Product_ID=Product_ID; + -- + EXCEPTION -- not found + WHEN OTHERS THEN + RETURN 0; + END; + -- Unimited capacity if no item + IF (v_IsBOM='N' AND (v_ProductType<>'I' OR v_IsStocked='N')) THEN + RETURN v_Quantity; + -- Stocked item + ELSIF (v_IsStocked='Y') THEN + -- Get ProductQty + SELECT COALESCE(SUM(QtyOnHand), 0) + INTO v_ProductQty + FROM M_STORAGE s + WHERE M_Product_ID=Product_ID + AND EXISTS (SELECT * FROM M_LOCATOR l WHERE s.M_Locator_ID=l.M_Locator_ID + AND l.M_Warehouse_ID=myWarehouse_ID); + -- + RETURN v_ProductQty; + END IF; + + -- Go though BOM + FOR bom IN -- Get BOM Product info + SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM, p.IsStocked, p.ProductType + FROM M_PRODUCT_BOM b, M_PRODUCT p + WHERE b.M_ProductBOM_ID=p.M_Product_ID + AND b.M_Product_ID=product_ID + AND b.M_ProductBOM_ID != Product_ID + AND p.IsBOM='Y' + AND p.IsVerified='Y' + AND b.IsActive='Y' + LOOP + -- Stocked Items "leaf node" + IF (bom.ProductType = 'I' AND bom.IsStocked = 'Y') THEN + -- Get v_ProductQty + SELECT COALESCE(SUM(QtyOnHand), 0) + INTO v_ProductQty + FROM M_STORAGE s + WHERE M_Product_ID=bom.M_ProductBOM_ID + AND EXISTS (SELECT * FROM M_LOCATOR l WHERE s.M_Locator_ID=l.M_Locator_ID + AND l.M_Warehouse_ID=myWarehouse_ID); + -- Get Rounding Precision + SELECT COALESCE(MAX(u.StdPrecision), 0) + INTO v_StdPrecision + FROM C_UOM u, M_PRODUCT p + WHERE u.C_UOM_ID=p.C_UOM_ID AND p.M_Product_ID=bom.M_ProductBOM_ID; + -- How much can we make with this product + v_ProductQty := ROUND (v_ProductQty/bom.BOMQty, v_StdPrecision); + -- How much can we make overall + IF (v_ProductQty < v_Quantity) THEN + v_Quantity := v_ProductQty; + END IF; + -- Another BOM + ELSIF (bom.IsBOM = 'Y') THEN + v_ProductQty := Bomqtyonhand (bom.M_ProductBOM_ID, myWarehouse_ID, Locator_ID); + -- How much can we make overall + IF (v_ProductQty < v_Quantity) THEN + v_Quantity := v_ProductQty; + END IF; + END IF; + END LOOP; -- BOM + + IF (v_Quantity > 0) THEN + -- Get Rounding Precision for Product + SELECT COALESCE(MAX(u.StdPrecision), 0) + INTO v_StdPrecision + FROM C_UOM u, M_PRODUCT p + WHERE u.C_UOM_ID=p.C_UOM_ID AND p.M_Product_ID=Product_ID; + -- + RETURN ROUND (v_Quantity, v_StdPrecision); + END IF; + RETURN 0; +END; +$BODY$ +LANGUAGE 'plpgsql' STABLE +; + +CREATE OR REPLACE FUNCTION bomqtyordered (in p_product_id numeric, in p_warehouse_id numeric, in p_locator_id numeric) RETURNS numeric AS +$BODY$ +DECLARE + v_Warehouse_ID numeric; + v_Quantity numeric := 99999; -- unlimited + v_IsBOM CHAR(1); + v_IsStocked CHAR(1); + v_ProductType CHAR(1); + v_ProductQty numeric; + v_StdPrecision int; + bom record; +BEGIN + -- Check Parameters + v_Warehouse_ID := p_Warehouse_ID; + IF (v_Warehouse_ID IS NULL) THEN + IF (p_Locator_ID IS NULL) THEN + RETURN 0; + ELSE + SELECT MAX(M_Warehouse_ID) INTO v_Warehouse_ID + FROM M_LOCATOR + WHERE M_Locator_ID=p_Locator_ID; + END IF; + END IF; + IF (v_Warehouse_ID IS NULL) THEN + RETURN 0; + END IF; + + -- Check, if product exists and if it is stocked + BEGIN + SELECT IsBOM, ProductType, IsStocked + INTO v_IsBOM, v_ProductType, v_IsStocked + FROM M_PRODUCT + WHERE M_Product_ID=p_Product_ID; + -- + EXCEPTION -- not found + WHEN OTHERS THEN + RETURN 0; + END; + + -- No reservation for non-stocked + IF (v_IsBOM='N' AND (v_ProductType<>'I' OR v_IsStocked='N')) THEN + RETURN 0; + -- Stocked item + ELSIF (v_IsStocked='Y') THEN + -- Get ProductQty + SELECT COALESCE(SUM(Qty), 0) + INTO v_ProductQty + FROM M_StorageReservation + WHERE M_Product_ID=p_Product_ID + AND M_Warehouse_ID=v_Warehouse_ID + AND IsSOTrx='N' + AND IsActive='Y'; + -- + RETURN v_ProductQty; + END IF; + + -- Go though BOM + FOR bom IN + -- Get BOM Product info + SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM, p.IsStocked, p.ProductType + FROM M_PRODUCT_BOM b, M_PRODUCT p + WHERE b.M_ProductBOM_ID=p.M_Product_ID + AND b.M_Product_ID=p_Product_ID + AND b.M_ProductBOM_ID != p_Product_ID + AND p.IsBOM='Y' + AND p.IsVerified='Y' + AND b.IsActive='Y' + LOOP + -- Stocked Items "leaf node" + IF (bom.ProductType = 'I' AND bom.IsStocked = 'Y') THEN + -- Get ProductQty + SELECT COALESCE(SUM(Qty), 0) + INTO v_ProductQty + FROM M_StorageReservation + WHERE M_Product_ID=p_Product_ID + AND M_Warehouse_ID=v_Warehouse_ID + AND IsSOTrx='N' + AND IsActive='Y'; + -- Get Rounding Precision + SELECT COALESCE(MAX(u.StdPrecision), 0) + INTO v_StdPrecision + FROM C_UOM u, M_PRODUCT p + WHERE u.C_UOM_ID=p.C_UOM_ID AND p.M_Product_ID=bom.M_ProductBOM_ID; + -- How much can we make with this product + v_ProductQty := ROUND (v_ProductQty/bom.BOMQty, v_StdPrecision ); + + -- How much can we make overall + IF (v_ProductQty < v_Quantity) THEN + v_Quantity := v_ProductQty; + END IF; + -- Another BOM + ELSIF (bom.IsBOM = 'Y') THEN + v_ProductQty := Bomqtyordered (bom.M_ProductBOM_ID, v_Warehouse_ID, p_Locator_ID); + -- How much can we make overall + IF (v_ProductQty < v_Quantity) THEN + v_Quantity := v_ProductQty; + END IF; + END IF; + END LOOP; -- BOM + + -- Unlimited (e.g. only services) + IF (v_Quantity = 99999) THEN + RETURN 0; + END IF; + + IF (v_Quantity > 0) THEN + -- Get Rounding Precision for Product + SELECT COALESCE(MAX(u.StdPrecision), 0) + INTO v_StdPrecision + FROM C_UOM u, M_PRODUCT p + WHERE u.C_UOM_ID=p.C_UOM_ID AND p.M_Product_ID=p_Product_ID; + -- + RETURN ROUND (v_Quantity, v_StdPrecision ); + END IF; + -- + RETURN 0; +END; +$BODY$ + LANGUAGE plpgsql STABLE; + +CREATE OR REPLACE FUNCTION bomqtyreserved (in p_product_id numeric, in p_warehouse_id numeric, in p_locator_id numeric) RETURNS numeric AS +$BODY$ +DECLARE + v_Warehouse_ID numeric; + v_Quantity numeric := 99999; -- unlimited + v_IsBOM CHAR(1); + v_IsStocked CHAR(1); + v_ProductType CHAR(1); + v_ProductQty numeric; + v_StdPrecision int; + bom record; +BEGIN + -- Check Parameters + v_Warehouse_ID := p_Warehouse_ID; + IF (v_Warehouse_ID IS NULL) THEN + IF (p_Locator_ID IS NULL) THEN + RETURN 0; + ELSE + SELECT MAX(M_Warehouse_ID) INTO v_Warehouse_ID + FROM M_LOCATOR + WHERE M_Locator_ID=p_Locator_ID; + END IF; + END IF; + IF (v_Warehouse_ID IS NULL) THEN + RETURN 0; + END IF; + + -- Check, if product exists and if it is stocked + BEGIN + SELECT IsBOM, ProductType, IsStocked + INTO v_IsBOM, v_ProductType, v_IsStocked + FROM M_PRODUCT + WHERE M_Product_ID=p_Product_ID; + -- + EXCEPTION -- not found + WHEN OTHERS THEN + RETURN 0; + END; + + -- No reservation for non-stocked + IF (v_IsBOM='N' AND (v_ProductType<>'I' OR v_IsStocked='N')) THEN + RETURN 0; + -- Stocked item + ELSIF (v_IsStocked='Y') THEN + -- Get ProductQty + SELECT COALESCE(SUM(Qty), 0) + INTO v_ProductQty + FROM M_StorageReservation + WHERE M_Product_ID=p_Product_ID + AND M_Warehouse_ID=v_Warehouse_ID + AND IsSOTrx='Y' + AND IsActive='Y'; + -- + RETURN v_ProductQty; + END IF; + + -- Go though BOM + FOR bom IN + -- Get BOM Product info + SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM, p.IsStocked, p.ProductType + FROM M_PRODUCT_BOM b, M_PRODUCT p + WHERE b.M_ProductBOM_ID=p.M_Product_ID + AND b.M_Product_ID=p_Product_ID + AND b.M_ProductBOM_ID != p_Product_ID + AND p.IsBOM='Y' + AND p.IsVerified='Y' + AND b.IsActive='Y' + LOOP + -- Stocked Items "leaf node" + IF (bom.ProductType = 'I' AND bom.IsStocked = 'Y') THEN + -- Get ProductQty + SELECT COALESCE(SUM(Qty), 0) + INTO v_ProductQty + FROM M_StorageReservation + WHERE M_Product_ID=bom.M_ProductBOM_ID + AND M_Warehouse_ID =v_Warehouse_ID + AND IsSOTrx='Y' + AND IsActive='Y'; + -- Get Rounding Precision + SELECT COALESCE(MAX(u.StdPrecision), 0) + INTO v_StdPrecision + FROM C_UOM u, M_PRODUCT p + WHERE u.C_UOM_ID=p.C_UOM_ID AND p.M_Product_ID=bom.M_ProductBOM_ID; + -- How much can we make with this product + v_ProductQty := ROUND (v_ProductQty/bom.BOMQty, v_StdPrecision); + -- How much can we make overall + IF (v_ProductQty < v_Quantity) THEN + v_Quantity := v_ProductQty; + END IF; + -- Another BOM + ELSIF (bom.IsBOM = 'Y') THEN + v_ProductQty := Bomqtyreserved (bom.M_ProductBOM_ID, v_Warehouse_ID, p_Locator_ID); + -- How much can we make overall + IF (v_ProductQty < v_Quantity) THEN + v_Quantity := v_ProductQty; + END IF; + END IF; + END LOOP; -- BOM + + -- Unlimited (e.g. only services) + IF (v_Quantity = 99999) THEN + RETURN 0; + END IF; + + IF (v_Quantity > 0) THEN + -- Get Rounding Precision for Product + SELECT COALESCE(MAX(u.StdPrecision), 0) + INTO v_StdPrecision + FROM C_UOM u, M_PRODUCT p + WHERE u.C_UOM_ID=p.C_UOM_ID AND p.M_Product_ID=p_Product_ID; + -- + RETURN ROUND (v_Quantity, v_StdPrecision); + END IF; + RETURN 0; +END; +$BODY$ + LANGUAGE plpgsql STABLE; + + +SELECT register_migration_script('201405160521_IDEMPIERE-1953.sql') FROM dual +; \ No newline at end of file