IDEMPIERE-1370 Circular Reference in Product Info when a bom product is referenced to itself and its price-lists prices are set to zero

Thorough review of the BOM verification process:
1 - changed the three BOMprice functions to not take into account unverified BOMs
2 - fixed four BOMQty functions to point to correct BOM structures
3 - dropped from DB unused BOMQty*ASI functions and deprecated M_Product_BOM_Check.java class
4 - Verify BOMs process inactivated (pointing to unused structures)
5 - set default and displaylogic for process parameters of Verify BOM Structure
6 - set product verified flag as readonly to allow modification just via process verify BOM
7 - BOM products are marked as not verified when lines are changed or last line deleted
This commit is contained in:
Carlos Ruiz 2013-09-19 21:52:10 -05:00
parent c210b5e571
commit 5bc1dae184
27 changed files with 1889 additions and 1899 deletions

View File

@ -1,4 +1,4 @@
CREATE OR REPLACE FUNCTION Bompricelimit
CREATE OR REPLACE FUNCTION BOMPRICELIMIT
(
Product_ID IN NUMBER,
PriceList_Version_ID IN NUMBER
@ -22,15 +22,12 @@ AS
v_ProductPrice NUMBER;
-- Get BOM Product info
CURSOR CUR_BOM IS
/*SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM
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;*/
SELECT bl.M_Product_ID AS M_ProductBOM_ID, CASE WHEN bl.IsQtyPercentage = 'N' THEN bl.QtyBOM ELSE bl.QtyBatch / 100 END AS BomQty , p.IsBOM
FROM PP_PRODUCT_BOM b
INNER JOIN M_PRODUCT p ON (p.M_Product_ID=b.M_Product_ID)
INNER JOIN PP_PRODUCT_BOMLINE bl ON (bl.PP_Product_BOM_ID=b.PP_Product_BOM_ID)
WHERE b.M_Product_ID = Product_ID;
AND b.M_Product_ID=Product_ID
AND p.IsBOM='Y'
AND p.IsVerified='Y';
--
BEGIN
-- Try to get price from PriceList directly
@ -51,3 +48,4 @@ BEGIN
RETURN v_Price;
END Bompricelimit;
/

View File

@ -1,4 +1,4 @@
CREATE OR REPLACE FUNCTION Bompricelist
CREATE OR REPLACE FUNCTION BOMPRICELIST
(
Product_ID IN NUMBER,
PriceList_Version_ID IN NUMBER
@ -22,15 +22,12 @@ AS
v_ProductPrice NUMBER;
-- Get BOM Product info
CURSOR CUR_BOM IS
/*SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM
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;*/
SELECT bl.M_Product_ID AS M_ProductBOM_ID, CASE WHEN bl.IsQtyPercentage = 'N' THEN bl.QtyBOM ELSE bl.QtyBatch / 100 END AS BomQty , p.IsBOM
FROM PP_PRODUCT_BOM b
INNER JOIN M_PRODUCT p ON (p.M_Product_ID=b.M_Product_ID)
INNER JOIN PP_PRODUCT_BOMLINE bl ON (bl.PP_Product_BOM_ID=b.PP_Product_BOM_ID)
WHERE b.M_Product_ID = Product_ID;
AND b.M_Product_ID=Product_ID
AND p.IsBOM='Y'
AND p.IsVerified='Y';
--
BEGIN
-- Try to get price from pricelist directly
@ -52,3 +49,4 @@ BEGIN
RETURN v_Price;
END Bompricelist;
/

View File

@ -1,4 +1,4 @@
CREATE OR REPLACE FUNCTION Bompricestd
CREATE OR REPLACE FUNCTION BOMPRICESTD
(
Product_ID IN NUMBER,
PriceList_Version_ID IN NUMBER
@ -22,15 +22,12 @@ AS
v_ProductPrice NUMBER;
-- Get BOM Product info
CURSOR CUR_BOM IS
/*SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM
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;*/
SELECT bl.M_Product_ID AS M_ProductBOM_ID, CASE WHEN bl.IsQtyPercentage = 'N' THEN bl.QtyBOM ELSE bl.QtyBatch / 100 END AS BomQty , p.IsBOM
FROM PP_PRODUCT_BOM b
INNER JOIN M_PRODUCT p ON (p.M_Product_ID=b.M_Product_ID)
INNER JOIN PP_PRODUCT_BOMLINE bl ON (bl.PP_Product_BOM_ID=b.PP_Product_BOM_ID)
WHERE b.M_Product_ID = Product_ID;
AND b.M_Product_ID=Product_ID
AND p.IsBOM='Y'
AND p.IsVerified='Y';
--
BEGIN
-- Try to get price from pricelist directly
@ -52,3 +49,4 @@ BEGIN
RETURN v_Price;
END Bompricestd;
/

View File

@ -1,4 +1,4 @@
CREATE OR REPLACE FUNCTION bomQtyAvailable
CREATE OR REPLACE FUNCTION BOMQTYAVAILABLE
(
Product_ID IN NUMBER,
Warehouse_ID IN NUMBER,
@ -19,3 +19,4 @@ BEGIN
- bomQtyReserved(Product_ID, Warehouse_ID, Locator_ID);
END bomQtyAvailable;
/

View File

@ -1,22 +0,0 @@
CREATE OR REPLACE FUNCTION BomqtyavailableASI
(
Product_ID IN NUMBER,
AttributeSetInstance_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 available for BOM ASI
*/
AS
BEGIN
RETURN BomqtyonhandASI(Product_ID, AttributeSetInstance_ID, Warehouse_ID, Locator_ID)
- BomqtyreservedASI(Product_ID, AttributeSetInstance_ID, Warehouse_ID, Locator_ID);
END BomqtyavailableASI;
/

View File

@ -1,4 +1,4 @@
CREATE OR REPLACE FUNCTION Bomqtyonhand
CREATE OR REPLACE FUNCTION BOMQTYONHAND
(
Product_ID IN NUMBER,
Warehouse_ID IN NUMBER,
@ -23,15 +23,12 @@ AS
StdPrecision NUMBER;
-- Get BOM Product info
CURSOR CUR_BOM IS
/*SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM, p.IsStocked, p.ProductType
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;*/
SELECT bl.M_Product_ID AS M_ProductBOM_ID, CASE WHEN bl.IsQtyPercentage = 'N' THEN bl.QtyBOM ELSE bl.QtyBatch / 100 END AS BomQty , p.IsBOM , p.IsStocked, p.ProductType
FROM PP_PRODUCT_BOM b
INNER JOIN M_PRODUCT p ON (p.M_Product_ID=b.M_Product_ID)
INNER JOIN PP_PRODUCT_BOMLINE bl ON (bl.PP_Product_BOM_ID=b.PP_Product_BOM_ID)
WHERE b.M_Product_ID = Product_ID;
AND b.M_Product_ID=Product_ID
AND p.IsBOM='Y'
AND p.IsVerified='Y';
--
BEGIN
-- Check Parameters
@ -123,3 +120,4 @@ BEGIN
RETURN 0;
END Bomqtyonhand;
/

View File

@ -1,128 +0,0 @@
CREATE OR REPLACE FUNCTION BomqtyonhandASI
(
Product_ID IN NUMBER,
AttributeSetInstance_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=p_Product_ID;*/
SELECT bl.M_Product_ID AS M_ProductBOM_ID, CASE WHEN bl.IsQtyPercentage = 'N' THEN bl.QtyBOM ELSE bl.QtyBatch / 100 END AS BomQty , p.IsBOM , p.IsStocked, p.ProductType
FROM PP_PRODUCT_BOM b
INNER JOIN M_PRODUCT p ON (p.M_Product_ID=b.M_Product_ID)
INNER JOIN PP_PRODUCT_BOMLINE bl ON (bl.PP_Product_BOM_ID=b.PP_Product_BOM_ID)
WHERE b.M_Product_ID = Product_ID;
--
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)
AND (s.M_AttributeSetInstance_ID = AttributeSetInstance_ID OR NVL(AttributeSetInstance_ID,0) = 0);
--
-- 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)
AND (s.M_AttributeSetInstance_ID = AttributeSetInstance_ID OR NVL(AttributeSetInstance_ID,0) = 0);
-- 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 := BomqtyonhandASI (bom.M_ProductBOM_ID, AttributeSetInstance_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 BomqtyonhandASI;
/

View File

@ -1,4 +1,4 @@
CREATE OR REPLACE FUNCTION Bomqtyordered
CREATE OR REPLACE FUNCTION BOMQTYORDERED
(
p_Product_ID IN NUMBER,
p_Warehouse_ID IN NUMBER,
@ -23,15 +23,12 @@ AS
v_StdPrecision NUMBER;
-- Get BOM Product info
CURSOR CUR_BOM IS
/*SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM, p.IsStocked, p.ProductType
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;*/
SELECT bl.M_Product_ID AS M_ProductBOM_ID, CASE WHEN bl.IsQtyPercentage = 'N' THEN bl.QtyBOM ELSE bl.QtyBatch / 100 END AS BomQty , p.IsBOM , p.IsStocked, p.ProductType
FROM PP_PRODUCT_BOM b
INNER JOIN M_PRODUCT p ON (p.M_Product_ID=b.M_Product_ID)
INNER JOIN PP_PRODUCT_BOMLINE bl ON (bl.PP_Product_BOM_ID=b.PP_Product_BOM_ID)
WHERE b.M_Product_ID = p_Product_ID;
AND b.M_Product_ID=p_Product_ID
AND p.IsBOM='Y'
AND p.IsVerified='Y';
--
BEGIN
-- Check Parameters
@ -129,3 +126,4 @@ BEGIN
RETURN 0;
END Bomqtyordered;
/

View File

@ -1,134 +0,0 @@
CREATE OR REPLACE FUNCTION BomqtyorderedASI
(
p_Product_ID IN NUMBER,
AttributeSetInstance_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;*/
SELECT bl.M_Product_ID AS M_ProductBOM_ID, CASE WHEN bl.IsQtyPercentage = 'N' THEN bl.QtyBOM ELSE bl.QtyBatch / 100 END AS BomQty , p.IsBOM , p.IsStocked, p.ProductType
FROM PP_PRODUCT_BOM b
INNER JOIN M_PRODUCT p ON (p.M_Product_ID=b.M_Product_ID)
INNER JOIN PP_PRODUCT_BOMLINE bl ON (bl.PP_Product_BOM_ID=b.PP_Product_BOM_ID)
WHERE b.M_Product_ID = p_Product_ID;
--
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(QtyOrdered), 0)
INTO v_ProductQty
FROM M_STORAGE s
WHERE M_Product_ID=p_Product_ID
AND EXISTS (SELECT * FROM M_LOCATOR l WHERE s.M_Locator_ID=l.M_Locator_ID
AND l.M_Warehouse_ID=v_Warehouse_ID)
AND (s.M_AttributeSetInstance_ID = AttributeSetInstance_ID OR NVL(AttributeSetInstance_ID,0) = 0);
--
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(QtyOrdered), 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=v_Warehouse_ID)
AND (s.M_AttributeSetInstance_ID = AttributeSetInstance_ID OR NVL(AttributeSetInstance_ID,0) = 0);
-- 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 := BomqtyorderedASI (bom.M_ProductBOM_ID, AttributeSetInstance_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 BomqtyorderedASI;
/

View File

@ -1,4 +1,4 @@
CREATE OR REPLACE FUNCTION Bomqtyreserved
CREATE OR REPLACE FUNCTION BOMQTYRESERVED
(
p_Product_ID IN NUMBER,
p_Warehouse_ID IN NUMBER,
@ -23,15 +23,12 @@ AS
v_StdPrecision NUMBER;
-- Get BOM Product info
CURSOR CUR_BOM IS
/*SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM, p.IsStocked, p.ProductType
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;*/
SELECT bl.M_Product_ID AS M_ProductBOM_ID, CASE WHEN bl.IsQtyPercentage = 'N' THEN bl.QtyBOM ELSE bl.QtyBatch / 100 END AS BomQty , p.IsBOM , p.IsStocked, p.ProductType
FROM PP_PRODUCT_BOM b
INNER JOIN M_PRODUCT p ON (p.M_Product_ID=b.M_Product_ID)
INNER JOIN PP_PRODUCT_BOMLINE bl ON (bl.PP_Product_BOM_ID=b.PP_Product_BOM_ID)
WHERE b.M_Product_ID = p_Product_ID;
AND b.M_Product_ID=p_Product_ID
AND p.IsBOM='Y'
AND p.IsVerified='Y';
--
BEGIN
-- Check Parameters
@ -128,3 +125,4 @@ BEGIN
RETURN 0;
END Bomqtyreserved;
/

View File

@ -1,133 +0,0 @@
CREATE OR REPLACE FUNCTION BomqtyreservedASI
(
p_Product_ID IN NUMBER,
AttributeSetInstance_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;*/
SELECT bl.M_Product_ID AS M_ProductBOM_ID, CASE WHEN bl.IsQtyPercentage = 'N' THEN bl.QtyBOM ELSE bl.QtyBatch / 100 END AS BomQty , p.IsBOM , p.IsStocked, p.ProductType
FROM PP_PRODUCT_BOM b
INNER JOIN M_PRODUCT p ON (p.M_Product_ID=b.M_Product_ID)
INNER JOIN PP_PRODUCT_BOMLINE bl ON (bl.PP_Product_BOM_ID=b.PP_Product_BOM_ID)
WHERE b.M_Product_ID = p_Product_ID;
--
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(QtyReserved), 0)
INTO v_ProductQty
FROM M_STORAGE s
WHERE M_Product_ID=p_Product_ID
AND EXISTS (SELECT * FROM M_LOCATOR l WHERE s.M_Locator_ID=l.M_Locator_ID
AND l.M_Warehouse_ID=v_Warehouse_ID)
AND (s.M_AttributeSetInstance_ID = AttributeSetInstance_ID OR NVL(AttributeSetInstance_ID,0) = 0);
--
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(QtyReserved), 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=v_Warehouse_ID)
AND (s.M_AttributeSetInstance_ID = AttributeSetInstance_ID OR NVL(AttributeSetInstance_ID,0) = 0);
-- 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 := BomqtyreservedASI (bom.M_ProductBOM_ID, AttributeSetInstance_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 BomqtyreservedASI;
/

View File

@ -1,50 +1,37 @@
/*
*This file is part of Adempiere ERP Bazaar
*http://www.adempiere.org
*
*Copyright (C) 2006-2008 Antonio Cañaveral, e-Evolution
*
*This program is free software; you can redistribute it and/or
*modify it under the terms of the GNU General Public License
*as published by the Free Software Foundation; either version 2
*of the License, or (at your option) any later version.
*
*This program is distributed in the hope that it will be useful,
*but WITHOUT ANY WARRANTY; without even the implied warranty of
*MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*GNU General Public License for more details.
*
*You should have received a copy of the GNU General Public License
*along with this program; if not, write to the Free Software
*Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.of
*/
CREATE OR REPLACE FUNCTION bompricelimit(p_product_id numeric, p_pricelist_version_id numeric)
RETURNS numeric AS
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;
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=p_PriceList_Version_ID AND M_Product_ID=p_Product_ID;
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 bl.M_Product_ID AS M_ProductBOM_ID,
CASE WHEN bl.IsQtyPercentage = 'N' THEN bl.QtyBOM ELSE bl.QtyBatch / 100 END AS BomQty , p.IsBOM
FROM PP_PRODUCT_BOM b
INNER JOIN M_PRODUCT p ON (p.M_Product_ID=b.M_Product_ID)
INNER JOIN PP_PRODUCT_BOMLINE bl ON (bl.PP_Product_BOM_ID=b.PP_Product_BOM_ID)
WHERE b.M_Product_ID = p_Product_ID
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 p.IsBOM='Y'
AND p.IsVerified='Y'
LOOP
v_ProductPrice := Bompricelimit (bom.M_ProductBOM_ID, p_PriceList_Version_ID);
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' ;
LANGUAGE 'plpgsql'
;

View File

@ -1,60 +1,37 @@
/*
*This file is part of Adempiere ERP Bazaar
*http://www.adempiere.org
*Copyright (C) 2006-2008 victor.perez@e-evolution.com, e-Evolution
*This program is free software; you can redistribute it and/or
*modify it under the terms of the GNU General Public License
*as published by the Free Software Foundation; either version 2
*of the License, or (at your option) any later version.
*
*This program is distributed in the hope that it will be useful,
*but WITHOUT ANY WARRANTY; without even the implied warranty of
*MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*GNU General Public License for more details.
*
*You should have received a copy of the GNU General Public License
*along with this program; if not, write to the Free Software
*Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.of
* Title: Return List Price of Product/BOM
* Description:
*/
CREATE OR REPLACE FUNCTION Bompricelist
(
Product_ID numeric,
PriceList_Version_ID numeric
)
RETURNS numeric
AS
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;
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
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
--Get BOM Product info
SELECT bl.M_Product_ID AS M_ProductBOM_ID, CASE WHEN bl.IsQtyPercentage = 'N' THEN bl.QtyBOM ELSE bl.QtyBatch / 100 END AS BomQty , p.IsBOM
FROM PP_PRODUCT_BOM b
INNER JOIN M_PRODUCT p ON (p.M_Product_ID=b.M_Product_ID)
INNER JOIN PP_PRODUCT_BOMLINE bl ON (bl.PP_Product_BOM_ID=b.PP_Product_BOM_ID)
WHERE b.M_Product_ID = Product_ID
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 p.IsBOM='Y'
AND p.IsVerified='Y'
LOOP
v_ProductPrice := Bompricelist (bom.M_ProductBOM_ID, PriceList_Version_ID);
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 LOOP;
END IF;
--
RETURN v_Price;
END;
$BODY$
LANGUAGE 'plpgsql' ;
LANGUAGE 'plpgsql'
;

View File

@ -1,59 +1,37 @@
/*
*This file is part of Adempiere ERP Bazaar
*http://www.adempiere.org
*Copyright (C) 2006-2008 victor.perez@e-evolution.com, e-Evolution
*This program is free software; you can redistribute it and/or
*modify it under the terms of the GNU General Public License
*as published by the Free Software Foundation; either version 2
*of the License, or (at your option) any later version.
*
*This program is distributed in the hope that it will be useful,
*but WITHOUT ANY WARRANTY; without even the implied warranty of
*MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*GNU General Public License for more details.
*
*You should have received a copy of the GNU General Public License
*along with this program; if not, write to the Free Software
*Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.of
* Title: Return Standard Price of Product/BOM
* Description:
*/
CREATE OR REPLACE FUNCTION Bompricestd
(
Product_ID numeric,
PriceList_Version_ID numeric
)
RETURNS numeric
AS
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;
v_Price NUMERIC;
v_ProductPrice NUMERIC;
bom RECORD;
BEGIN
-- Try to get price from pricelist directly
-- Try to get price from PriceList directly
SELECT COALESCE(SUM(PriceStd), 0)
INTO v_Price
FROM M_PRODUCTPRICE
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 -- Get BOM Product info
SELECT bl.M_Product_ID AS M_ProductBOM_ID, CASE WHEN bl.IsQtyPercentage = 'N' THEN bl.QtyBOM ELSE bl.QtyBatch / 100 END AS BomQty , p.IsBOM
FROM PP_PRODUCT_BOM b
INNER JOIN M_PRODUCT p ON (p.M_Product_ID=b.M_Product_ID)
INNER JOIN PP_PRODUCT_BOMLINE bl ON (bl.PP_Product_BOM_ID=b.PP_Product_BOM_ID)
WHERE b.M_Product_ID = Product_ID
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 p.IsBOM='Y'
AND p.IsVerified='Y'
LOOP
v_ProductPrice := Bompricestd (bom.M_ProductBOM_ID, PriceList_Version_ID);
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 LOOP;
END IF;
--
RETURN v_Price;
END;
$BODY$
LANGUAGE 'plpgsql' ;
LANGUAGE 'plpgsql'
;

View File

@ -1,26 +1,9 @@
/*
*This file is part of Adempiere ERP Bazaar
*http://www.adempiere.org
*Copyright (C) 2006-2008 victor.perez@e-evolution.com, e-Evolution
*This program is free software; you can redistribute it and/or
*modify it under the terms of the GNU General Public License
*as published by the Free Software Foundation; either version 2
*of the License, or (at your option) any later version.
*
*This program is distributed in the hope that it will be useful,
*but WITHOUT ANY WARRANTY; without even the implied warranty of
*MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*GNU General Public License for more details.
*
*You should have received a copy of the GNU General Public License
*along with this program; if not, write to the Free Software
*Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.of
*/
CREATE OR REPLACE FUNCTION bomQtyAvailable(Product_ID numeric, Warehouse_ID numeric ,Locator_ID numeric)
RETURNS numeric AS
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' ;
LANGUAGE 'plpgsql'
;

View File

@ -1,27 +0,0 @@
/*
*This file is part of Adempiere ERP Bazaar
*http://www.adempiere.org
*Copyright (C) 2006-2008 carlos.ruiz@globalqss.com, GlobalQSS
*This program is free software; you can redistribute it and/or
*modify it under the terms of the GNU General Public License
*as published by the Free Software Foundation; either version 2
*of the License, or (at your option) any later version.
*
*This program is distributed in the hope that it will be useful,
*but WITHOUT ANY WARRANTY; without even the implied warranty of
*MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*GNU General Public License for more details.
*
*You should have received a copy of the GNU General Public License
*along with this program; if not, write to the Free Software
*Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.of
*/
CREATE OR REPLACE FUNCTION bomQtyAvailableASI(Product_ID numeric, AttributeSetInstance_ID numeric, Warehouse_ID numeric ,Locator_ID numeric)
RETURNS numeric AS
$BODY$
BEGIN
RETURN bomQtyOnHandASI(Product_ID, AttributeSetInstance_ID, Warehouse_ID, Locator_ID) -
bomQtyReservedASI(Product_ID, AttributeSetInstance_ID, Warehouse_ID, Locator_ID);
END;
$BODY$
LANGUAGE 'plpgsql' ;

View File

@ -1,29 +1,4 @@
/*
*This file is part of Adempiere ERP Bazaar
*http://www.adempiere.org
*Copyright (C) 2006-2008 victor.perez@e-evolution.com, e-Evolution
*This program is free software; you can redistribute it and/or
*modify it under the terms of the GNU General Public License
*as published by the Free Software Foundation; either version 2
*of the License, or (at your option) any later version.
*
*This program is distributed in the hope that it will be useful,
*but WITHOUT ANY WARRANTY; without even the implied warranty of
*MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*GNU General Public License for more details.
*
*You should have received a copy of the GNU General Public License
*along with this program; if not, write to the Free Software
*Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.of
*/
CREATE OR REPLACE FUNCTION Bomqtyonhand
(
Product_ID numeric,
Warehouse_ID numeric,
Locator_ID numeric -- Only used, if warehouse is null
)
RETURNS numeric
AS
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;
@ -50,7 +25,7 @@ BEGIN
IF (myWarehouse_ID IS NULL) THEN
RETURN 0;
END IF;
-- DBMS_OUTPUT.PUT_LINE('Warehouse=' || myWarehouse_ID);
-- DBMS_OUTPUT.PUT_LINE(''Warehouse='' || myWarehouse_ID);
-- Check, if product exists and if it is stocked
BEGIN
@ -76,18 +51,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);
-- DBMS_OUTPUT.PUT_LINE(''Qty='' || v_ProductQty);
RETURN v_ProductQty;
END IF;
-- Go though BOM
-- DBMS_OUTPUT.PUT_LINE('BOM');
-- DBMS_OUTPUT.PUT_LINE(''BOM'');
FOR bom IN -- Get BOM Product info
SELECT bl.M_Product_ID AS M_ProductBOM_ID, CASE WHEN bl.IsQtyPercentage = 'N' THEN bl.QtyBOM ELSE bl.QtyBatch / 100 END AS BomQty , p.IsBOM , p.IsStocked, p.ProductType
FROM PP_PRODUCT_BOM b
INNER JOIN M_PRODUCT p ON (p.M_Product_ID=b.M_Product_ID)
INNER JOIN PP_PRODUCT_BOMLINE bl ON (bl.PP_Product_BOM_ID=b.PP_Product_BOM_ID)
WHERE b.M_Product_ID = Product_ID
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 p.IsBOM='Y'
AND p.IsVerified='Y'
LOOP
-- Stocked Items "leaf node"
IF (bom.ProductType = 'I' AND bom.IsStocked = 'Y') THEN
@ -131,4 +107,6 @@ BEGIN
RETURN 0;
END;
$BODY$
LANGUAGE 'plpgsql' ;
LANGUAGE 'plpgsql'
;

View File

@ -1,138 +0,0 @@
/*
*This file is part of Adempiere ERP Bazaar
*http://www.adempiere.org
*Copyright (C) 2006-2008 victor.perez@e-evolution.com, e-Evolution
*This program is free software; you can redistribute it and/or
*modify it under the terms of the GNU General Public License
*as published by the Free Software Foundation; either version 2
*of the License, or (at your option) any later version.
*
*This program is distributed in the hope that it will be useful,
*but WITHOUT ANY WARRANTY; without even the implied warranty of
*MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*GNU General Public License for more details.
*
*You should have received a copy of the GNU General Public License
*along with this program; if not, write to the Free Software
*Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.of
* Return quantity on hand for BOM ASI
*/
CREATE OR REPLACE FUNCTION BomqtyonhandASI
(
Product_ID numeric,
AttributeSetInstance_ID numeric,
Warehouse_ID numeric,
Locator_ID numeric -- Only used, if warehouse is null
)
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 numeric;
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;
-- DBMS_OUTPUT.PUT_LINE('Warehouse=' || myWarehouse_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=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 v_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)
AND (s.M_AttributeSetInstance_ID = AttributeSetInstance_ID OR COALESCE(AttributeSetInstance_ID,0) = 0);
--
-- 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 bl.M_Product_ID AS M_ProductBOM_ID, CASE WHEN bl.IsQtyPercentage = 'N' THEN bl.QtyBOM ELSE bl.QtyBatch / 100 END AS BomQty , p.IsBOM , p.IsStocked, p.ProductType
FROM PP_PRODUCT_BOM b
INNER JOIN M_PRODUCT p ON (p.M_Product_ID=b.M_Product_ID)
INNER JOIN PP_PRODUCT_BOMLINE bl ON (bl.PP_Product_BOM_ID=b.PP_Product_BOM_ID)
WHERE b.M_Product_ID = Product_ID
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)
AND (s.M_AttributeSetInstance_ID = AttributeSetInstance_ID OR COALESCE(AttributeSetInstance_ID,0) = 0);
-- 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 := BomqtyonhandASI (bom.M_ProductBOM_ID, AttributeSetInstance_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' ;

View File

@ -1,30 +1,4 @@
/*
*This file is part of Adempiere ERP Bazaar
*http://www.adempiere.org
*Copyright (C) 2006-2008 victor.perez@e-evolution.com, e-Evolution
*This program is free software; you can redistribute it and/or
*modify it under the terms of the GNU General Public License
*as published by the Free Software Foundation; either version 2
*of the License, or (at your option) any later version.
*
*This program is distributed in the hope that it will be useful,
*but WITHOUT ANY WARRANTY; without even the implied warranty of
*MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*GNU General Public License for more details.
*
*You should have received a copy of the GNU General Public License
*along with this program; if not, write to the Free Software
*Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.of
* Return quantity ordered for BOM
*/
CREATE OR REPLACE FUNCTION Bomqtyordered
(
p_Product_ID numeric,
p_Warehouse_ID numeric,
p_Locator_ID numeric -- Only used, if warehouse is null
)
RETURNS numeric
AS
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;
@ -50,7 +24,7 @@ BEGIN
IF (v_Warehouse_ID IS NULL) THEN
RETURN 0;
END IF;
-- DBMS_OUTPUT.PUT_LINE('Warehouse=' || v_Warehouse_ID);
-- DBMS_OUTPUT.PUT_LINE(''Warehouse='' || v_Warehouse_ID);
-- Check, if product exists and if it is stocked
BEGIN
@ -81,14 +55,15 @@ BEGIN
END IF;
-- Go though BOM
-- DBMS_OUTPUT.PUT_LINE('BOM');
-- DBMS_OUTPUT.PUT_LINE(''BOM'');
FOR bom IN
-- Get BOM Product info
SELECT bl.M_Product_ID AS M_ProductBOM_ID, CASE WHEN bl.IsQtyPercentage = 'N' THEN bl.QtyBOM ELSE bl.QtyBatch / 100 END AS BomQty , p.IsBOM , p.IsStocked, p.ProductType
FROM PP_PRODUCT_BOM b
INNER JOIN M_PRODUCT p ON (p.M_Product_ID=b.M_Product_ID)
INNER JOIN PP_PRODUCT_BOMLINE bl ON (bl.PP_Product_BOM_ID=b.PP_Product_BOM_ID)
WHERE b.M_Product_ID = p_Product_ID
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 p.IsBOM='Y'
AND p.IsVerified='Y'
LOOP
-- Stocked Items "leaf node"
IF (bom.ProductType = 'I' AND bom.IsStocked = 'Y') THEN
@ -139,4 +114,6 @@ BEGIN
RETURN 0;
END;
$BODY$
LANGUAGE 'plpgsql' ;
LANGUAGE 'plpgsql'
;

View File

@ -1,144 +0,0 @@
/*
*This file is part of Adempiere ERP Bazaar
*http://www.adempiere.org
*Copyright (C) 2006-2008 victor.perez@e-evolution.com, e-Evolution
*This program is free software; you can redistribute it and/or
*modify it under the terms of the GNU General Public License
*as published by the Free Software Foundation; either version 2
*of the License, or (at your option) any later version.
*
*This program is distributed in the hope that it will be useful,
*but WITHOUT ANY WARRANTY; without even the implied warranty of
*MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*GNU General Public License for more details.
*
*You should have received a copy of the GNU General Public License
*along with this program; if not, write to the Free Software
*Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.of
* Return quantity ordered for BOM ASI
*/
CREATE OR REPLACE FUNCTION BomqtyorderedASI
(
p_Product_ID numeric,
AttributeSetInstance_ID numeric,
p_Warehouse_ID numeric,
p_Locator_ID numeric -- Only used, if warehouse is null
)
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;
-- 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 COALESCE(SUM(QtyOrdered), 0)
INTO v_ProductQty
FROM M_STORAGE s
WHERE M_Product_ID=p_Product_ID
AND EXISTS (SELECT * FROM M_LOCATOR l WHERE s.M_Locator_ID=l.M_Locator_ID
AND l.M_Warehouse_ID=v_Warehouse_ID)
AND (s.M_AttributeSetInstance_ID = AttributeSetInstance_ID OR COALESCE(AttributeSetInstance_ID,0) = 0);
--
RETURN v_ProductQty;
END IF;
-- Go though BOM
-- DBMS_OUTPUT.PUT_LINE('BOM');
FOR bom IN
-- Get BOM Product info
SELECT bl.M_Product_ID AS M_ProductBOM_ID, CASE WHEN bl.IsQtyPercentage = 'N' THEN bl.QtyBOM ELSE bl.QtyBatch / 100 END AS BomQty , p.IsBOM , p.IsStocked, p.ProductType
FROM PP_PRODUCT_BOM b
INNER JOIN M_PRODUCT p ON (p.M_Product_ID=b.M_Product_ID)
INNER JOIN PP_PRODUCT_BOMLINE bl ON (bl.PP_Product_BOM_ID=b.PP_Product_BOM_ID)
WHERE b.M_Product_ID = p_Product_ID
LOOP
-- Stocked Items "leaf node"
IF (bom.ProductType = 'I' AND bom.IsStocked = 'Y') THEN
-- Get ProductQty
SELECT COALESCE(SUM(QtyOrdered), 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=v_Warehouse_ID)
AND (s.M_AttributeSetInstance_ID = AttributeSetInstance_ID OR COALESCE(AttributeSetInstance_ID,0) = 0);
-- 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 := BomqtyorderedASI (bom.M_ProductBOM_ID, AttributeSetInstance_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' ;

View File

@ -1,30 +1,4 @@
/*
*This file is part of Adempiere ERP Bazaar
*http://www.adempiere.org
*Copyright (C) 2006-2008 victor.perez@e-evolution.com, e-Evolution
*This program is free software; you can redistribute it and/or
*modify it under the terms of the GNU General Public License
*as published by the Free Software Foundation; either version 2
*of the License, or (at your option) any later version.
*
*This program is distributed in the hope that it will be useful,
*but WITHOUT ANY WARRANTY; without even the implied warranty of
*MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*GNU General Public License for more details.
*
*You should have received a copy of the GNU General Public License
*along with this program; if not, write to the Free Software
*Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.of
* Return quantity reserved for BOM
*/
CREATE OR REPLACE FUNCTION Bomqtyreserved
(
p_Product_ID numeric,
p_Warehouse_ID numeric,
p_Locator_ID numeric -- Only used, if warehouse is null
)
RETURNS numeric
AS
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;
@ -50,7 +24,7 @@ BEGIN
IF (v_Warehouse_ID IS NULL) THEN
RETURN 0;
END IF;
-- DBMS_OUTPUT.PUT_LINE('Warehouse=' || v_Warehouse_ID);
-- DBMS_OUTPUT.PUT_LINE(''Warehouse='' || v_Warehouse_ID);
-- Check, if product exists and if it is stocked
BEGIN
@ -81,14 +55,15 @@ BEGIN
END IF;
-- Go though BOM
-- DBMS_OUTPUT.PUT_LINE('BOM');
-- DBMS_OUTPUT.PUT_LINE(''BOM'');
FOR bom IN
-- Get BOM Product info
SELECT bl.M_Product_ID AS M_ProductBOM_ID, CASE WHEN bl.IsQtyPercentage = 'N' THEN bl.QtyBOM ELSE bl.QtyBatch / 100 END AS BomQty , p.IsBOM , p.IsStocked, p.ProductType
FROM PP_PRODUCT_BOM b
INNER JOIN M_PRODUCT p ON (p.M_Product_ID=b.M_Product_ID)
INNER JOIN PP_PRODUCT_BOMLINE bl ON (bl.PP_Product_BOM_ID=b.PP_Product_BOM_ID)
WHERE b.M_Product_ID = p_Product_ID
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 p.IsBOM='Y'
AND p.IsVerified='Y'
LOOP
-- Stocked Items "leaf node"
IF (bom.ProductType = 'I' AND bom.IsStocked = 'Y') THEN
@ -137,4 +112,6 @@ BEGIN
RETURN 0;
END;
$BODY$
LANGUAGE 'plpgsql' ;
LANGUAGE 'plpgsql'
;

View File

@ -1,144 +0,0 @@
/*
*This file is part of Adempiere ERP Bazaar
*http://www.adempiere.org
*Copyright (C) 2006-2008 victor.perez@e-evolution.com, e-Evolution
*This program is free software; you can redistribute it and/or
*modify it under the terms of the GNU General Public License
*as published by the Free Software Foundation; either version 2
*of the License, or (at your option) any later version.
*
*This program is distributed in the hope that it will be useful,
*but WITHOUT ANY WARRANTY; without even the implied warranty of
*MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*GNU General Public License for more details.
*
*You should have received a copy of the GNU General Public License
*along with this program; if not, write to the Free Software
*Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.of
* Return quantity reserved for BOM ASI
*/
CREATE OR REPLACE FUNCTION BomqtyreservedASI
(
p_Product_ID numeric,
AttributeSetInstance_ID numeric,
p_Warehouse_ID numeric,
p_Locator_ID numeric -- Only used, if warehouse is null
)
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;
-- 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 COALESCE(SUM(QtyReserved), 0)
INTO v_ProductQty
FROM M_STORAGE s
WHERE M_Product_ID=p_Product_ID
AND EXISTS (SELECT * FROM M_LOCATOR l WHERE s.M_Locator_ID=l.M_Locator_ID
AND l.M_Warehouse_ID=v_Warehouse_ID)
AND (s.M_AttributeSetInstance_ID = AttributeSetInstance_ID OR COALESCE(AttributeSetInstance_ID,0) = 0);
--
RETURN v_ProductQty;
END IF;
-- Go though BOM
-- DBMS_OUTPUT.PUT_LINE('BOM');
FOR bom IN
--Get BOM Product info
SELECT bl.M_Product_ID AS M_ProductBOM_ID, CASE WHEN bl.IsQtyPercentage = 'N' THEN bl.QtyBOM ELSE bl.QtyBatch / 100 END AS BomQty , p.IsBOM , p.IsStocked, p.ProductType
FROM PP_PRODUCT_BOM b
INNER JOIN M_PRODUCT p ON (p.M_Product_ID=b.M_Product_ID)
INNER JOIN PP_PRODUCT_BOMLINE bl ON (bl.PP_Product_BOM_ID=b.PP_Product_BOM_ID)
WHERE b.M_Product_ID = p_Product_ID
LOOP
-- Stocked Items "leaf node"
IF (bom.ProductType = 'I' AND bom.IsStocked = 'Y') THEN
-- Get ProductQty
SELECT COALESCE(SUM(QtyReserved), 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=v_Warehouse_ID)
AND (s.M_AttributeSetInstance_ID = AttributeSetInstance_ID OR COALESCE(AttributeSetInstance_ID,0) = 0);
-- 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 := BomqtyreservedASI (bom.M_ProductBOM_ID, AttributeSetInstance_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' ;

View File

@ -0,0 +1,625 @@
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 p.IsBOM='Y'
AND p.IsVerified='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 p.IsBOM='Y'
AND p.IsVerified='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 p.IsBOM='Y'
AND p.IsVerified='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 p.IsBOM='Y'
AND p.IsVerified='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 p.IsBOM='Y'
AND p.IsVerified='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(QtyOrdered), 0)
INTO v_ProductQty
FROM M_STORAGE s
WHERE M_Product_ID=p_Product_ID
AND EXISTS (SELECT * FROM M_LOCATOR l WHERE s.M_Locator_ID=l.M_Locator_ID
AND l.M_Warehouse_ID=v_Warehouse_ID);
--
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(QtyOrdered), 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=v_Warehouse_ID);
-- 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 p.IsBOM='Y'
AND p.IsVerified='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(QtyReserved), 0)
INTO v_ProductQty
FROM M_STORAGE s
WHERE M_Product_ID=p_Product_ID
AND EXISTS (SELECT * FROM M_LOCATOR l WHERE s.M_Locator_ID=l.M_Locator_ID
AND l.M_Warehouse_ID=v_Warehouse_ID);
--
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(QtyReserved), 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=v_Warehouse_ID);
-- 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;
/
CREATE OR REPLACE FUNCTION BOMQTYAVAILABLE
(
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 available for BOM
*/
AS
BEGIN
RETURN bomQtyOnHand(Product_ID, Warehouse_ID, Locator_ID)
- bomQtyReserved(Product_ID, Warehouse_ID, Locator_ID);
END bomQtyAvailable;
/
DROP FUNCTION BOMQTYAVAILABLEASI
;
DROP FUNCTION BOMQTYONHANDASI
;
DROP FUNCTION BOMQTYORDEREDASI
;
DROP FUNCTION BOMQTYRESERVEDASI
;
-- Sep 19, 2013 8:34:08 PM COT
-- IDEMPIERE-1370 Circular Reference in Product Info when a bom product is referenced to itself and its price-lists prices are set to zero
UPDATE AD_Process SET IsActive='N',Updated=TO_DATE('2013-09-19 20:34:08','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Process_ID=346
;
-- Sep 19, 2013 8:34:08 PM COT
UPDATE AD_Menu SET Name='Verify BOMs', Description='Verify BOM Structures', IsActive='N',Updated=TO_DATE('2013-09-19 20:34:08','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Menu_ID=585
;
-- Sep 19, 2013 8:36:42 PM COT
UPDATE AD_Process_Para SET IsMandatory='Y', DefaultValue='Y',Updated=TO_DATE('2013-09-19 20:36:42','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Process_Para_ID=53463
;
-- Sep 19, 2013 8:55:22 PM COT
UPDATE AD_Field SET IsReadOnly='Y',Updated=TO_DATE('2013-09-19 20:55:22','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=3746
;
-- Sep 19, 2013 8:55:55 PM COT
UPDATE AD_Field SET IsReadOnly='Y',Updated=TO_DATE('2013-09-19 20:55:55','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=59805
;
-- Sep 19, 2013 8:59:20 PM COT
UPDATE AD_Process_Para SET DefaultValue='@M_Product_ID@',Updated=TO_DATE('2013-09-19 20:59:20','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Process_Para_ID=53461
;
-- Sep 19, 2013 8:59:36 PM COT
UPDATE AD_Process_Para SET ReadOnlyLogic='@M_Product_ID@>0',Updated=TO_DATE('2013-09-19 20:59:36','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Process_Para_ID=53461
;
-- Sep 19, 2013 8:59:50 PM COT
UPDATE AD_Process_Para SET DisplayLogic='@M_Product_ID@=0',Updated=TO_DATE('2013-09-19 20:59:50','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Process_Para_ID=53462
;
-- Sep 19, 2013 9:12:59 PM COT
UPDATE AD_Process_Para SET DisplayLogic='@M_Product_ID@=0',Updated=TO_DATE('2013-09-19 21:12:59','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Process_Para_ID=53463
;
-- Sep 19, 2013 9:21:47 PM COT
UPDATE M_Product SET IsVerified='Y',Updated=TO_DATE('2013-09-19 21:21:47','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE M_Product_ID=133
;
-- Sep 19, 2013 9:21:47 PM COT
UPDATE M_Product SET IsVerified='Y',Updated=TO_DATE('2013-09-19 21:21:47','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE M_Product_ID=145
;
-- Sep 19, 2013 9:22:10 PM COT
UPDATE M_Product SET IsVerified='Y',Updated=TO_DATE('2013-09-19 21:22:10','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE M_Product_ID=50001
;
-- Sep 19, 2013 9:44:39 PM COT
UPDATE M_Product SET IsVerified='Y',Updated=TO_DATE('2013-09-19 21:44:39','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE M_Product_ID=50000
;
SELECT register_migration_script('201309192024_IDEMPIERE-1370.sql') FROM dual
;

View File

@ -0,0 +1,539 @@
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 p.IsBOM='Y'
AND p.IsVerified='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'
;
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 p.IsBOM='Y'
AND p.IsVerified='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'
;
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 p.IsBOM='Y'
AND p.IsVerified='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'
;
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;
-- DBMS_OUTPUT.PUT_LINE(''Warehouse='' || myWarehouse_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=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);
--
-- 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 p.IsBOM='Y'
AND p.IsVerified='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'
;
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;
-- 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 COALESCE(SUM(QtyOrdered), 0)
INTO v_ProductQty
FROM M_STORAGE s
WHERE M_Product_ID=p_Product_ID
AND EXISTS (SELECT * FROM M_LOCATOR l WHERE s.M_Locator_ID=l.M_Locator_ID
AND l.M_Warehouse_ID=v_Warehouse_ID);
--
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=p_Product_ID
AND p.IsBOM='Y'
AND p.IsVerified='Y'
LOOP
-- Stocked Items "leaf node"
IF (bom.ProductType = 'I' AND bom.IsStocked = 'Y') THEN
-- Get ProductQty
SELECT COALESCE(SUM(QtyOrdered), 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=v_Warehouse_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 := 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'
;
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;
-- 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 COALESCE(SUM(QtyReserved), 0)
INTO v_ProductQty
FROM M_STORAGE s
WHERE M_Product_ID=p_Product_ID
AND EXISTS (SELECT * FROM M_LOCATOR l WHERE s.M_Locator_ID=l.M_Locator_ID
AND l.M_Warehouse_ID=v_Warehouse_ID);
--
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=p_Product_ID
AND p.IsBOM='Y'
AND p.IsVerified='Y'
LOOP
-- Stocked Items "leaf node"
IF (bom.ProductType = 'I' AND bom.IsStocked = 'Y') THEN
-- Get ProductQty
SELECT COALESCE(SUM(QtyReserved), 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=v_Warehouse_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 := 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'
;
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'
;
DROP FUNCTION bomqtyavailable (in product_id numeric, in attributesetinstance_id numeric, in warehouse_id numeric, in locator_id numeric)
;
DROP FUNCTION bomqtyavailableasi (in product_id numeric, in attributesetinstance_id numeric, in warehouse_id numeric, in locator_id numeric)
;
DROP FUNCTION bomqtyonhandasi (in product_id numeric, in attributesetinstance_id numeric, in warehouse_id numeric, in locator_id numeric)
;
DROP FUNCTION bomqtyorderedasi (in p_product_id numeric, in attributesetinstance_id numeric, in p_warehouse_id numeric, in p_locator_id numeric)
;
DROP FUNCTION bomqtyreservedasi (in p_product_id numeric, in attributesetinstance_id numeric, in p_warehouse_id numeric, in p_locator_id numeric)
;
-- Sep 19, 2013 8:34:08 PM COT
-- IDEMPIERE-1370 Circular Reference in Product Info when a bom product is referenced to itself and its price-lists prices are set to zero
UPDATE AD_Process SET IsActive='N',Updated=TO_TIMESTAMP('2013-09-19 20:34:08','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Process_ID=346
;
-- Sep 19, 2013 8:34:08 PM COT
UPDATE AD_Menu SET Name='Verify BOMs', Description='Verify BOM Structures', IsActive='N',Updated=TO_TIMESTAMP('2013-09-19 20:34:08','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Menu_ID=585
;
-- Sep 19, 2013 8:36:42 PM COT
UPDATE AD_Process_Para SET IsMandatory='Y', DefaultValue='Y',Updated=TO_TIMESTAMP('2013-09-19 20:36:42','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Process_Para_ID=53463
;
-- Sep 19, 2013 8:55:22 PM COT
UPDATE AD_Field SET IsReadOnly='Y',Updated=TO_TIMESTAMP('2013-09-19 20:55:22','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=3746
;
-- Sep 19, 2013 8:55:55 PM COT
UPDATE AD_Field SET IsReadOnly='Y',Updated=TO_TIMESTAMP('2013-09-19 20:55:55','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=59805
;
-- Sep 19, 2013 8:59:20 PM COT
UPDATE AD_Process_Para SET DefaultValue='@M_Product_ID@',Updated=TO_TIMESTAMP('2013-09-19 20:59:20','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Process_Para_ID=53461
;
-- Sep 19, 2013 8:59:36 PM COT
UPDATE AD_Process_Para SET ReadOnlyLogic='@M_Product_ID@>0',Updated=TO_TIMESTAMP('2013-09-19 20:59:36','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Process_Para_ID=53461
;
-- Sep 19, 2013 8:59:50 PM COT
UPDATE AD_Process_Para SET DisplayLogic='@M_Product_ID@=0',Updated=TO_TIMESTAMP('2013-09-19 20:59:50','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Process_Para_ID=53462
;
-- Sep 19, 2013 9:12:59 PM COT
UPDATE AD_Process_Para SET DisplayLogic='@M_Product_ID@=0',Updated=TO_TIMESTAMP('2013-09-19 21:12:59','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Process_Para_ID=53463
;
-- Sep 19, 2013 9:21:47 PM COT
UPDATE M_Product SET IsVerified='Y',Updated=TO_TIMESTAMP('2013-09-19 21:21:47','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE M_Product_ID=133
;
-- Sep 19, 2013 9:21:47 PM COT
UPDATE M_Product SET IsVerified='Y',Updated=TO_TIMESTAMP('2013-09-19 21:21:47','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE M_Product_ID=145
;
-- Sep 19, 2013 9:22:10 PM COT
UPDATE M_Product SET IsVerified='Y',Updated=TO_TIMESTAMP('2013-09-19 21:22:10','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE M_Product_ID=50001
;
-- Sep 19, 2013 9:44:39 PM COT
UPDATE M_Product SET IsVerified='Y',Updated=TO_TIMESTAMP('2013-09-19 21:44:39','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE M_Product_ID=50000
;
SELECT register_migration_script('201309192024_IDEMPIERE-1370.sql') FROM dual
;

View File

@ -39,6 +39,8 @@ public class BOMVerify extends SvrProcess
/** Re-Validate */
private boolean p_IsReValidate = false;
private boolean p_fromButton = false;
/** List of Products */
private ArrayList<MProduct> foundproducts = new ArrayList<MProduct>();
private ArrayList<MProduct> validproducts = new ArrayList<MProduct>();
@ -68,6 +70,7 @@ public class BOMVerify extends SvrProcess
}
if ( p_M_Product_ID == 0 )
p_M_Product_ID = getRecord_ID();
p_fromButton = (getRecord_ID() > 0);
} // prepare
/**
@ -155,9 +158,12 @@ public class BOMVerify extends SvrProcess
MProductBOM[] productsBOMs = MProductBOM.getBOMLines(product);
boolean containsinvalid = false;
boolean invalid = false;
for (int i = 0; i < productsBOMs.length; i++)
int lines = 0;
for (MProductBOM productsBOM : productsBOMs)
{
MProductBOM productsBOM = productsBOMs[i];
if (! productsBOM.isActive())
continue;
lines++;
MProduct pp = new MProduct(getCtx(), productsBOM.getM_ProductBOM_ID(), get_TrxName());
if (!pp.isBOM()) {
if (log.isLoggable(Level.FINER)) log.finer(pp.getName());
@ -173,6 +179,9 @@ public class BOMVerify extends SvrProcess
else if (foundproducts.contains(pp))
{
invalid = true;
if (p_fromButton)
addLog(0, null, null, product.getValue() + " recursively contains " + pp.getValue());
else
addLog(0, null, null, product.getValue() + " recursively contains " + pp.getValue(), MProduct.Table_ID, product.getM_Product_ID());
}
else
@ -184,9 +193,14 @@ public class BOMVerify extends SvrProcess
}
}
}
if (lines == 0) {
invalid = true;
if (p_fromButton)
addLog(0, null, null, product.getValue() + " does not have lines");
else
addLog(0, null, null, product.getValue() + " does not have lines", MProduct.Table_ID, product.getM_Product_ID());
}
checkedproducts.add(product);

View File

@ -1,193 +0,0 @@
/******************************************************************************
* Product: Adempiere ERP & CRM Smart Business Solution *
* Copyright (C) 1999-2006 ComPiere, Inc. All Rights Reserved. *
* This program is free software; you can redistribute it and/or modify it *
* under the terms version 2 of the GNU General Public License as published *
* by the Free Software Foundation. This program is distributed in the hope *
* that it will be useful, but WITHOUT ANY WARRANTY; without even the implied *
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* See the GNU General Public License for more details. *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
* For the text or an alternative of this public license, you may reach us *
* ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA *
* or via info@compiere.org or http://www.compiere.org/license.html *
* Portions created by Carlos Ruiz are Copyright (C) 2005 QSS Ltda.
* Contributor(s): Carlos Ruiz (globalqss)
*****************************************************************************/
package org.compiere.process;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.logging.Level;
import org.compiere.model.X_M_Product;
import org.compiere.util.AdempiereUserError;
import org.compiere.util.CLogger;
import org.compiere.util.DB;
import org.compiere.util.Env;
import org.compiere.util.ValueNamePair;
/**
* Title: Check BOM Structure (free of cycles)
* Description:
* Tree cannot contain BOMs which are already referenced
*
* @author Carlos Ruiz (globalqss)
* @version $Id: M_Product_BOM_Check.java,v 1.0 2005/09/17 13:32:00 globalqss Exp $
* @author Carlos Ruiz (globalqss)
* Make T_Selection tables permanent
*/
public class M_Product_BOM_Check extends SvrProcess
{
/** The Record */
private int p_Record_ID = 0;
private int m_AD_PInstance_ID = 0;
/**
* Prepare - e.g., get Parameters.
*/
protected void prepare()
{
ProcessInfoParameter[] para = getParameter();
for (int i = 0; i < para.length; i++)
{
String name = para[i].getParameterName();
if (para[i].getParameter() == null)
;
else
log.log(Level.SEVERE, "Unknown Parameter: " + name);
}
p_Record_ID = getRecord_ID();
m_AD_PInstance_ID = getAD_PInstance_ID();
} // prepare
/**
* Process
* @return message
* @throws Exception
*/
protected String doIt() throws Exception
{
StringBuilder sql1 = null;
int no = 0;
log.info("Check BOM Structure");
// Record ID is M_Product_ID of product to be tested
X_M_Product xp = new X_M_Product(Env.getCtx(), p_Record_ID, get_TrxName());
if (! xp.isBOM()) {
log.info("NOT BOM Product");
// No BOM - should not happen, but no problem
xp.setIsVerified(true);
xp.saveEx(get_TrxName());
return "OK";
}
// Table to put all BOMs - duplicate will cause exception
sql1 = new StringBuilder("DELETE FROM T_Selection2 WHERE Query_ID = 0 AND AD_PInstance_ID=").append(m_AD_PInstance_ID);
no = DB.executeUpdate(sql1.toString(), get_TrxName());
sql1 = new StringBuilder("INSERT INTO T_Selection2 (AD_PInstance_ID, Query_ID, T_Selection_ID) VALUES (")
.append(m_AD_PInstance_ID)
.append(", 0, ")
.append(p_Record_ID).append(")");
no = DB.executeUpdate(sql1.toString(), get_TrxName());
// Table of root modes
sql1 = new StringBuilder("DELETE FROM T_Selection WHERE AD_PInstance_ID=").append(m_AD_PInstance_ID);
no = DB.executeUpdate(sql1.toString(), get_TrxName());
sql1 = new StringBuilder("INSERT INTO T_Selection (AD_PInstance_ID, T_Selection_ID) VALUES (")
.append(m_AD_PInstance_ID)
.append(", ")
.append(p_Record_ID).append(")");
no = DB.executeUpdate(sql1.toString(), get_TrxName());
while (true) {
// Get count remaining on t_selection
int countno = 0;
PreparedStatement pstmt = null;
ResultSet rs = null;
try
{
StringBuilder dbpst = new StringBuilder("SELECT COUNT(*) FROM T_Selection WHERE AD_PInstance_ID=").append(m_AD_PInstance_ID);
pstmt = DB.prepareStatement(dbpst.toString(), get_TrxName());
rs = pstmt.executeQuery();
if (rs.next())
countno = rs.getInt(1);
}
catch (SQLException e)
{
throw new Exception ("count t_selection", e);
}
finally
{
DB.close(rs, pstmt);
rs = null;
pstmt = null;
}
if (log.isLoggable(Level.FINE)) log.fine("Count T_Selection =" + countno);
if (countno == 0)
break;
try
{
// if any command fails (no==-1) break and inform failure
// Insert BOM Nodes into "All" table
sql1 = new StringBuilder("INSERT INTO T_Selection2 (AD_PInstance_ID, Query_ID, T_Selection_ID) ")
.append("SELECT ").append(m_AD_PInstance_ID).append(", 0, p.M_Product_ID FROM M_Product p WHERE IsBOM='Y' AND EXISTS ")
//+ "(SELECT * FROM M_Product_BOM b WHERE p.M_Product_ID=b.M_ProductBOM_ID AND b.M_Product_ID IN "
.append("(SELECT * FROM PP_Product_BOM b WHERE p.M_Product_ID=b.M_Product_ID AND b.M_Product_ID IN ")
.append("(SELECT T_Selection_ID FROM T_Selection WHERE AD_PInstance_ID=").append(m_AD_PInstance_ID).append("))");
no = DB.executeUpdate(sql1.toString(), get_TrxName());
if (no == -1) raiseError("InsertingRoot:ERROR", sql1.toString());
// Insert BOM Nodes into temporary table
sql1 = new StringBuilder("DELETE FROM T_Selection2 WHERE Query_ID = 1 AND AD_PInstance_ID=").append(m_AD_PInstance_ID);
no = DB.executeUpdate(sql1.toString(), get_TrxName());
if (no == -1) raiseError("InsertingRoot:ERROR", sql1.toString());
sql1 = new StringBuilder("INSERT INTO T_Selection2 (AD_PInstance_ID, Query_ID, T_Selection_ID) ")
.append("SELECT ").append(m_AD_PInstance_ID).append(", 1, p.M_Product_ID FROM M_Product p WHERE IsBOM='Y' AND EXISTS ")
//+ "(SELECT * FROM M_Product_BOM b WHERE p.M_Product_ID=b.M_ProductBOM_ID AND b.M_Product_ID IN "
.append("(SELECT * FROM PP_Product_BOM b WHERE p.M_Product_ID=b.M_Product_ID AND b.M_Product_ID IN ")
.append("(SELECT T_Selection_ID FROM T_Selection WHERE AD_PInstance_ID=").append(m_AD_PInstance_ID).append("))");
no = DB.executeUpdate(sql1.toString(), get_TrxName());
if (no == -1) raiseError("InsertingRoot:ERROR", sql1.toString());
// Copy into root table
sql1 = new StringBuilder("DELETE FROM T_Selection WHERE AD_PInstance_ID=").append(m_AD_PInstance_ID);
no = DB.executeUpdate(sql1.toString(), get_TrxName());
if (no == -1) raiseError("InsertingRoot:ERROR", sql1.toString());
sql1 = new StringBuilder("INSERT INTO T_Selection (AD_PInstance_ID, T_Selection_ID) ")
.append("SELECT ").append(m_AD_PInstance_ID).append(", T_Selection_ID ")
.append("FROM T_Selection2 WHERE Query_ID = 1 AND AD_PInstance_ID=").append(m_AD_PInstance_ID);
no = DB.executeUpdate(sql1.toString(), get_TrxName());
if (no == -1) raiseError("InsertingRoot:ERROR", sql1.toString());
}
catch (Exception e)
{
throw new Exception ("root insert", e);
}
}
// Finish process
xp.setIsVerified(true);
xp.saveEx(get_TrxName());
return "OK";
} // doIt
private void raiseError(String string, String sql) throws Exception {
DB.rollback(false, get_TrxName());
StringBuilder msg = new StringBuilder(string);
ValueNamePair pp = CLogger.retrieveError();
if (pp != null)
msg = new StringBuilder(pp.getName()).append(" - ");
msg.append(sql);
throw new AdempiereUserError (msg.toString());
}
} // M_Product_BOM_Check

View File

@ -21,6 +21,7 @@ import java.util.List;
import java.util.Properties;
import org.compiere.util.CLogger;
import org.compiere.util.DB;
import org.compiere.util.Env;
/**
@ -37,8 +38,7 @@ public class MProductBOM extends X_M_Product_BOM
/**
*
*/
private static final long serialVersionUID = 6029128559467083124L;
private static final long serialVersionUID = 2615029184168566124L;
/**
* Get BOM Lines for Product
@ -96,7 +96,7 @@ public class MProductBOM extends X_M_Product_BOM
} // MProductBOM
/**
* Load Construvtor
* Load Constructor
* @param ctx context
* @param rs result set
* @param trxName transaction
@ -159,22 +159,51 @@ public class MProductBOM extends X_M_Product_BOM
{
if (!success)
return success;
// Product Line was changed
if (newRecord || is_ValueChanged("M_ProductBOM_ID"))
{
// Invalidate BOM
MProduct product = new MProduct (getCtx(), getM_Product_ID(), get_TrxName());
if (get_TrxName() != null)
product.load(get_TrxName());
if (product.isVerified())
{
if ( newRecord
|| is_ValueChanged("M_ProductBOM_ID") // Product Line was changed
|| (is_ValueChanged("IsActive") && isActive())) // line was activated
{
// Invalidate BOM
product.setIsVerified(false);
product.saveEx(get_TrxName());
}
// Invalidate Products where BOM is used
if (product.isVerified() && is_ValueChanged("IsActive") && !isActive()) { // line was inactivated
if (! hasActiveComponents(getM_Product_ID())) {
product.setIsVerified(false);
product.saveEx(get_TrxName());
}
}
}
return success;
} // afterSave
@Override
protected boolean afterDelete(boolean success) {
if (!success)
return success;
MProduct product = new MProduct (getCtx(), getM_Product_ID(), get_TrxName());
if (get_TrxName() != null)
product.load(get_TrxName());
if (product.isVerified())
{
if (! hasActiveComponents(getM_Product_ID())) {
product.setIsVerified(false);
product.saveEx(get_TrxName());
}
}
return success;
}
private boolean hasActiveComponents(int productID) {
int cnt = DB.getSQLValue(get_TrxName(),
"SELECT COUNT(*) FROM M_Product_BOM WHERE IsActive='Y' AND M_Product_ID=? AND M_Product_BOM_ID!=?",
productID, getM_Product_BOM_ID());
return cnt > 0;
}
} // MProductBOM