diff --git a/db/ddlutils/oracle/functions/BOM_Qty_Ordered.sql b/db/ddlutils/oracle/functions/BOM_Qty_Ordered.sql index c34b6434be..c0eb2b93ea 100644 --- a/db/ddlutils/oracle/functions/BOM_Qty_Ordered.sql +++ b/db/ddlutils/oracle/functions/BOM_Qty_Ordered.sql @@ -65,12 +65,13 @@ BEGIN -- Stocked item ELSIF (v_IsStocked='Y') THEN -- Get ProductQty - SELECT NVL(SUM(QtyOrdered), 0) + SELECT NVL(SUM(Qty), 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); + FROM M_StorageReservation + WHERE M_Product_ID=p_Product_ID + AND M_Warehouse_ID=v_Warehouse_ID + AND IsSOTrx='N' + AND IsActive='Y'; -- RETURN v_ProductQty; END IF; @@ -81,12 +82,13 @@ BEGIN -- Stocked Items "leaf node" IF (bom.ProductType = 'I' AND bom.IsStocked = 'Y') THEN -- Get ProductQty - SELECT NVL(SUM(QtyOrdered), 0) + SELECT NVL(SUM(Qty), 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); + FROM M_StorageReservation + WHERE M_Product_ID=p_Product_ID + AND M_Warehouse_ID=v_Warehouse_ID + AND IsSOTrx='N' + AND IsActive='Y'; -- Get Rounding Precision SELECT NVL(MAX(u.StdPrecision), 0) INTO v_StdPrecision diff --git a/db/ddlutils/oracle/functions/BOM_Qty_Reserved.sql b/db/ddlutils/oracle/functions/BOM_Qty_Reserved.sql index 6921fc37ac..3cb43168ac 100644 --- a/db/ddlutils/oracle/functions/BOM_Qty_Reserved.sql +++ b/db/ddlutils/oracle/functions/BOM_Qty_Reserved.sql @@ -65,12 +65,13 @@ BEGIN -- Stocked item ELSIF (v_IsStocked='Y') THEN -- Get ProductQty - SELECT NVL(SUM(QtyReserved), 0) + SELECT NVL(SUM(Qty), 0) INTO v_ProductQty - FROM M_STORAGE s + FROM M_StorageReservation 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 M_Warehouse_ID=v_Warehouse_ID + AND IsSOTrx='Y' + AND IsActive='Y'; -- RETURN v_ProductQty; END IF; @@ -81,12 +82,13 @@ BEGIN -- Stocked Items "leaf node" IF (bom.ProductType = 'I' AND bom.IsStocked = 'Y') THEN -- Get ProductQty - SELECT NVL(SUM(QtyReserved), 0) + SELECT NVL(SUM(Qty), 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); + FROM M_StorageReservation + WHERE M_Product_ID=bom.M_ProductBOM_ID + AND M_Warehouse_ID=v_Warehouse_ID + AND IsSOTrx='Y' + AND IsActive='Y'; -- Get Rounding Precision SELECT NVL(MAX(u.StdPrecision), 0) INTO v_StdPrecision diff --git a/db/ddlutils/postgresql/functions/BOM_Qty_Ordered.sql b/db/ddlutils/postgresql/functions/BOM_Qty_Ordered.sql index d9f8fe49a2..b82e7dc67b 100644 --- a/db/ddlutils/postgresql/functions/BOM_Qty_Ordered.sql +++ b/db/ddlutils/postgresql/functions/BOM_Qty_Ordered.sql @@ -24,7 +24,6 @@ BEGIN 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 @@ -44,18 +43,18 @@ BEGIN -- Stocked item ELSIF (v_IsStocked='Y') THEN -- Get ProductQty - SELECT COALESCE(SUM(QtyOrdered), 0) + SELECT COALESCE(SUM(Qty), 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); + FROM M_StorageReservation + WHERE M_Product_ID=p_Product_ID + AND M_Warehouse_ID=v_Warehouse_ID + AND IsSOTrx='N' + AND IsActive='Y'; -- RETURN v_ProductQty; END IF; -- Go though BOM --- DBMS_OUTPUT.PUT_LINE(''BOM''); FOR bom IN -- Get BOM Product info SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM, p.IsStocked, p.ProductType @@ -68,12 +67,13 @@ BEGIN -- Stocked Items "leaf node" IF (bom.ProductType = 'I' AND bom.IsStocked = 'Y') THEN -- Get ProductQty - SELECT COALESCE(SUM(QtyOrdered), 0) + SELECT COALESCE(SUM(Qty), 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); + FROM M_StorageReservation + WHERE M_Product_ID=p_Product_ID + AND M_Warehouse_ID=v_Warehouse_ID + AND IsSOTrx='N' + AND IsActive='Y'; -- Get Rounding Precision SELECT COALESCE(MAX(u.StdPrecision), 0) INTO v_StdPrecision @@ -114,6 +114,5 @@ BEGIN RETURN 0; END; $BODY$ -LANGUAGE 'plpgsql' -; + LANGUAGE plpgsql VOLATILE; diff --git a/db/ddlutils/postgresql/functions/BOM_Qty_Reserved.sql b/db/ddlutils/postgresql/functions/BOM_Qty_Reserved.sql index c4ea21df31..22775b364b 100644 --- a/db/ddlutils/postgresql/functions/BOM_Qty_Reserved.sql +++ b/db/ddlutils/postgresql/functions/BOM_Qty_Reserved.sql @@ -24,7 +24,6 @@ BEGIN 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 @@ -44,18 +43,18 @@ BEGIN -- Stocked item ELSIF (v_IsStocked='Y') THEN -- Get ProductQty - SELECT COALESCE(SUM(QtyReserved), 0) + SELECT COALESCE(SUM(Qty), 0) INTO v_ProductQty - FROM M_STORAGE s + FROM M_StorageReservation 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 M_Warehouse_ID=v_Warehouse_ID + AND IsSOTrx='Y' + AND IsActive='Y'; -- RETURN v_ProductQty; END IF; -- Go though BOM --- DBMS_OUTPUT.PUT_LINE(''BOM''); FOR bom IN -- Get BOM Product info SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM, p.IsStocked, p.ProductType @@ -68,12 +67,13 @@ BEGIN -- Stocked Items "leaf node" IF (bom.ProductType = 'I' AND bom.IsStocked = 'Y') THEN -- Get ProductQty - SELECT COALESCE(SUM(QtyReserved), 0) + SELECT COALESCE(SUM(Qty), 0) INTO v_ProductQty - FROM M_STORAGE s + FROM M_StorageReservation 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 M_Warehouse_ID =v_Warehouse_ID + AND IsSOTrx='Y' + AND IsActive='Y'; -- Get Rounding Precision SELECT COALESCE(MAX(u.StdPrecision), 0) INTO v_StdPrecision @@ -112,6 +112,6 @@ BEGIN RETURN 0; END; $BODY$ -LANGUAGE 'plpgsql' -; + LANGUAGE plpgsql VOLATILE; + diff --git a/migration/i2.0/oracle/201404290949_Ticket_1003965.sql b/migration/i2.0/oracle/201404290949_Ticket_1003965.sql new file mode 100644 index 0000000000..bd45f413f4 --- /dev/null +++ b/migration/i2.0/oracle/201404290949_Ticket_1003965.sql @@ -0,0 +1,266 @@ +SET SQLBLANKLINES ON +SET DEFINE OFF + +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(Qty), 0) + INTO v_ProductQty + FROM M_StorageReservation + WHERE M_Product_ID=p_Product_ID + AND M_Warehouse_ID=v_Warehouse_ID + AND IsSOTrx='Y' + AND IsActive='Y'; + -- + RETURN v_ProductQty; + END IF; + + -- Go though BOM +-- DBMS_OUTPUT.PUT_LINE('BOM'); + FOR bom IN CUR_BOM LOOP + -- Stocked Items "leaf node" + IF (bom.ProductType = 'I' AND bom.IsStocked = 'Y') THEN + -- Get ProductQty + SELECT NVL(SUM(Qty), 0) + INTO v_ProductQty + FROM M_StorageReservation + WHERE M_Product_ID=bom.M_ProductBOM_ID + AND M_Warehouse_ID=v_Warehouse_ID + AND IsSOTrx='Y' + AND IsActive='Y'; + -- Get Rounding Precision + SELECT NVL(MAX(u.StdPrecision), 0) + INTO v_StdPrecision + FROM C_UOM u, M_PRODUCT p + WHERE u.C_UOM_ID=p.C_UOM_ID AND p.M_Product_ID=bom.M_ProductBOM_ID; + -- How much can we make with this product + v_ProductQty := ROUND (v_ProductQty/bom.BOMQty, v_StdPrecision); + -- How much can we make overall + IF (v_ProductQty < v_Quantity) THEN + v_Quantity := v_ProductQty; + END IF; + -- Another BOM + ELSIF (bom.IsBOM = 'Y') THEN + v_ProductQty := Bomqtyreserved (bom.M_ProductBOM_ID, v_Warehouse_ID, p_Locator_ID); + -- How much can we make overall + IF (v_ProductQty < v_Quantity) THEN + v_Quantity := v_ProductQty; + END IF; + END IF; + END LOOP; -- BOM + + -- Unlimited (e.g. only services) + IF (v_Quantity = 99999) THEN + RETURN 0; + END IF; + + IF (v_Quantity > 0) THEN + -- Get Rounding Precision for Product + SELECT NVL(MAX(u.StdPrecision), 0) + INTO v_StdPrecision + FROM C_UOM u, M_PRODUCT p + WHERE u.C_UOM_ID=p.C_UOM_ID AND p.M_Product_ID=p_Product_ID; + -- + RETURN ROUND (v_Quantity, v_StdPrecision); + END IF; + RETURN 0; +END Bomqtyreserved; + +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(Qty), 0) + INTO v_ProductQty + FROM M_StorageReservation + WHERE M_Product_ID=p_Product_ID + AND M_Warehouse_ID=v_Warehouse_ID + AND IsSOTrx='N' + AND IsActive='Y'; + -- + RETURN v_ProductQty; + END IF; + + -- Go though BOM +-- DBMS_OUTPUT.PUT_LINE('BOM'); + FOR bom IN CUR_BOM LOOP + -- Stocked Items "leaf node" + IF (bom.ProductType = 'I' AND bom.IsStocked = 'Y') THEN + -- Get ProductQty + SELECT NVL(SUM(Qty), 0) + INTO v_ProductQty + FROM M_StorageReservation + WHERE M_Product_ID=p_Product_ID + AND M_Warehouse_ID=v_Warehouse_ID + AND IsSOTrx='N' + AND IsActive='Y'; + -- Get Rounding Precision + SELECT NVL(MAX(u.StdPrecision), 0) + INTO v_StdPrecision + FROM C_UOM u, M_PRODUCT p + WHERE u.C_UOM_ID=p.C_UOM_ID AND p.M_Product_ID=bom.M_ProductBOM_ID; + -- How much can we make with this product + v_ProductQty := ROUND (v_ProductQty/bom.BOMQty, v_StdPrecision); + -- How much can we make overall + IF (v_ProductQty < v_Quantity) THEN + v_Quantity := v_ProductQty; + END IF; + -- Another BOM + ELSIF (bom.IsBOM = 'Y') THEN + v_ProductQty := Bomqtyordered (bom.M_ProductBOM_ID, v_Warehouse_ID, p_Locator_ID); + -- How much can we make overall + IF (v_ProductQty < v_Quantity) THEN + v_Quantity := v_ProductQty; + END IF; + END IF; + END LOOP; -- BOM + + -- Unlimited (e.g. only services) + IF (v_Quantity = 99999) THEN + RETURN 0; + END IF; + + IF (v_Quantity > 0) THEN + -- Get Rounding Precision for Product + SELECT NVL(MAX(u.StdPrecision), 0) + INTO v_StdPrecision + FROM C_UOM u, M_PRODUCT p + WHERE u.C_UOM_ID=p.C_UOM_ID AND p.M_Product_ID=p_Product_ID; + -- + RETURN ROUND (v_Quantity, v_StdPrecision); + END IF; + -- + RETURN 0; +END Bomqtyordered; +/ + +SELECT register_migration_script('201404290949_Ticket_1003965.sql') FROM dual +; + diff --git a/migration/i2.0/postgresql/201404290949_Ticket_1003965.sql b/migration/i2.0/postgresql/201404290949_Ticket_1003965.sql new file mode 100644 index 0000000000..8da5fceaad --- /dev/null +++ b/migration/i2.0/postgresql/201404290949_Ticket_1003965.sql @@ -0,0 +1,237 @@ +CREATE OR REPLACE FUNCTION bomqtyreserved (in p_product_id numeric, in p_warehouse_id numeric, in p_locator_id numeric) RETURNS numeric AS +$BODY$ +DECLARE + v_Warehouse_ID numeric; + v_Quantity numeric := 99999; -- unlimited + v_IsBOM CHAR(1); + v_IsStocked CHAR(1); + v_ProductType CHAR(1); + v_ProductQty numeric; + v_StdPrecision int; + bom record; +BEGIN + -- Check Parameters + v_Warehouse_ID := p_Warehouse_ID; + IF (v_Warehouse_ID IS NULL) THEN + IF (p_Locator_ID IS NULL) THEN + RETURN 0; + ELSE + SELECT MAX(M_Warehouse_ID) INTO v_Warehouse_ID + FROM M_LOCATOR + WHERE M_Locator_ID=p_Locator_ID; + END IF; + END IF; + IF (v_Warehouse_ID IS NULL) THEN + RETURN 0; + END IF; + + -- Check, if product exists and if it is stocked + BEGIN + SELECT IsBOM, ProductType, IsStocked + INTO v_IsBOM, v_ProductType, v_IsStocked + FROM M_PRODUCT + WHERE M_Product_ID=p_Product_ID; + -- + EXCEPTION -- not found + WHEN OTHERS THEN + RETURN 0; + END; + + -- No reservation for non-stocked + IF (v_IsBOM='N' AND (v_ProductType<>'I' OR v_IsStocked='N')) THEN + RETURN 0; + -- Stocked item + ELSIF (v_IsStocked='Y') THEN + -- Get ProductQty + SELECT COALESCE(SUM(Qty), 0) + INTO v_ProductQty + FROM M_StorageReservation + WHERE M_Product_ID=p_Product_ID + AND M_Warehouse_ID=v_Warehouse_ID + AND IsSOTrx='Y' + AND IsActive='Y'; + -- + RETURN v_ProductQty; + END IF; + + -- Go though BOM + FOR bom IN + -- Get BOM Product info + SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM, p.IsStocked, p.ProductType + FROM M_PRODUCT_BOM b, M_PRODUCT p + WHERE b.M_ProductBOM_ID=p.M_Product_ID + AND b.M_Product_ID=p_Product_ID + AND 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(Qty), 0) + INTO v_ProductQty + FROM M_StorageReservation + WHERE M_Product_ID=bom.M_ProductBOM_ID + AND M_Warehouse_ID =v_Warehouse_ID + AND IsSOTrx='Y' + AND IsActive='Y'; + -- Get Rounding Precision + SELECT COALESCE(MAX(u.StdPrecision), 0) + INTO v_StdPrecision + FROM C_UOM u, M_PRODUCT p + WHERE u.C_UOM_ID=p.C_UOM_ID AND p.M_Product_ID=bom.M_ProductBOM_ID; + -- How much can we make with this product + v_ProductQty := ROUND (v_ProductQty/bom.BOMQty, v_StdPrecision); + -- How much can we make overall + IF (v_ProductQty < v_Quantity) THEN + v_Quantity := v_ProductQty; + END IF; + -- Another BOM + ELSIF (bom.IsBOM = 'Y') THEN + v_ProductQty := Bomqtyreserved (bom.M_ProductBOM_ID, v_Warehouse_ID, p_Locator_ID); + -- How much can we make overall + IF (v_ProductQty < v_Quantity) THEN + v_Quantity := v_ProductQty; + END IF; + END IF; + END LOOP; -- BOM + + -- Unlimited (e.g. only services) + IF (v_Quantity = 99999) THEN + RETURN 0; + END IF; + + IF (v_Quantity > 0) THEN + -- Get Rounding Precision for Product + SELECT COALESCE(MAX(u.StdPrecision), 0) + INTO v_StdPrecision + FROM C_UOM u, M_PRODUCT p + WHERE u.C_UOM_ID=p.C_UOM_ID AND p.M_Product_ID=p_Product_ID; + -- + RETURN ROUND (v_Quantity, v_StdPrecision); + END IF; + RETURN 0; +END; +$BODY$ + LANGUAGE plpgsql VOLATILE; + +CREATE OR REPLACE FUNCTION bomqtyordered (in p_product_id numeric, in p_warehouse_id numeric, in p_locator_id numeric) RETURNS numeric AS +$BODY$ +DECLARE + v_Warehouse_ID numeric; + v_Quantity numeric := 99999; -- unlimited + v_IsBOM CHAR(1); + v_IsStocked CHAR(1); + v_ProductType CHAR(1); + v_ProductQty numeric; + v_StdPrecision int; + bom record; +BEGIN + -- Check Parameters + v_Warehouse_ID := p_Warehouse_ID; + IF (v_Warehouse_ID IS NULL) THEN + IF (p_Locator_ID IS NULL) THEN + RETURN 0; + ELSE + SELECT MAX(M_Warehouse_ID) INTO v_Warehouse_ID + FROM M_LOCATOR + WHERE M_Locator_ID=p_Locator_ID; + END IF; + END IF; + IF (v_Warehouse_ID IS NULL) THEN + RETURN 0; + END IF; + + -- Check, if product exists and if it is stocked + BEGIN + SELECT IsBOM, ProductType, IsStocked + INTO v_IsBOM, v_ProductType, v_IsStocked + FROM M_PRODUCT + WHERE M_Product_ID=p_Product_ID; + -- + EXCEPTION -- not found + WHEN OTHERS THEN + RETURN 0; + END; + + -- No reservation for non-stocked + IF (v_IsBOM='N' AND (v_ProductType<>'I' OR v_IsStocked='N')) THEN + RETURN 0; + -- Stocked item + ELSIF (v_IsStocked='Y') THEN + -- Get ProductQty + SELECT COALESCE(SUM(Qty), 0) + INTO v_ProductQty + FROM M_StorageReservation + WHERE M_Product_ID=p_Product_ID + AND M_Warehouse_ID=v_Warehouse_ID + AND IsSOTrx='N' + AND IsActive='Y'; + -- + RETURN v_ProductQty; + END IF; + + -- Go though BOM + FOR bom IN + -- Get BOM Product info + SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM, p.IsStocked, p.ProductType + FROM M_PRODUCT_BOM b, M_PRODUCT p + WHERE b.M_ProductBOM_ID=p.M_Product_ID + AND b.M_Product_ID=p_Product_ID + AND 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(Qty), 0) + INTO v_ProductQty + FROM M_StorageReservation + WHERE M_Product_ID=p_Product_ID + AND M_Warehouse_ID=v_Warehouse_ID + AND IsSOTrx='N' + AND IsActive='Y'; + -- Get Rounding Precision + SELECT COALESCE(MAX(u.StdPrecision), 0) + INTO v_StdPrecision + FROM C_UOM u, M_PRODUCT p + WHERE u.C_UOM_ID=p.C_UOM_ID AND p.M_Product_ID=bom.M_ProductBOM_ID; + -- How much can we make with this product + v_ProductQty := ROUND (v_ProductQty/bom.BOMQty, v_StdPrecision ); + + -- How much can we make overall + IF (v_ProductQty < v_Quantity) THEN + v_Quantity := v_ProductQty; + END IF; + -- Another BOM + ELSIF (bom.IsBOM = 'Y') THEN + v_ProductQty := Bomqtyordered (bom.M_ProductBOM_ID, v_Warehouse_ID, p_Locator_ID); + -- How much can we make overall + IF (v_ProductQty < v_Quantity) THEN + v_Quantity := v_ProductQty; + END IF; + END IF; + END LOOP; -- BOM + + -- Unlimited (e.g. only services) + IF (v_Quantity = 99999) THEN + RETURN 0; + END IF; + + IF (v_Quantity > 0) THEN + -- Get Rounding Precision for Product + SELECT COALESCE(MAX(u.StdPrecision), 0) + INTO v_StdPrecision + FROM C_UOM u, M_PRODUCT p + WHERE u.C_UOM_ID=p.C_UOM_ID AND p.M_Product_ID=p_Product_ID; + -- + RETURN ROUND (v_Quantity, v_StdPrecision ); + END IF; + -- + RETURN 0; +END; +$BODY$ + LANGUAGE plpgsql VOLATILE; + +SELECT register_migration_script('201404290949_Ticket_1003965.sql') FROM dual +; + diff --git a/org.adempiere.base/src/org/compiere/acct/Doc_Invoice.java b/org.adempiere.base/src/org/compiere/acct/Doc_Invoice.java index ef2e6076ed..1f0c6368c5 100644 --- a/org.adempiere.base/src/org/compiere/acct/Doc_Invoice.java +++ b/org.adempiere.base/src/org/compiere/acct/Doc_Invoice.java @@ -869,13 +869,17 @@ public class Doc_Invoice extends Doc BigDecimal qty = allocation.getQty(); if (qty.compareTo(iol.getMovementQty()) != 0) { - amt = amt.multiply(iol.getMovementQty()).divide(qty, BigDecimal.ROUND_HALF_UP); + amt = amt.multiply(iol.getMovementQty()).divide(qty, 12, BigDecimal.ROUND_HALF_UP); } estimatedAmt = estimatedAmt.add(amt); } } } + if (estimatedAmt.scale() > as.getCostingPrecision()) + { + estimatedAmt.setScale(as.getCostingPrecision(), BigDecimal.ROUND_HALF_UP); + } BigDecimal costAdjustmentAmt = allocationAmt; if (estimatedAmt.signum() > 0) { @@ -907,43 +911,46 @@ public class Doc_Invoice extends Doc if (!dr) costAdjustmentAmt = costAdjustmentAmt.negate(); - Trx trx = Trx.get(getTrxName(), false); - Savepoint savepoint = null; boolean zeroQty = false; - try { - savepoint = trx.setSavepoint(null); - BigDecimal costDetailAmt = costAdjustmentAmt; - //convert to accounting schema currency - if (getC_Currency_ID() != as.getC_Currency_ID()) - costDetailAmt = MConversionRate.convert(getCtx(), costDetailAmt, - getC_Currency_ID(), as.getC_Currency_ID(), - getDateAcct(), getC_ConversionType_ID(), - getAD_Client_ID(), getAD_Org_ID()); - if (costDetailAmt.scale() > as.getCostingPrecision()) - costDetailAmt = costDetailAmt.setScale(as.getCostingPrecision(), BigDecimal.ROUND_HALF_UP); - - if (!MCostDetail.createInvoice(as, lca.getAD_Org_ID(), - lca.getM_Product_ID(), lca.getM_AttributeSetInstance_ID(), - C_InvoiceLine_ID, lca.getM_CostElement_ID(), - costDetailAmt, lca.getQty(), - desc, getTrxName())) { - throw new RuntimeException("Failed to create cost detail record."); - } - } catch (SQLException e) { - throw new RuntimeException(e.getLocalizedMessage(), e); - } catch (AverageCostingZeroQtyException e) { - zeroQty = true; + if (costAdjustmentAmt.signum() != 0) + { + Trx trx = Trx.get(getTrxName(), false); + Savepoint savepoint = null; try { - trx.rollback(savepoint); - savepoint = null; - } catch (SQLException e1) { - throw new RuntimeException(e1.getLocalizedMessage(), e1); - } - } finally { - if (savepoint != null) { + savepoint = trx.setSavepoint(null); + BigDecimal costDetailAmt = costAdjustmentAmt; + //convert to accounting schema currency + if (getC_Currency_ID() != as.getC_Currency_ID()) + costDetailAmt = MConversionRate.convert(getCtx(), costDetailAmt, + getC_Currency_ID(), as.getC_Currency_ID(), + getDateAcct(), getC_ConversionType_ID(), + getAD_Client_ID(), getAD_Org_ID()); + if (costDetailAmt.scale() > as.getCostingPrecision()) + costDetailAmt = costDetailAmt.setScale(as.getCostingPrecision(), BigDecimal.ROUND_HALF_UP); + + if (!MCostDetail.createInvoice(as, lca.getAD_Org_ID(), + lca.getM_Product_ID(), lca.getM_AttributeSetInstance_ID(), + C_InvoiceLine_ID, lca.getM_CostElement_ID(), + costDetailAmt, lca.getQty(), + desc, getTrxName())) { + throw new RuntimeException("Failed to create cost detail record."); + } + } catch (SQLException e) { + throw new RuntimeException(e.getLocalizedMessage(), e); + } catch (AverageCostingZeroQtyException e) { + zeroQty = true; try { - trx.releaseSavepoint(savepoint); - } catch (SQLException e) {} + trx.rollback(savepoint); + savepoint = null; + } catch (SQLException e1) { + throw new RuntimeException(e1.getLocalizedMessage(), e1); + } + } finally { + if (savepoint != null) { + try { + trx.releaseSavepoint(savepoint); + } catch (SQLException e) {} + } } } @@ -977,6 +984,25 @@ public class Doc_Invoice extends Doc fl.setQty(line.getQty()); } else if (compare < 0) + { + drAmt = dr ? (reversal ? null : estimatedAmt) : (reversal ? estimatedAmt : null); + crAmt = dr ? (reversal ? estimatedAmt : null) : (reversal ? null : estimatedAmt); + account = pc.getAccount(ProductCost.ACCTTYPE_P_LandedCostClearing, as); + FactLine fl = fact.createLine (line, account, getC_Currency_ID(), drAmt, crAmt); + fl.setDescription(desc); + fl.setM_Product_ID(lca.getM_Product_ID()); + fl.setQty(line.getQty()); + + BigDecimal underAmt = estimatedAmt.subtract(allocationAmt); + drAmt = dr ? (reversal ? underAmt : null) : (reversal ? null : underAmt); + crAmt = dr ? (reversal ? null : underAmt) : (reversal ? underAmt : null); + account = zeroQty ? pc.getAccount(ProductCost.ACCTTYPE_P_AverageCostVariance, as) : pc.getAccount(ProductCost.ACCTTYPE_P_Asset, as); + fl = fact.createLine (line, account, getC_Currency_ID(), drAmt, crAmt); + fl.setDescription(desc); + fl.setM_Product_ID(lca.getM_Product_ID()); + fl.setQty(line.getQty()); + } + else { drAmt = dr ? (reversal ? null : allocationAmt) : (reversal ? allocationAmt : null); crAmt = dr ? (reversal ? allocationAmt : null) : (reversal ? null : allocationAmt); diff --git a/org.adempiere.base/src/org/compiere/acct/Doc_MatchInv.java b/org.adempiere.base/src/org/compiere/acct/Doc_MatchInv.java index 632b18bccf..18907d884a 100644 --- a/org.adempiere.base/src/org/compiere/acct/Doc_MatchInv.java +++ b/org.adempiere.base/src/org/compiere/acct/Doc_MatchInv.java @@ -508,7 +508,7 @@ public class Doc_MatchInv extends Doc { BigDecimal totalAmt = allocation.getAmt(); BigDecimal totalQty = allocation.getQty(); - BigDecimal amt = totalAmt.multiply(tQty).divide(totalQty, BigDecimal.ROUND_HALF_UP); + BigDecimal amt = totalAmt.multiply(tQty).divide(totalQty, 12, BigDecimal.ROUND_HALF_UP); if (orderLine.getC_Currency_ID() != as.getC_Currency_ID()) { I_C_Order order = orderLine.getC_Order(); diff --git a/org.adempiere.base/src/org/compiere/acct/Doc_MatchPO.java b/org.adempiere.base/src/org/compiere/acct/Doc_MatchPO.java index 419fe010b1..eafa8c67e2 100644 --- a/org.adempiere.base/src/org/compiere/acct/Doc_MatchPO.java +++ b/org.adempiere.base/src/org/compiere/acct/Doc_MatchPO.java @@ -198,7 +198,7 @@ public class Doc_MatchPO extends Doc { BigDecimal totalAmt = allocation.getAmt(); BigDecimal totalQty = allocation.getQty(); - BigDecimal amt = totalAmt.multiply(m_ioLine.getMovementQty()).divide(totalQty, as.getCostingPrecision(), RoundingMode.HALF_UP); + BigDecimal amt = totalAmt.multiply(m_ioLine.getMovementQty()).divide(totalQty, 12, RoundingMode.HALF_UP); if (m_oLine.getC_Currency_ID() != as.getC_Currency_ID()) { MOrder order = m_oLine.getParent(); @@ -213,11 +213,11 @@ public class Doc_MatchPO extends Doc return null; } amt = amt.multiply(rate); - if (amt.scale() > as.getCostingPrecision()) - amt = amt.setScale(as.getCostingPrecision(), BigDecimal.ROUND_HALF_UP); } - amt = amt.divide(getQty(), as.getCostingPrecision(), RoundingMode.HALF_UP); + amt = amt.divide(getQty(), 12, RoundingMode.HALF_UP); landedCost = landedCost.add(amt); + if (landedCost.scale() > as.getCostingPrecision()) + landedCost = landedCost.setScale(as.getCostingPrecision(), BigDecimal.ROUND_HALF_UP); int elementId = allocation.getC_OrderLandedCost().getM_CostElement_ID(); BigDecimal elementAmt = landedCostMap.get(elementId); if (elementAmt == null) @@ -442,6 +442,8 @@ public class Doc_MatchPO extends Doc return error; } + if (tAmt.scale() > as.getCostingPrecision()) + tAmt = tAmt.setScale(as.getCostingPrecision(), BigDecimal.ROUND_HALF_UP); // Set Total Amount and Total Quantity from Matched PO if (!MCostDetail.createOrder(as, m_oLine.getAD_Org_ID(), getM_Product_ID(), mMatchPO.getM_AttributeSetInstance_ID(), @@ -471,6 +473,8 @@ public class Doc_MatchPO extends Doc { BigDecimal amt = landedCostMap.get(elementId); amt = amt.multiply(tQty); + if (amt.scale() > as.getCostingPrecision()) + amt = amt.setScale(as.getCostingPrecision(), BigDecimal.ROUND_HALF_UP); if (!MCostDetail.createOrder(as, m_oLine.getAD_Org_ID(), getM_Product_ID(), mMatchPO.getM_AttributeSetInstance_ID(), m_oLine.getC_OrderLine_ID(), elementId, diff --git a/org.adempiere.base/src/org/compiere/model/MInvoiceLine.java b/org.adempiere.base/src/org/compiere/model/MInvoiceLine.java index fc636f457d..ca1376503d 100644 --- a/org.adempiere.base/src/org/compiere/model/MInvoiceLine.java +++ b/org.adempiere.base/src/org/compiere/model/MInvoiceLine.java @@ -1075,7 +1075,7 @@ public class MInvoiceLine extends X_C_InvoiceLine { double result = getLineNetAmt().multiply(base).doubleValue(); result /= total.doubleValue(); - lca.setAmt(result, getPrecision()); + lca.setAmt(result, getParent().getC_Currency().getCostingPrecision()); } if (!lca.save()){ msgreturn = new StringBuilder("Cannot save line Allocation = ").append(lca); @@ -1200,7 +1200,7 @@ public class MInvoiceLine extends X_C_InvoiceLine { double result = getLineNetAmt().multiply(base).doubleValue(); result /= total.doubleValue(); - lca.setAmt(result, getPrecision()); + lca.setAmt(result, getParent().getC_Currency().getCostingPrecision()); } if (!lca.save()){ msgreturn = new StringBuilder("Cannot save line Allocation = ").append(lca); diff --git a/org.adempiere.base/src/org/compiere/model/MJournal.java b/org.adempiere.base/src/org/compiere/model/MJournal.java index 6ae875d6f8..add27fd13b 100644 --- a/org.adempiere.base/src/org/compiere/model/MJournal.java +++ b/org.adempiere.base/src/org/compiere/model/MJournal.java @@ -760,15 +760,29 @@ public class MJournal extends X_GL_Journal implements DocAction reverse.setReversal_ID(getGL_Journal_ID()); if (!reverse.save()) return null; + + // Lines + reverse.copyLinesFrom(this, null, 'C'); + // + if (!reverse.processIt(DocAction.ACTION_Complete)) + { + m_processMsg = "Reversal ERROR: " + reverse.getProcessMsg(); + return null; + } + reverse.closeIt(); + reverse.setProcessing(false); + reverse.setDocStatus(DOCSTATUS_Reversed); + reverse.setDocAction(DOCACTION_None); + reverse.saveEx(get_TrxName()); + // msgd = new StringBuilder("(").append(reverse.getDocumentNo()).append("<-)"); addDescription(msgd.toString()); - // Lines - reverse.copyLinesFrom(this, null, 'C'); // setProcessed(true); //FR [ 1948157 ] setReversal_ID(reverse.getGL_Journal_ID()); + setDocStatus(DOCSTATUS_Reversed); setDocAction(DOCACTION_None); return reverse; } // reverseCorrectionIt @@ -814,19 +828,31 @@ public class MJournal extends X_GL_Journal implements DocAction reverse.set_ValueNoCheck ("C_Period_ID", null); // reset reverse.setDateAcct(reverse.getDateDoc()); // Reverse indicator - StringBuilder description; - if (reverse.getDescription() == null) - description = new StringBuilder("** ").append(getDocumentNo()).append(" **"); - else - description = new StringBuilder(reverse.getDescription()).append(" ** ").append(getDocumentNo()).append(" **"); - reverse.setDescription(description.toString()); + StringBuilder msgd = new StringBuilder("(->").append(getDocumentNo()).append(")"); + reverse.addDescription(msgd.toString()); + reverse.setReversal_ID(getGL_Journal_ID()); if (!reverse.save()) return null; - // Lines reverse.copyLinesFrom(this, reverse.getDateAcct(), 'R'); // + if (!reverse.processIt(DocAction.ACTION_Complete)) + { + m_processMsg = "Reversal ERROR: " + reverse.getProcessMsg(); + return null; + } + reverse.closeIt(); + reverse.setProcessing(false); + reverse.setDocStatus(DOCSTATUS_Reversed); + reverse.setDocAction(DOCACTION_None); + reverse.saveEx(get_TrxName()); + // + msgd = new StringBuilder("(").append(reverse.getDocumentNo()).append("<-)"); + addDescription(msgd.toString()); + setProcessed(true); + setReversal_ID(reverse.getGL_Journal_ID()); + setDocStatus(DOCSTATUS_Reversed); setDocAction(DOCACTION_None); return reverse; } // reverseAccrualIt diff --git a/org.adempiere.base/src/org/compiere/model/MJournalBatch.java b/org.adempiere.base/src/org/compiere/model/MJournalBatch.java index 336cbc25f8..ec67283ab7 100644 --- a/org.adempiere.base/src/org/compiere/model/MJournalBatch.java +++ b/org.adempiere.base/src/org/compiere/model/MJournalBatch.java @@ -628,16 +628,11 @@ public class MJournalBatch extends X_GL_JournalBatch implements DocAction reverse.setC_Period_ID(getC_Period_ID()); reverse.setDateAcct(getDateAcct()); // Reverse indicator - StringBuilder description; - if (reverse.getDescription() == null) - description = new StringBuilder("** ").append(getDocumentNo()).append(" **"); - else - description = new StringBuilder(reverse.getDescription()).append(" ** ").append(getDocumentNo()).append(" **"); - reverse.setDescription(description.toString()); + StringBuilder msgd = new StringBuilder("(->").append(getDocumentNo()).append(")"); + reverse.addDescription(msgd.toString()); //[ 1948157 ] reverse.setReversal_ID(getGL_JournalBatch_ID()); reverse.saveEx(); - // // Reverse Journals for (int i = 0; i < journals.length; i++) @@ -652,9 +647,26 @@ public class MJournalBatch extends X_GL_JournalBatch implements DocAction } journal.saveEx(); } - + // + if (!reverse.processIt(DocAction.ACTION_Complete)) + { + m_processMsg = "Reversal ERROR: " + reverse.getProcessMsg(); + return false; + } + reverse.closeIt(); + reverse.setProcessing(false); + reverse.setDocStatus(DOCSTATUS_Reversed); + reverse.setDocAction(DOCACTION_None); + reverse.saveEx(get_TrxName()); + // + msgd = new StringBuilder("(").append(reverse.getDocumentNo()).append("<-)"); + addDescription(msgd.toString()); + + setProcessed(true); //[ 1948157 ] setReversal_ID(reverse.getGL_JournalBatch_ID()); + setDocStatus(DOCSTATUS_Reversed); + setDocAction(DOCACTION_None); saveEx(); // After reverseCorrect m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REVERSECORRECT); @@ -699,12 +711,9 @@ public class MJournalBatch extends X_GL_JournalBatch implements DocAction reverse.setDateDoc(new Timestamp(System.currentTimeMillis())); reverse.setDateAcct(reverse.getDateDoc()); // Reverse indicator - StringBuilder description; - if (reverse.getDescription() == null) - description = new StringBuilder("** ").append(getDocumentNo()).append(" **"); - else - description = new StringBuilder(reverse.getDescription()).append(" ** ").append(getDocumentNo()).append(" **"); - reverse.setDescription(description.toString()); + StringBuilder msgd = new StringBuilder("(->").append(getDocumentNo()).append(")"); + reverse.addDescription(msgd.toString()); + reverse.setReversal_ID(getGL_JournalBatch_ID()); reverse.saveEx(); // Reverse Journals @@ -720,6 +729,26 @@ public class MJournalBatch extends X_GL_JournalBatch implements DocAction } journal.saveEx(); } + // + if (!reverse.processIt(DocAction.ACTION_Complete)) + { + m_processMsg = "Reversal ERROR: " + reverse.getProcessMsg(); + return false; + } + reverse.closeIt(); + reverse.setProcessing(false); + reverse.setDocStatus(DOCSTATUS_Reversed); + reverse.setDocAction(DOCACTION_None); + reverse.saveEx(get_TrxName()); + // + msgd = new StringBuilder("(").append(reverse.getDocumentNo()).append("<-)"); + addDescription(msgd.toString()); + + setProcessed(true); + setReversal_ID(reverse.getGL_JournalBatch_ID()); + setDocStatus(DOCSTATUS_Reversed); + setDocAction(DOCACTION_None); + saveEx(); // After reverseAccrual m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REVERSEACCRUAL); if (m_processMsg != null) @@ -872,4 +901,14 @@ public class MJournalBatch extends X_GL_JournalBatch implements DocAction return getTotalDr(); } // getApprovalAmt + public void addDescription (String description) + { + String desc = getDescription(); + if (desc == null) + setDescription(description); + else{ + StringBuilder msgd = new StringBuilder(desc).append(" | ").append(description); + setDescription(msgd.toString()); + } + } } // MJournalBatch diff --git a/org.adempiere.base/src/org/compiere/model/MOrderLandedCost.java b/org.adempiere.base/src/org/compiere/model/MOrderLandedCost.java index 15daceb800..adb1b17bdd 100644 --- a/org.adempiere.base/src/org/compiere/model/MOrderLandedCost.java +++ b/org.adempiere.base/src/org/compiere/model/MOrderLandedCost.java @@ -146,8 +146,8 @@ public class MOrderLandedCost extends X_C_OrderLandedCost { if (base.signum() != 0) { BigDecimal result = getAmt().multiply(base); - result = result.divide(total, orderLine.getParent().getC_Currency().getStdPrecision(), BigDecimal.ROUND_HALF_UP); - allocation.setAmt(result.doubleValue(), orderLine.getParent().getC_Currency().getStdPrecision()); + result = result.divide(total, orderLine.getParent().getC_Currency().getCostingPrecision(), BigDecimal.ROUND_HALF_UP); + allocation.setAmt(result.doubleValue(), orderLine.getParent().getC_Currency().getCostingPrecision()); } allocation.saveEx(); } diff --git a/org.adempiere.pipo/src/org/adempiere/pipo2/PackOut.java b/org.adempiere.pipo/src/org/adempiere/pipo2/PackOut.java index fba147e04e..4161c57663 100644 --- a/org.adempiere.pipo/src/org/adempiere/pipo2/PackOut.java +++ b/org.adempiere.pipo/src/org/adempiere/pipo2/PackOut.java @@ -229,6 +229,8 @@ public class PackOut .append("-") .append(client.getName()); atts.addAttribute("", "", "Client", "CDATA", sb.toString()); + if (client.getAD_Client_UU() == null) + throw new IllegalStateException("2Pack requires UUID on AD_Client"); atts.addAttribute("", "", "AD_Client_UU", "CDATA", client.getAD_Client_UU()); packoutHandler.startElement("","","idempiere",atts);