diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index bf8f8bc231..6709a52b42 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -35,33 +35,42 @@ jobs:
# Install Java
- name: Setup Java
- uses: actions/setup-java@v3
+ uses: actions/setup-java@v4
with:
java-version: '${{ env.java }}'
distribution: ${{ env.java_distribution }}
+ check-latest: true
+
+ # setup maven to 3.9 for tycho
+ - name: Set up Maven
+ uses: stCarolas/setup-maven@v5
+ with:
+ maven-version: 3.9.6
# on case PR it check out to commit is merger of PR to base (master)
- name: Checkout repository
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
# fetch all commit so sornar can know who change a line, it's resolved Warning: Shallow clone detected, no blame information will be provided. You can convert to non-shallow with 'git fetch --unshallow'.
fetch-depth: 0
+ # restore sonar cache
- name: Cache sonar material restore
id: cache-sonar-material-restore
- uses: actions/cache/restore@v3
+ uses: actions/cache/restore@v4
with:
path: |
~/.sonar/cache
key: ${{ runner.os }}-sonar-${{ env.branch_name }}
+ # restore maven cache
- name: Cache maven material restore
id: cache-maven-material-restore
- uses: actions/cache/restore@v3
+ uses: actions/cache/restore@v4
with:
path: |
- ~/.m2
- key: ${{ runner.os }}-maven-${{ env.branch_name }}
+ ~/.m2/repository
+ key: ${{ runner.os }}-maven-${{ env.branch_name }}-repository
# run sonar on master only because sonar for PR come from other repository isn't support at moment (already on develop)
# https://stackoverflow.com/a/39720346
@@ -78,19 +87,21 @@ jobs:
codeql="-Dfindbugs.skip -Dcheckstyle.skip -Dpmd.skip=true -Denforcer.skip -Dmaven.javadoc.skip -DskipTests -Dmaven.test.skip.exec -Dlicense.skip=true -Drat.skip=true"
mvn -B -V -e $codeql $sonar $sonarProject $sonarExclusions package org.sonarsource.scanner.maven:sonar-maven-plugin:sonar
+ # save sonar cache
- name: Cache sonar material save
- uses: actions/cache/save@v3
+ uses: actions/cache/save@v4
with:
path: |
~/.sonar/cache
key: ${{ steps.cache-sonar-material-restore.outputs.cache-primary-key }}
-
+
+ # save maven cache
- name: Cache maven material save
- uses: actions/cache/save@v3
+ uses: actions/cache/save@v4
with:
path: |
- ~/.m2
- key: ${{ steps.cache-maven-material-restore.outputs.cache-primary-key }}
+ ~/.m2/repository
+ key: ${{ steps.cache-maven-material-restore.outputs.cache-primary-key }}-repository
analyze_java_codeQL:
name: Analyze java by code QL
@@ -103,26 +114,34 @@ jobs:
# Install Java
- name: Setup Java
- uses: actions/setup-java@v3
+ uses: actions/setup-java@v4
with:
java-version: '${{ env.java }}'
distribution: ${{ env.java_distribution }}
+ check-latest: true
+
+ # setup maven to 3.9 for tycho
+ - name: Set up Maven
+ uses: stCarolas/setup-maven@v5
+ with:
+ maven-version: 3.9.6
# on case PR it check out to commit is merger of PR to base (master)
- name: Checkout repository
- uses: actions/checkout@v3
-
+ uses: actions/checkout@v4
+
+ # restore maven cache
- name: Cache maven material restore
id: cache-maven-material-restore
- uses: actions/cache/restore@v3
+ uses: actions/cache/restore@v4
with:
path: |
- ~/.m2
- key: ${{ runner.os }}-maven-${{ env.branch_name }}
+ ~/.m2/repository
+ key: ${{ runner.os }}-maven-${{ env.branch_name }}-repository
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
- uses: github/codeql-action/init@v2
+ uses: github/codeql-action/init@v3
with:
languages: java
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -131,17 +150,18 @@ jobs:
# queries: ./path/to/local/query, your-org/your-repo/queries@main
- name: Autobuild
- uses: github/codeql-action/autobuild@v2
+ uses: github/codeql-action/autobuild@v3
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v2
+ uses: github/codeql-action/analyze@v3
+ # save maven cache
- name: Cache maven material save
- uses: actions/cache/save@v3
+ uses: actions/cache/save@v4
with:
path: |
- ~/.m2
- key: ${{ steps.cache-maven-material-restore.outputs.cache-primary-key }}
+ ~/.m2/repository
+ key: ${{ steps.cache-maven-material-restore.outputs.cache-primary-key }}-repository
analyze_javascript_codeQL:
@@ -155,12 +175,12 @@ jobs:
# on case PR it check out to commit is merger of PR to base (master)
- name: Checkout repository
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Initialize CodeQL
- uses: github/codeql-action/init@v2
+ uses: github/codeql-action/init@v3
with:
languages: javascript
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v2
+ uses: github/codeql-action/analyze@v3
diff --git a/db/oracle/functions/BOM_PriceLimit.sql b/db/oracle/functions/BOM_PriceLimit.sql
index 8ca102b31c..f946df8932 100644
--- a/db/oracle/functions/BOM_PriceLimit.sql
+++ b/db/oracle/functions/BOM_PriceLimit.sql
@@ -27,6 +27,7 @@ AS
WHERE b.M_ProductBOM_ID=p.M_Product_ID
AND b.M_Product_ID=Product_ID
AND b.M_ProductBOM_ID != Product_ID
+ AND (p.IsBOM='N' OR p.IsVerified='Y')
AND b.IsActive='Y';
--
BEGIN
@@ -34,7 +35,7 @@ BEGIN
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;
+ WHERE IsActive='Y' AND 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
diff --git a/db/oracle/functions/BOM_PriceList.sql b/db/oracle/functions/BOM_PriceList.sql
index b9590f4501..20a634f5d8 100644
--- a/db/oracle/functions/BOM_PriceList.sql
+++ b/db/oracle/functions/BOM_PriceList.sql
@@ -27,6 +27,7 @@ AS
WHERE b.M_ProductBOM_ID=p.M_Product_ID
AND b.M_Product_ID=Product_ID
AND b.M_ProductBOM_ID != Product_ID
+ AND (p.IsBOM='N' OR p.IsVerified='Y')
AND b.IsActive='Y';
--
BEGIN
@@ -34,7 +35,7 @@ BEGIN
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;
+ WHERE IsActive='Y' AND M_PriceList_Version_ID=PriceList_Version_ID AND M_Product_ID=Product_ID;
-- DBMS_OUTPUT.PUT_LINE('Price=' || Price);
-- No Price - Check if BOM
diff --git a/db/oracle/functions/BOM_PriceStd.sql b/db/oracle/functions/BOM_PriceStd.sql
index 143dbf87d6..528ee669b3 100644
--- a/db/oracle/functions/BOM_PriceStd.sql
+++ b/db/oracle/functions/BOM_PriceStd.sql
@@ -27,6 +27,7 @@ AS
WHERE b.M_ProductBOM_ID=p.M_Product_ID
AND b.M_Product_ID=Product_ID
AND b.M_ProductBOM_ID != Product_ID
+ AND (p.IsBOM='N' OR p.IsVerified='Y')
AND b.IsActive='Y';
--
BEGIN
@@ -34,7 +35,7 @@ BEGIN
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;
+ WHERE IsActive='Y' AND 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
diff --git a/db/oracle/functions/BOM_Qty_OnHand.sql b/db/oracle/functions/BOM_Qty_OnHand.sql
index 285c5ffe5a..e199e43bc4 100644
--- a/db/oracle/functions/BOM_Qty_OnHand.sql
+++ b/db/oracle/functions/BOM_Qty_OnHand.sql
@@ -23,13 +23,11 @@ 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, p.IsVerified
FROM M_PRODUCT_BOM b, M_PRODUCT p
WHERE b.M_ProductBOM_ID=p.M_Product_ID
AND b.M_Product_ID=Product_ID
AND b.M_ProductBOM_ID != Product_ID
- AND p.IsBOM='Y'
- AND p.IsVerified='Y'
AND b.IsActive='Y';
--
BEGIN
@@ -87,19 +85,14 @@ BEGIN
FROM M_Storageonhand s
JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)
WHERE s.M_Product_ID=bom.M_ProductBOM_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);
+ ProductQty := ProductQty/bom.BOMQty;
-- How much can we make overall
IF (ProductQty < Quantity) THEN
Quantity := ProductQty;
END IF;
-- Another BOM
- ELSIF (bom.IsBOM = 'Y') THEN
+ ELSIF (bom.IsBOM = 'Y' AND BOM.IsVerified = 'Y') THEN
ProductQty := Bomqtyonhand (bom.M_ProductBOM_ID, myWarehouse_ID, Locator_ID);
-- How much can we make overall
IF (ProductQty < Quantity) THEN
@@ -115,7 +108,7 @@ BEGIN
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);
+ RETURN TRUNC(Quantity, StdPrecision); -- RoundDown
END IF;
RETURN 0;
END Bomqtyonhand;
diff --git a/db/oracle/functions/BOM_Qty_OnHandForReservation.sql b/db/oracle/functions/BOM_Qty_OnHandForReservation.sql
index 125893a0e1..e7160d8658 100644
--- a/db/oracle/functions/BOM_Qty_OnHandForReservation.sql
+++ b/db/oracle/functions/BOM_Qty_OnHandForReservation.sql
@@ -23,13 +23,11 @@ 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, p.IsVerified
FROM M_PRODUCT_BOM b, M_PRODUCT p
WHERE b.M_ProductBOM_ID=p.M_Product_ID
AND b.M_Product_ID=Product_ID
AND b.M_ProductBOM_ID != Product_ID
- AND p.IsBOM='Y'
- AND p.IsVerified='Y'
AND b.IsActive='Y';
--
BEGIN
@@ -90,20 +88,15 @@ BEGIN
JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)
LEFT JOIN M_LocatorType lt ON (l.M_LocatorType_ID=lt.M_LocatorType_ID)
WHERE s.M_Product_ID=bom.M_ProductBOM_ID AND l.M_Warehouse_ID=myWarehouse_ID
- AND COALESCE(lt.IsAvailableForReservation,'Y')='Y';
- -- 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;
+ AND COALESCE(lt.IsAvailableForReservation,'Y')='Y';
-- How much can we make with this product
- ProductQty := ROUND (ProductQty/bom.BOMQty, StdPrecision);
+ ProductQty := ProductQty/bom.BOMQty;
-- How much can we make overall
IF (ProductQty < Quantity) THEN
Quantity := ProductQty;
END IF;
-- Another BOM
- ELSIF (bom.IsBOM = 'Y') THEN
+ ELSIF (bom.IsBOM = 'Y' AND bom.IsVerified = 'Y') THEN
ProductQty := BomqtyonhandForReservation (bom.M_ProductBOM_ID, myWarehouse_ID, Locator_ID);
-- How much can we make overall
IF (ProductQty < Quantity) THEN
@@ -119,7 +112,7 @@ BEGIN
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);
+ RETURN TRUNC(Quantity, StdPrecision); -- RoundDown
END IF;
RETURN 0;
END BOMQTYONHANDFORRESERVATION;
diff --git a/db/oracle/functions/BOM_Qty_Ordered.sql b/db/oracle/functions/BOM_Qty_Ordered.sql
index 3fb7ec0651..a7d89aee11 100644
--- a/db/oracle/functions/BOM_Qty_Ordered.sql
+++ b/db/oracle/functions/BOM_Qty_Ordered.sql
@@ -23,13 +23,11 @@ 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, p.IsVerified
FROM M_PRODUCT_BOM b, M_PRODUCT p
WHERE b.M_ProductBOM_ID=p.M_Product_ID
AND b.M_Product_ID=p_Product_ID
AND b.M_ProductBOM_ID != p_Product_ID
- AND p.IsBOM='Y'
- AND p.IsVerified='Y'
AND b.IsActive='Y';
--
BEGIN
@@ -91,19 +89,14 @@ BEGIN
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);
+ v_ProductQty := v_ProductQty/bom.BOMQty;
-- 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
+ ELSIF (bom.IsBOM = 'Y' AND BOM.IsVerified = '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
@@ -124,7 +117,7 @@ BEGIN
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);
+ RETURN TRUNC(v_Quantity, v_StdPrecision); -- RoundDown
END IF;
--
RETURN 0;
diff --git a/db/oracle/functions/BOM_Qty_Reserved.sql b/db/oracle/functions/BOM_Qty_Reserved.sql
index 1d07949d8d..812f5fd3b3 100644
--- a/db/oracle/functions/BOM_Qty_Reserved.sql
+++ b/db/oracle/functions/BOM_Qty_Reserved.sql
@@ -23,13 +23,11 @@ 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, p.IsVerified
FROM M_PRODUCT_BOM b, M_PRODUCT p
WHERE b.M_ProductBOM_ID=p.M_Product_ID
AND b.M_Product_ID=p_Product_ID
AND b.M_ProductBOM_ID != p_Product_ID
- AND p.IsBOM='Y'
- AND p.IsVerified='Y'
AND b.IsActive='Y';
--
BEGIN
@@ -91,19 +89,14 @@ BEGIN
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);
+ v_ProductQty := v_ProductQty/bom.BOMQty;
-- 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
+ ELSIF (bom.IsBOM = 'Y' AND BOM.IsVerified = '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
@@ -124,7 +117,7 @@ BEGIN
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);
+ RETURN TRUNC(v_Quantity, v_StdPrecision); -- RoundDown
END IF;
RETURN 0;
END Bomqtyreserved;
diff --git a/db/postgresql/functions/BOM_PriceLimit.sql b/db/postgresql/functions/BOM_PriceLimit.sql
index 216dce20a5..fb22fcf3a6 100644
--- a/db/postgresql/functions/BOM_PriceLimit.sql
+++ b/db/postgresql/functions/BOM_PriceLimit.sql
@@ -10,7 +10,7 @@ BEGIN
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;
+ WHERE IsActive='Y' AND M_PriceList_Version_ID=PriceList_Version_ID AND M_Product_ID=Product_ID;
-- No Price - Check if BOM
IF (v_Price = 0) THEN
@@ -20,6 +20,7 @@ BEGIN
WHERE b.M_ProductBOM_ID=p.M_Product_ID
AND b.M_Product_ID=Product_ID
AND b.M_ProductBOM_ID != Product_ID
+ AND (p.IsBOM='N' OR p.IsVerified='Y')
AND b.IsActive='Y'
LOOP
v_ProductPrice := bomPriceLimit (bom.M_ProductBOM_ID, PriceList_Version_ID);
@@ -30,7 +31,6 @@ BEGIN
RETURN v_Price;
END;
-
$BODY$
LANGUAGE 'plpgsql' STABLE
;
diff --git a/db/postgresql/functions/BOM_PriceList.sql b/db/postgresql/functions/BOM_PriceList.sql
index 03f69e0dae..d0f007fafa 100644
--- a/db/postgresql/functions/BOM_PriceList.sql
+++ b/db/postgresql/functions/BOM_PriceList.sql
@@ -10,7 +10,7 @@ BEGIN
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;
+ WHERE IsActive='Y' AND M_PriceList_Version_ID=PriceList_Version_ID AND M_Product_ID=Product_ID;
-- No Price - Check if BOM
IF (v_Price = 0) THEN
@@ -20,6 +20,7 @@ BEGIN
WHERE b.M_ProductBOM_ID=p.M_Product_ID
AND b.M_Product_ID=Product_ID
AND b.M_ProductBOM_ID != Product_ID
+ AND (p.IsBOM='N' OR p.IsVerified='Y')
AND b.IsActive='Y'
LOOP
v_ProductPrice := bomPriceList (bom.M_ProductBOM_ID, PriceList_Version_ID);
@@ -30,7 +31,6 @@ BEGIN
RETURN v_Price;
END;
-
$BODY$
LANGUAGE 'plpgsql' STABLE
;
diff --git a/db/postgresql/functions/BOM_PriceStd.sql b/db/postgresql/functions/BOM_PriceStd.sql
index 5c34e42dca..bf3a3f0711 100644
--- a/db/postgresql/functions/BOM_PriceStd.sql
+++ b/db/postgresql/functions/BOM_PriceStd.sql
@@ -10,7 +10,7 @@ BEGIN
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;
+ WHERE IsActive='Y' AND M_PriceList_Version_ID=PriceList_Version_ID AND M_Product_ID=Product_ID;
-- No Price - Check if BOM
IF (v_Price = 0) THEN
@@ -20,6 +20,7 @@ BEGIN
WHERE b.M_ProductBOM_ID=p.M_Product_ID
AND b.M_Product_ID=Product_ID
AND b.M_ProductBOM_ID != Product_ID
+ AND (p.IsBOM='N' OR p.IsVerified='Y')
AND b.IsActive='Y'
LOOP
v_ProductPrice := bomPriceStd (bom.M_ProductBOM_ID, PriceList_Version_ID);
@@ -30,7 +31,6 @@ BEGIN
RETURN v_Price;
END;
-
$BODY$
LANGUAGE 'plpgsql' STABLE
;
diff --git a/db/postgresql/functions/BOM_Qty_OnHand.sql b/db/postgresql/functions/BOM_Qty_OnHand.sql
index 697b2de662..b6060f6a40 100644
--- a/db/postgresql/functions/BOM_Qty_OnHand.sql
+++ b/db/postgresql/functions/BOM_Qty_OnHand.sql
@@ -54,13 +54,11 @@ BEGIN
-- Go through BOM
FOR bom IN -- Get BOM Product info
- 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, p.IsVerified
FROM M_PRODUCT_BOM b, M_PRODUCT p
WHERE b.M_ProductBOM_ID=p.M_Product_ID
AND b.M_Product_ID=product_ID
AND b.M_ProductBOM_ID != Product_ID
- AND p.IsBOM='Y'
- AND p.IsVerified='Y'
AND b.IsActive='Y'
LOOP
-- Stocked Items "leaf node"
@@ -71,19 +69,14 @@ BEGIN
FROM M_Storageonhand s
JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)
WHERE s.M_Product_ID=bom.M_ProductBOM_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);
+ v_ProductQty := v_ProductQty/bom.BOMQty;
-- 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
+ ELSIF (bom.IsBOM = 'Y' AND BOM.IsVerified = '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
@@ -99,7 +92,7 @@ BEGIN
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);
+ RETURN TRUNC(v_Quantity, v_StdPrecision); -- RoundDown
END IF;
RETURN 0;
END;
diff --git a/db/postgresql/functions/BOM_Qty_OnHandForReservation.sql b/db/postgresql/functions/BOM_Qty_OnHandForReservation.sql
index 6cde685873..a3b8cf7555 100644
--- a/db/postgresql/functions/BOM_Qty_OnHandForReservation.sql
+++ b/db/postgresql/functions/BOM_Qty_OnHandForReservation.sql
@@ -56,13 +56,11 @@ BEGIN
-- Go through BOM
FOR bom IN -- Get BOM Product info
- 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, p.IsVerified
FROM M_PRODUCT_BOM b, M_PRODUCT p
WHERE b.M_ProductBOM_ID=p.M_Product_ID
AND b.M_Product_ID=product_ID
AND b.M_ProductBOM_ID != Product_ID
- AND p.IsBOM='Y'
- AND p.IsVerified='Y'
AND b.IsActive='Y'
LOOP
-- Stocked Items "leaf node"
@@ -75,19 +73,14 @@ BEGIN
LEFT JOIN M_LocatorType lt ON (l.M_LocatorType_ID=lt.M_LocatorType_ID)
WHERE s.M_Product_ID=bom.M_ProductBOM_ID AND l.M_Warehouse_ID=myWarehouse_ID
AND COALESCE(lt.IsAvailableForReservation,'Y')='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);
+ v_ProductQty := v_ProductQty/bom.BOMQty;
-- 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
+ ELSIF (bom.IsBOM = 'Y' AND bom.IsVerified = 'Y') THEN
v_ProductQty := BOMQtyOnHandForReservation (bom.M_ProductBOM_ID, myWarehouse_ID, Locator_ID);
-- How much can we make overall
IF (v_ProductQty < v_Quantity) THEN
@@ -103,7 +96,7 @@ BEGIN
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);
+ RETURN TRUNC(v_Quantity, v_StdPrecision); -- RoundDown
END IF;
RETURN 0;
END;
diff --git a/db/postgresql/functions/BOM_Qty_Ordered.sql b/db/postgresql/functions/BOM_Qty_Ordered.sql
index 79c0fda12e..d2bc166dbd 100644
--- a/db/postgresql/functions/BOM_Qty_Ordered.sql
+++ b/db/postgresql/functions/BOM_Qty_Ordered.sql
@@ -57,13 +57,11 @@ BEGIN
-- Go though BOM
FOR bom IN
-- Get BOM Product info
- 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, p.IsVerified
FROM M_PRODUCT_BOM b, M_PRODUCT p
WHERE b.M_ProductBOM_ID=p.M_Product_ID
AND b.M_Product_ID=p_Product_ID
AND b.M_ProductBOM_ID != p_Product_ID
- AND p.IsBOM='Y'
- AND p.IsVerified='Y'
AND b.IsActive='Y'
LOOP
-- Stocked Items "leaf node"
@@ -76,20 +74,15 @@ BEGIN
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 );
+ v_ProductQty := v_ProductQty/bom.BOMQty;
-- 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
+ ELSIF (bom.IsBOM = 'Y' AND BOM.IsVerified = '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
@@ -110,11 +103,12 @@ BEGIN
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 );
+ RETURN TRUNC(v_Quantity, v_StdPrecision); -- RoundDown
END IF;
--
RETURN 0;
END;
$BODY$
- LANGUAGE plpgsql STABLE;
+LANGUAGE 'plpgsql' STABLE
+;
diff --git a/db/postgresql/functions/BOM_Qty_Reserved.sql b/db/postgresql/functions/BOM_Qty_Reserved.sql
index 2f2b4f428a..e93f19044d 100644
--- a/db/postgresql/functions/BOM_Qty_Reserved.sql
+++ b/db/postgresql/functions/BOM_Qty_Reserved.sql
@@ -57,13 +57,11 @@ BEGIN
-- Go though BOM
FOR bom IN
-- Get BOM Product info
- 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, p.IsVerified
FROM M_PRODUCT_BOM b, M_PRODUCT p
WHERE b.M_ProductBOM_ID=p.M_Product_ID
AND b.M_Product_ID=p_Product_ID
AND b.M_ProductBOM_ID != p_Product_ID
- AND p.IsBOM='Y'
- AND p.IsVerified='Y'
AND b.IsActive='Y'
LOOP
-- Stocked Items "leaf node"
@@ -76,19 +74,14 @@ BEGIN
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);
+ v_ProductQty := v_ProductQty/bom.BOMQty;
-- 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
+ ELSIF (bom.IsBOM = 'Y' AND BOM.IsVerified = '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
@@ -109,11 +102,11 @@ BEGIN
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);
+ RETURN TRUNC(v_Quantity, v_StdPrecision); -- RoundDown
END IF;
RETURN 0;
END;
$BODY$
- LANGUAGE plpgsql STABLE;
-
+LANGUAGE 'plpgsql' STABLE
+;
diff --git a/db/postgresql/functions/ProductAttribute.sql b/db/postgresql/functions/ProductAttribute.sql
index 426b74fefe..3f24880458 100644
--- a/db/postgresql/functions/ProductAttribute.sql
+++ b/db/postgresql/functions/ProductAttribute.sql
@@ -4,7 +4,8 @@ CREATE OR REPLACE FUNCTION ProductAttribute
(
p_M_AttributeSetInstance_ID NUMERIC
)
-RETURNS VARCHAR AS $body$
+RETURNS VARCHAR AS
+$BODY$
/*************************************************************************
* The contents of this file are subject to the Compiere License. You may
@@ -87,6 +88,7 @@ BEGIN
END IF;
RETURN v_Name;
END;
-
-$body$ LANGUAGE plpgsql STABLE;
+$BODY$
+LANGUAGE 'plpgsql' STABLE
+;
diff --git a/db/postgresql/functions/dbreplicasyncverifier.sql b/db/postgresql/functions/dbreplicasyncverifier.sql
new file mode 100644
index 0000000000..f02a615e22
--- /dev/null
+++ b/db/postgresql/functions/dbreplicasyncverifier.sql
@@ -0,0 +1,29 @@
+/*
+CREATE TABLE dbreplicasyncverifier (lastupdate date)
+;
+
+INSERT INTO dbreplicasyncverifier values (to_date('1900-01-01 00:00:00', 'yyyy-mm-dd HH24:MI:SS'))
+;
+*/
+
+CREATE OR REPLACE FUNCTION forbid_multiple_rows_in_dbreplicasyncverifier()
+RETURNS TRIGGER AS $$
+BEGIN
+ -- Check if the table already contains a row
+ IF (SELECT COUNT(*) FROM dbreplicasyncverifier) > 0 THEN
+ RAISE EXCEPTION 'Table can only contain one row.';
+ END IF;
+ RETURN NEW;
+END;
+$$ LANGUAGE plpgsql
+;
+
+CREATE TRIGGER single_row_only_trigger_dbreplicasyncverifier
+BEFORE INSERT ON dbreplicasyncverifier
+FOR EACH ROW
+EXECUTE FUNCTION forbid_multiple_rows_in_dbreplicasyncverifier()
+;
+
+CREATE OR REPLACE RULE delete_dbreplicasyncverifier AS ON DELETE TO dbreplicasyncverifier DO INSTEAD NOTHING
+;
+
diff --git a/db/postgresql/functions/register_migration_script.sql b/db/postgresql/functions/register_migration_script.sql
index 4d07a06bc4..5133534159 100644
--- a/db/postgresql/functions/register_migration_script.sql
+++ b/db/postgresql/functions/register_migration_script.sql
@@ -41,13 +41,6 @@ END;
$BODY$
LANGUAGE plpgsql;
-CREATE TABLE dual ( dummy char );
-
-INSERT INTO dual values ( 'X' );
-
-CREATE OR REPLACE RULE insert_dual AS ON INSERT TO dual DO INSTEAD NOTHING;
-
-CREATE OR REPLACE RULE update_dual AS ON UPDATE TO dual DO INSTEAD NOTHING;
-
-CREATE OR REPLACE RULE delete_dual AS ON DELETE TO dual DO INSTEAD NOTHING;
+CREATE VIEW dual AS SELECT 'X'::varchar AS dummy
+;
diff --git a/migration/iD11/oracle/202402261300_IDEMPIERE-2981.sql b/migration/iD11/oracle/202402261300_IDEMPIERE-2981.sql
new file mode 100644
index 0000000000..aab134c6d2
--- /dev/null
+++ b/migration/iD11/oracle/202402261300_IDEMPIERE-2981.sql
@@ -0,0 +1,10 @@
+-- IDEMPIERE-2981 - Implement JSON Field type
+SELECT register_migration_script('202402261300_IDEMPIERE-2981.sql') FROM dual;
+
+SET SQLBLANKLINES ON
+SET DEFINE OFF
+
+-- Feb 26, 2024, 1:00:29 PM CET
+INSERT INTO AD_Reference (AD_Reference_ID,Name,Description,ValidationType,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,EntityType,IsOrderByValue,AD_Reference_UU,ShowInactive) VALUES (200267,'JSON','JSON format values','D',0,0,'Y',TO_TIMESTAMP('2024-02-26 13:00:28','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-02-26 13:00:28','YYYY-MM-DD HH24:MI:SS'),100,'D','N','b6fcc751-edd8-4421-acd0-3cde02a9576d','N')
+;
+
diff --git a/migration/iD11/oracle/202402261354_IDEMPIERE-2981.sql b/migration/iD11/oracle/202402261354_IDEMPIERE-2981.sql
new file mode 100644
index 0000000000..d43ee4bb10
--- /dev/null
+++ b/migration/iD11/oracle/202402261354_IDEMPIERE-2981.sql
@@ -0,0 +1,30 @@
+-- IDEMPIERE-2981 - Implement JSON Field type
+SELECT register_migration_script('202402261354_IDEMPIERE-2981.sql') FROM dual;
+
+SET SQLBLANKLINES ON
+SET DEFINE OFF
+
+-- Feb 26, 2024, 1:54:35 PM CET
+INSERT INTO AD_Element (AD_Element_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,ColumnName,Name,Description,PrintName,EntityType,AD_Element_UU) VALUES (203924,0,0,'Y',TO_TIMESTAMP('2024-02-26 13:54:35','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-02-26 13:54:35','YYYY-MM-DD HH24:MI:SS'),100,'JsonData','JSON Data','The json field stores json data.','JSON Data','D','c4ea7a81-96a9-4a5d-bb87-e913e1c8ed48')
+;
+
+-- Feb 26, 2024, 1:55:37 PM CET
+INSERT INTO AD_Column (AD_Column_ID,Version,Name,Description,AD_Table_ID,ColumnName,FieldLength,IsKey,IsParent,IsMandatory,IsTranslated,IsIdentifier,SeqNo,IsEncrypted,AD_Reference_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Element_ID,IsUpdateable,IsSelectionColumn,EntityType,IsSyncDatabase,IsAlwaysUpdateable,IsAutocomplete,IsAllowLogging,AD_Column_UU,IsAllowCopy,SeqNoSelection,IsToolbarButton,IsSecure,IsHtml,IsPartitionKey) VALUES (216570,0,'JSON Data','The json field stores json data.',135,'JsonData',0,'N','N','N','N','N',0,'N',200267,0,0,'Y',TO_TIMESTAMP('2024-02-26 13:55:36','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-02-26 13:55:36','YYYY-MM-DD HH24:MI:SS'),100,203924,'Y','N','D','N','N','N','Y','927b83df-d161-4332-ad44-8ffed99e8cf4','Y',0,'N','N','N','N')
+;
+
+-- Feb 28, 2024, 5:41:55 PM CET
+ALTER TABLE Test ADD JsonData CLOB DEFAULT NULL CONSTRAINT test_jsondata_ij CHECK (JsonData IS JSON)
+;
+
+-- Feb 26, 2024, 1:56:08 PM CET
+INSERT INTO AD_Field (AD_Field_ID,Name,Description,AD_Tab_ID,AD_Column_ID,IsDisplayed,DisplayLength,SeqNo,IsSameLine,IsHeading,IsFieldOnly,IsEncrypted,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,IsReadOnly,IsCentrallyMaintained,EntityType,AD_Field_UU,IsDisplayedGrid,SeqNoGrid,ColumnSpan) VALUES (208472,'JSON Data','The json field stores json data.',152,216570,'Y',100,310,'N','N','N','N',0,0,'Y',TO_TIMESTAMP('2024-02-26 13:56:08','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-02-26 13:56:08','YYYY-MM-DD HH24:MI:SS'),100,'N','Y','D','e47ef529-71ba-4f9b-9014-b188e17e8ef4','Y',290,5)
+;
+
+-- Feb 29, 2024, 1:52:50 PM CET
+UPDATE AD_Field SET NumLines=5,Updated=TO_TIMESTAMP('2024-02-29 13:52:50','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=208472
+;
+
+-- Feb 29, 2024, 2:07:30 PM CET
+INSERT INTO AD_Message (MsgType,MsgText,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Message_ID,Value,EntityType,AD_Message_UU) VALUES ('E','Invalid JSON',0,0,'Y',TO_TIMESTAMP('2024-02-29 14:07:30','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-02-29 14:07:30','YYYY-MM-DD HH24:MI:SS'),100,200876,'InvalidJSON','D','a263376f-a12e-4943-92f1-7d7ce8a67a2b')
+;
+
diff --git a/migration/iD11/oracle/202404171125_IDEMPIERE-6110.sql b/migration/iD11/oracle/202404171125_IDEMPIERE-6110.sql
new file mode 100644
index 0000000000..a603f62ddc
--- /dev/null
+++ b/migration/iD11/oracle/202404171125_IDEMPIERE-6110.sql
@@ -0,0 +1,10 @@
+-- IDEMPIERE-6110
+SELECT register_migration_script('202404171125_IDEMPIERE-6110.sql') FROM dual;
+
+SET SQLBLANKLINES ON
+SET DEFINE OFF
+
+-- Apr 17, 2024, 11:25:53 AM BRT
+INSERT INTO AD_Message (MsgType,MsgText,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Message_ID,Value,EntityType,AD_Message_UU) VALUES ('E','Products or charges configured as ''Freight Product'' or ''Freight Charge'' cannot be added to this order due to the combination of delivery via rule and freight cost rule',0,0,'Y',TO_TIMESTAMP('2024-04-17 11:25:53','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-04-17 11:25:53','YYYY-MM-DD HH24:MI:SS'),100,200897,'FreightOrderLineNotAllowed','D','204c8bb9-d002-4beb-bbc3-1d1a11d7471d')
+;
+
diff --git a/migration/iD11/oracle/202404220830_IDEMPIERE-5136_MissingDropIndex.sql b/migration/iD11/oracle/202404220830_IDEMPIERE-5136_MissingDropIndex.sql
new file mode 100644
index 0000000000..f87d9fb8b7
--- /dev/null
+++ b/migration/iD11/oracle/202404220830_IDEMPIERE-5136_MissingDropIndex.sql
@@ -0,0 +1,8 @@
+-- IDEMPIERE-5136
+SELECT register_migration_script('202404220830_IDEMPIERE-5136_MissingDropIndex.sql') FROM dual;
+
+SET SQLBLANKLINES ON
+SET DEFINE OFF
+
+DROP INDEX AD_MESSAGE_TRL_KEY
+;
diff --git a/migration/iD11/oracle/202404302320_IDEMPIERE-6123.sql b/migration/iD11/oracle/202404302320_IDEMPIERE-6123.sql
new file mode 100644
index 0000000000..2d3aacce8e
--- /dev/null
+++ b/migration/iD11/oracle/202404302320_IDEMPIERE-6123.sql
@@ -0,0 +1,22 @@
+-- IDEMPIERE-6123 Query in search window causing slowness and load spikes in the database (FHCA-5356)
+SELECT register_migration_script('202404302320_IDEMPIERE-6123.sql') FROM dual;
+
+SET SQLBLANKLINES ON
+SET DEFINE OFF
+
+-- Apr 30, 2024, 11:20:08 PM CEST
+INSERT INTO AD_SysConfig (AD_SysConfig_ID,AD_Client_ID,AD_Org_ID,Created,Updated,CreatedBy,UpdatedBy,IsActive,Name,Value,Description,EntityType,ConfigurationLevel,AD_SysConfig_UU) VALUES (200244,0,0,TO_TIMESTAMP('2024-04-30 23:20:08','YYYY-MM-DD HH24:MI:SS'),TO_TIMESTAMP('2024-04-30 23:20:08','YYYY-MM-DD HH24:MI:SS'),100,100,'Y','GRIDTABLE_INITIAL_COUNT_TIMEOUT_IN_SECONDS','1','Timeout for the initial count on windows','D','C','5fae1af7-74ca-41d8-bbd3-d506c6c23b6a')
+;
+
+-- Apr 30, 2024, 11:22:16 PM CEST
+INSERT INTO AD_SysConfig (AD_SysConfig_ID,AD_Client_ID,AD_Org_ID,Created,Updated,CreatedBy,UpdatedBy,IsActive,Name,Value,Description,EntityType,ConfigurationLevel,AD_SysConfig_UU) VALUES (200245,0,0,TO_TIMESTAMP('2024-04-30 23:22:16','YYYY-MM-DD HH24:MI:SS'),TO_TIMESTAMP('2024-04-30 23:22:16','YYYY-MM-DD HH24:MI:SS'),100,100,'Y','GLOBAL_MAX_QUERY_RECORDS','100000','Maximum number of records allowed to search in a window, can be overriden per Role or Tab','D','C','840fb67c-4609-41f2-9e20-e0ea9d839065')
+;
+
+-- Apr 30, 2024, 11:23:28 PM CEST
+UPDATE AD_Message SET MsgText='The query returned more records than allowed, consider adding more filters.',Updated=TO_TIMESTAMP('2024-04-30 23:23:28','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Message_ID=852
+;
+
+-- Apr 30, 2024, 11:24:06 PM CEST
+INSERT INTO AD_Message (MsgType,MsgText,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Message_ID,Value,EntityType,AD_Message_UU) VALUES ('I','The initial count query timed out, loading records ...',0,0,'Y',TO_TIMESTAMP('2024-04-30 23:24:06','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-04-30 23:24:06','YYYY-MM-DD HH24:MI:SS'),100,200887,'CountQueryTimeoutLoadBackground','D','988292d7-175f-41c2-b560-43d62b8326a9')
+;
+
diff --git a/migration/iD11/oracle/202405071219_IDEMPIERE-6137.sql b/migration/iD11/oracle/202405071219_IDEMPIERE-6137.sql
new file mode 100644
index 0000000000..8344ff4658
--- /dev/null
+++ b/migration/iD11/oracle/202405071219_IDEMPIERE-6137.sql
@@ -0,0 +1,10 @@
+-- IDEMPIERE-6137 Payment Rule does not appear in reports from Sales Order
+SELECT register_migration_script('202405071219_IDEMPIERE-6137.sql') FROM dual;
+
+SET SQLBLANKLINES ON
+SET DEFINE OFF
+
+-- May 7, 2024, 12:19:00 PM CEST
+UPDATE AD_Column SET AD_Reference_Value_ID=195,Updated=TO_TIMESTAMP('2024-05-07 12:19:00','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Reference_ID=200012 AND COALESCE(AD_Reference_Value_ID,0)!=195
+;
+
diff --git a/migration/iD11/oracle/202405080101_IDEMPIERE-6123.sql b/migration/iD11/oracle/202405080101_IDEMPIERE-6123.sql
new file mode 100644
index 0000000000..ffd792aa44
--- /dev/null
+++ b/migration/iD11/oracle/202405080101_IDEMPIERE-6123.sql
@@ -0,0 +1,22 @@
+-- IDEMPIERE-6123 Query in search window causing slowness and load spikes in the database (FHCA-5356)
+SELECT register_migration_script('202405080101_IDEMPIERE-6123.sql') FROM dual;
+
+SET SQLBLANKLINES ON
+SET DEFINE OFF
+
+-- May 8, 2024, 1:01:16 AM CEST
+INSERT INTO AD_SysConfig (AD_SysConfig_ID,AD_Client_ID,AD_Org_ID,Created,Updated,CreatedBy,UpdatedBy,IsActive,Name,Value,Description,EntityType,ConfigurationLevel,AD_SysConfig_UU) VALUES (200247,0,0,TO_TIMESTAMP('2024-05-08 01:01:16','YYYY-MM-DD HH24:MI:SS'),TO_TIMESTAMP('2024-05-08 01:01:16','YYYY-MM-DD HH24:MI:SS'),100,100,'Y','REPORT_LOAD_TIMEOUT_IN_SECONDS','120','Timeout in seconds when loading a report','D','C','14e838b1-c25c-400e-b39c-61da9bf92099')
+;
+
+-- May 8, 2024, 1:01:42 AM CEST
+INSERT INTO AD_SysConfig (AD_SysConfig_ID,AD_Client_ID,AD_Org_ID,Created,Updated,CreatedBy,UpdatedBy,IsActive,Name,Value,Description,EntityType,ConfigurationLevel,AD_SysConfig_UU) VALUES (200248,0,0,TO_TIMESTAMP('2024-05-08 01:01:41','YYYY-MM-DD HH24:MI:SS'),TO_TIMESTAMP('2024-05-08 01:01:41','YYYY-MM-DD HH24:MI:SS'),100,100,'Y','GLOBAL_MAX_REPORT_RECORDS','100000','Max number of records allowed in a report','D','C','7030640a-1aa7-4ac7-a894-b4fe0dfde530')
+;
+
+-- May 8, 2024, 1:06:19 AM CEST
+INSERT INTO AD_Message (MsgType,MsgText,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Message_ID,Value,EntityType,AD_Message_UU) VALUES ('I','The data query for the report took too much time to execute (over {0} seconds) exceeding the allowed limit',0,0,'Y',TO_TIMESTAMP('2024-05-08 01:06:18','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-05-08 01:06:18','YYYY-MM-DD HH24:MI:SS'),100,200893,'ReportQueryTimeout','D','5f17f55f-adbe-4d97-bf83-9447983b4946')
+;
+
+-- May 8, 2024, 1:07:10 AM CEST
+INSERT INTO AD_Message (MsgType,MsgText,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Message_ID,Value,EntityType,AD_Message_UU) VALUES ('I','The report data exceeds the maximum limit of {0} rows',0,0,'Y',TO_TIMESTAMP('2024-05-08 01:07:09','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-05-08 01:07:09','YYYY-MM-DD HH24:MI:SS'),100,200894,'ReportMaxRowsReached','D','a4b55c31-0df0-4302-a62a-91cb7e79be0d')
+;
+
diff --git a/migration/iD11/oracle/202405131534_IDEMPIERE-6040.sql b/migration/iD11/oracle/202405131534_IDEMPIERE-6040.sql
new file mode 100644
index 0000000000..57a9109402
--- /dev/null
+++ b/migration/iD11/oracle/202405131534_IDEMPIERE-6040.sql
@@ -0,0 +1,25 @@
+-- IDEMPIERE-6040 Improvements for CSV import template
+SELECT register_migration_script('202405131534_IDEMPIERE-6040.sql') FROM dual;
+
+SET SQLBLANKLINES ON
+SET DEFINE OFF
+
+-- May 13, 2024, 3:34:49 PM CEST
+UPDATE AD_Ref_List SET Name='Comma-separated values (CSV)',Updated=TO_TIMESTAMP('2024-05-13 15:34:49','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Ref_List_ID=200704
+;
+
+-- May 13, 2024, 3:35:58 PM CEST
+UPDATE AD_Ref_List SET Name='Excel (XLS/XLSX)',Updated=TO_TIMESTAMP('2024-05-13 15:35:58','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Ref_List_ID=200706
+;
+
+-- May 13, 2024, 3:36:02 PM CEST
+UPDATE AD_Ref_List SET IsActive='N',Updated=TO_TIMESTAMP('2024-05-13 15:36:02','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Ref_List_ID=200705
+;
+
+UPDATE AD_ImportTemplate SET ImportTemplateType='XLSX' WHERE ImportTemplateType='XLS'
+;
+
+-- May 13, 2024, 3:59:49 PM CEST
+UPDATE AD_Field SET SeqNo=120, ColumnSpan=2,Updated=TO_TIMESTAMP('2024-05-13 15:59:49','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=208476
+;
+
diff --git a/migration/iD11/oracle/202405151228_IDEMPIERE-5728.sql b/migration/iD11/oracle/202405151228_IDEMPIERE-5728.sql
new file mode 100644
index 0000000000..0e88421aed
--- /dev/null
+++ b/migration/iD11/oracle/202405151228_IDEMPIERE-5728.sql
@@ -0,0 +1,10 @@
+-- IDEMPIERE-5728
+SELECT register_migration_script('202405151228_IDEMPIERE-5728.sql') FROM dual;
+
+SET SQLBLANKLINES ON
+SET DEFINE OFF
+
+-- May 15, 2024, 12:28:30 PM CEST
+UPDATE AD_ViewColumn SET ColumnName='RV_UnPosted_UU',Updated=TO_TIMESTAMP('2024-05-15 12:28:30','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_ViewColumn_ID=217690
+;
+
diff --git a/migration/iD11/oracle/202406072107_IDEMPIERE-6166.sql b/migration/iD11/oracle/202406072107_IDEMPIERE-6166.sql
new file mode 100644
index 0000000000..fe015d0e68
--- /dev/null
+++ b/migration/iD11/oracle/202406072107_IDEMPIERE-6166.sql
@@ -0,0 +1,5 @@
+-- IDEMPIERE-6166 PostgreSQL DUAL table with more than one record
+SELECT register_migration_script('202406072107_IDEMPIERE-6166.sql') FROM dual;
+
+-- placeholder, this is just required for postgresql
+
diff --git a/migration/iD11/oracle/202406072206_IDEMPIERE-6167.sql b/migration/iD11/oracle/202406072206_IDEMPIERE-6167.sql
new file mode 100644
index 0000000000..52857fe3e4
--- /dev/null
+++ b/migration/iD11/oracle/202406072206_IDEMPIERE-6167.sql
@@ -0,0 +1,161 @@
+-- IDEMPIERE-6167 BOM Price List must search just for Verified BOMs
+SELECT register_migration_script('202406072206_IDEMPIERE-6167.sql') FROM dual;
+
+CREATE OR REPLACE FUNCTION BOMPRICELIMIT
+(
+ Product_ID IN NUMBER,
+ PriceList_Version_ID IN NUMBER
+)
+RETURN NUMBER
+/*************************************************************************
+ * The contents of this file are subject to the Compiere License. You may
+ * obtain a copy of the License at http://www.compiere.org/license.html
+ * Software is on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
+ * express or implied. See the License for details. Code: Compiere ERP+CRM
+ * Copyright (C) 1999-2002 Jorg Janke, ComPiere, Inc. All Rights Reserved.
+ *************************************************************************
+ * $Id: BOM_PriceLimit.sql,v 1.1 2006/04/21 17:51:58 jjanke Exp $
+ ***
+ * Title: Return Limit Price of Product/BOM
+ * Description:
+ * if not found: 0
+ ************************************************************************/
+AS
+ v_Price NUMBER;
+ v_ProductPrice NUMBER;
+ -- Get BOM Product info
+ CURSOR CUR_BOM IS
+ SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM
+ FROM M_PRODUCT_BOM b, M_PRODUCT p
+ WHERE b.M_ProductBOM_ID=p.M_Product_ID
+ AND b.M_Product_ID=Product_ID
+ AND b.M_ProductBOM_ID != Product_ID
+ AND p.IsVerified='Y'
+ AND b.IsActive='Y';
+ --
+BEGIN
+ -- Try to get price from PriceList directly
+ SELECT COALESCE (SUM(PriceLimit), 0)
+ INTO v_Price
+ FROM M_PRODUCTPRICE
+ WHERE M_PriceList_Version_ID=PriceList_Version_ID AND M_Product_ID=Product_ID;
+-- DBMS_OUTPUT.PUT_LINE('Price=' || v_Price);
+
+ -- No Price - Check if BOM
+ IF (v_Price = 0) THEN
+ FOR bom IN CUR_BOM LOOP
+ v_ProductPrice := Bompricelimit (bom.M_ProductBOM_ID, PriceList_Version_ID);
+ v_Price := v_Price + (bom.BOMQty * v_ProductPrice);
+ END LOOP;
+ END IF;
+ --
+ RETURN v_Price;
+END Bompricelimit;
+/
+
+CREATE OR REPLACE FUNCTION BOMPRICELIST
+(
+ Product_ID IN NUMBER,
+ PriceList_Version_ID IN NUMBER
+)
+RETURN NUMBER
+/*************************************************************************
+ * The contents of this file are subject to the Compiere License. You may
+ * obtain a copy of the License at http://www.compiere.org/license.html
+ * Software is on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
+ * express or implied. See the License for details. Code: Compiere ERP+CRM
+ * Copyright (C) 1999-2002 Jorg Janke, ComPiere, Inc. All Rights Reserved.
+ *************************************************************************
+ * $Id: BOM_PriceList.sql,v 1.1 2006/04/21 17:51:58 jjanke Exp $
+ ***
+ * Title: Return List Price of Product/BOM
+ * Description:
+ * if not found: 0
+ ************************************************************************/
+AS
+ v_Price NUMBER;
+ v_ProductPrice NUMBER;
+ -- Get BOM Product info
+ CURSOR CUR_BOM IS
+ SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM
+ FROM M_PRODUCT_BOM b, M_PRODUCT p
+ WHERE b.M_ProductBOM_ID=p.M_Product_ID
+ AND b.M_Product_ID=Product_ID
+ AND b.M_ProductBOM_ID != Product_ID
+ AND p.IsVerified='Y'
+ AND b.IsActive='Y';
+ --
+BEGIN
+ -- Try to get price from pricelist directly
+ SELECT COALESCE (SUM(PriceList), 0)
+ INTO v_Price
+ FROM M_PRODUCTPRICE
+ WHERE M_PriceList_Version_ID=PriceList_Version_ID AND M_Product_ID=Product_ID;
+-- DBMS_OUTPUT.PUT_LINE('Price=' || Price);
+
+ -- No Price - Check if BOM
+ IF (v_Price = 0) THEN
+ FOR bom IN CUR_BOM LOOP
+ v_ProductPrice := Bompricelist (bom.M_ProductBOM_ID, PriceList_Version_ID);
+ v_Price := v_Price + (bom.BOMQty * v_ProductPrice);
+ -- DBMS_OUTPUT.PUT_LINE('Qry=' || bom.BOMQty || ' @ ' || v_ProductPrice || ', Price=' || v_Price);
+ END LOOP; -- BOM
+ END IF;
+ --
+ RETURN v_Price;
+END Bompricelist;
+/
+
+CREATE OR REPLACE FUNCTION BOMPRICESTD
+(
+ Product_ID IN NUMBER,
+ PriceList_Version_ID IN NUMBER
+)
+RETURN NUMBER
+/*************************************************************************
+ * The contents of this file are subject to the Compiere License. You may
+ * obtain a copy of the License at http://www.compiere.org/license.html
+ * Software is on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
+ * express or implied. See the License for details. Code: Compiere ERP+CRM
+ * Copyright (C) 1999-2002 Jorg Janke, ComPiere, Inc. All Rights Reserved.
+ *************************************************************************
+ * $Id: BOM_PriceStd.sql,v 1.1 2006/04/21 17:51:58 jjanke Exp $
+ ***
+ * Title: Return Standard Price of Product/BOM
+ * Description:
+ * if not found: 0
+ ************************************************************************/
+AS
+ v_Price NUMBER;
+ v_ProductPrice NUMBER;
+ -- Get BOM Product info
+ CURSOR CUR_BOM IS
+ SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM
+ FROM M_PRODUCT_BOM b, M_PRODUCT p
+ WHERE b.M_ProductBOM_ID=p.M_Product_ID
+ AND b.M_Product_ID=Product_ID
+ AND b.M_ProductBOM_ID != Product_ID
+ AND p.IsVerified='Y'
+ AND b.IsActive='Y';
+ --
+BEGIN
+ -- Try to get price from pricelist directly
+ SELECT COALESCE(SUM(PriceStd), 0)
+ INTO v_Price
+ FROM M_PRODUCTPRICE
+ WHERE M_PriceList_Version_ID=PriceList_Version_ID AND M_Product_ID=Product_ID;
+-- DBMS_OUTPUT.PUT_LINE('Price=' || v_Price);
+
+ -- No Price - Check if BOM
+ IF (v_Price = 0) THEN
+ FOR bom IN CUR_BOM LOOP
+ v_ProductPrice := Bompricestd (bom.M_ProductBOM_ID, PriceList_Version_ID);
+ v_Price := v_Price + (bom.BOMQty * v_ProductPrice);
+ -- DBMS_OUTPUT.PUT_LINE('Price=' || v_Price);
+ END LOOP; -- BOM
+ END IF;
+ --
+ RETURN v_Price;
+END Bompricestd;
+/
+
diff --git a/migration/iD11/oracle/202406131736_IDEMPIERE-6169.sql b/migration/iD11/oracle/202406131736_IDEMPIERE-6169.sql
new file mode 100644
index 0000000000..f1564872e8
--- /dev/null
+++ b/migration/iD11/oracle/202406131736_IDEMPIERE-6169.sql
@@ -0,0 +1,12 @@
+-- IDEMPIERE-6169 Performance on AD_ChangeLog with Record_UU
+SELECT register_migration_script('202406131736_IDEMPIERE-6169.sql') FROM dual;
+
+ALTER TABLE ad_changelog DROP CONSTRAINT ad_changelog_key
+;
+
+DROP INDEX ad_changelog_key
+;
+
+ALTER TABLE ad_changelog ADD CONSTRAINT ad_changelog_pkey PRIMARY KEY (ad_session_id, ad_column_id, ad_changelog_id)
+;
+
diff --git a/migration/iD11/oracle/202406180027_IDEMPIERE-6176.sql b/migration/iD11/oracle/202406180027_IDEMPIERE-6176.sql
new file mode 100644
index 0000000000..cdc959eda6
--- /dev/null
+++ b/migration/iD11/oracle/202406180027_IDEMPIERE-6176.sql
@@ -0,0 +1,22 @@
+-- IDEMPIERE-6176 UUID indexes without constraint
+SELECT register_migration_script('202406180027_IDEMPIERE-6176.sql') FROM dual;
+
+SET SQLBLANKLINES ON
+SET DEFINE OFF
+
+-- Jun 18, 2024, 12:29:20 AM CEST
+UPDATE AD_IndexColumn SET IsActive='N',Updated=TO_TIMESTAMP('2024-06-18 00:29:20','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_IndexColumn_ID=200980
+;
+
+-- Jun 18, 2024, 12:29:27 AM CEST
+UPDATE AD_TableIndex SET IsActive='N', IsCreateConstraint='Y',Updated=TO_TIMESTAMP('2024-06-18 00:29:27','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_TableIndex_ID=200806
+;
+
+-- Jun 18, 2024, 12:30:44 AM CEST
+UPDATE AD_TableIndex SET IsCreateConstraint='Y',Updated=TO_TIMESTAMP('2024-06-18 00:30:44','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_TableIndex_ID=201272
+;
+
+-- Jun 18, 2024, 12:31:49 AM CEST
+UPDATE AD_TableIndex SET IsCreateConstraint='Y',Updated=TO_TIMESTAMP('2024-06-18 00:31:49','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_TableIndex_ID=201275
+;
+
diff --git a/migration/iD11/oracle/202407061229_IDEMPIERE-6188.sql b/migration/iD11/oracle/202407061229_IDEMPIERE-6188.sql
new file mode 100644
index 0000000000..e1d06a1f6b
--- /dev/null
+++ b/migration/iD11/oracle/202407061229_IDEMPIERE-6188.sql
@@ -0,0 +1,50 @@
+-- IDEMPIERE-6188 Read-Only Session
+SELECT register_migration_script('202407061229_IDEMPIERE-6188.sql') FROM dual;
+
+SET SQLBLANKLINES ON
+SET DEFINE OFF
+
+-- Jul 6, 2024, 12:29:56 PM CEST
+INSERT INTO AD_Element (AD_Element_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,ColumnName,Name,Description,Help,PrintName,EntityType,AD_Element_UU) VALUES (203938,0,0,'Y',TO_TIMESTAMP('2024-07-06 12:29:42','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-07-06 12:29:42','YYYY-MM-DD HH24:MI:SS'),100,'IsReadOnlySession','Read Only Session',NULL,NULL,'Read Only Session','D','6ee179e4-9573-4a3d-8815-23723ef241d7')
+;
+
+-- Jul 6, 2024, 12:30:19 PM CEST
+INSERT INTO AD_Column (AD_Column_ID,Version,Name,AD_Table_ID,ColumnName,DefaultValue,FieldLength,IsKey,IsParent,IsMandatory,IsTranslated,IsIdentifier,SeqNo,IsEncrypted,AD_Reference_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Element_ID,IsUpdateable,IsSelectionColumn,EntityType,IsSyncDatabase,IsAlwaysUpdateable,IsAutocomplete,IsAllowLogging,AD_Column_UU,IsAllowCopy,SeqNoSelection,IsToolbarButton,IsSecure,IsHtml,IsPartitionKey) VALUES (216614,0,'Read Only Session',200174,'IsReadOnlySession','N',1,'N','N','Y','N','N',0,'N',20,0,0,'Y',TO_TIMESTAMP('2024-07-06 12:30:18','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-07-06 12:30:18','YYYY-MM-DD HH24:MI:SS'),100,203938,'Y','N','D','N','N','N','Y','8c255baa-fbec-4a90-a43d-5461060368e9','Y',0,'N','N','N','N')
+;
+
+-- Jul 6, 2024, 12:30:29 PM CEST
+ALTER TABLE AD_UserPreference ADD IsReadOnlySession CHAR(1) DEFAULT 'N' CHECK (IsReadOnlySession IN ('Y','N')) NOT NULL
+;
+
+-- Jul 6, 2024, 12:30:45 PM CEST
+INSERT INTO AD_Field (AD_Field_ID,Name,AD_Tab_ID,AD_Column_ID,IsDisplayed,DisplayLength,SeqNo,IsSameLine,IsHeading,IsFieldOnly,IsEncrypted,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,IsReadOnly,IsCentrallyMaintained,EntityType,AD_Field_UU,IsDisplayedGrid,SeqNoGrid,XPosition,ColumnSpan) VALUES (208494,'Read Only Session',200189,216614,'Y',1,170,'N','N','N','N',0,0,'Y',TO_TIMESTAMP('2024-07-06 12:30:45','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-07-06 12:30:45','YYYY-MM-DD HH24:MI:SS'),100,'N','Y','D','f69131e7-4fca-4011-89d5-650ca506c920','Y',170,2,2)
+;
+
+-- Jul 6, 2024, 12:31:06 PM CEST
+UPDATE AD_Field SET SeqNo=110,Updated=TO_TIMESTAMP('2024-07-06 12:31:06','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=206133
+;
+
+-- Jul 6, 2024, 12:31:06 PM CEST
+UPDATE AD_Field SET SeqNo=120,Updated=TO_TIMESTAMP('2024-07-06 12:31:06','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=206134
+;
+
+-- Jul 6, 2024, 12:31:06 PM CEST
+UPDATE AD_Field SET SeqNo=130,Updated=TO_TIMESTAMP('2024-07-06 12:31:06','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=206407
+;
+
+-- Jul 6, 2024, 12:31:06 PM CEST
+UPDATE AD_Field SET SeqNo=140,Updated=TO_TIMESTAMP('2024-07-06 12:31:06','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=208189
+;
+
+-- Jul 6, 2024, 12:31:06 PM CEST
+UPDATE AD_Field SET IsDisplayed='Y', SeqNo=150, XPosition=5,Updated=TO_TIMESTAMP('2024-07-06 12:31:06','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=208494
+;
+
+-- Jul 6, 2024, 12:31:25 PM CEST
+UPDATE AD_Field SET IsQuickEntry='Y',Updated=TO_TIMESTAMP('2024-07-06 12:31:25','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=208494
+;
+
+-- Jul 9, 2024, 7:48:40 PM CEST
+INSERT INTO AD_Message (MsgType,MsgText,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Message_ID,Value,EntityType,AD_Message_UU) VALUES ('I','Read-only session',0,0,'Y',TO_TIMESTAMP('2024-07-09 19:48:39','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-07-09 19:48:39','YYYY-MM-DD HH24:MI:SS'),100,200900,'ReadOnlySession','D','dc819f55-662d-4d30-b68d-0a93b46a8458')
+;
+
diff --git a/migration/iD11/oracle/202407231647_IDEMPIERE-6196.sql b/migration/iD11/oracle/202407231647_IDEMPIERE-6196.sql
new file mode 100644
index 0000000000..95f96e20c9
--- /dev/null
+++ b/migration/iD11/oracle/202407231647_IDEMPIERE-6196.sql
@@ -0,0 +1,18 @@
+-- IDEMPIERE-6196
+SELECT register_migration_script('202407231647_IDEMPIERE-6196.sql') FROM dual;
+
+SET SQLBLANKLINES ON
+SET DEFINE OFF
+
+-- Jul 23, 2024, 4:47:40 PM CEST
+UPDATE AD_Column SET SeqNoSelection=30,Updated=TO_TIMESTAMP('2024-07-23 16:47:40','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=10 WHERE AD_Column_ID=103
+;
+
+-- Jul 23, 2024, 4:47:43 PM CEST
+UPDATE AD_Column SET SeqNoSelection=20,Updated=TO_TIMESTAMP('2024-07-23 16:47:43','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=10 WHERE AD_Column_ID=102
+;
+
+-- Jul 23, 2024, 4:47:52 PM CEST
+UPDATE AD_Column SET SeqNoSelection=10,Updated=TO_TIMESTAMP('2024-07-23 16:47:52','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=10 WHERE AD_Column_ID=107
+;
+
diff --git a/migration/iD11/oracle/202408191316_IDEMPIERE-6189.sql b/migration/iD11/oracle/202408191316_IDEMPIERE-6189.sql
new file mode 100644
index 0000000000..c5fefb5fa7
--- /dev/null
+++ b/migration/iD11/oracle/202408191316_IDEMPIERE-6189.sql
@@ -0,0 +1,26 @@
+-- IDEMPIERE-6189 Performance: queries on RV_C_Invoice doing unnecesary index or seq scan
+SELECT register_migration_script('202408191316_IDEMPIERE-6189.sql') FROM dual;
+
+SET SQLBLANKLINES ON
+SET DEFINE OFF
+
+-- Aug 19, 2024, 1:16:19 PM CEST
+UPDATE AD_ViewComponent SET FromClause='FROM c_invoice i
+LEFT JOIN c_doctype d ON i.c_doctype_id = d.c_doctype_id
+LEFT JOIN c_bpartner b ON i.c_bpartner_id = b.c_bpartner_id
+LEFT JOIN c_bpartner_location bpl ON i.c_bpartner_location_id = bpl.c_bpartner_location_id
+LEFT JOIN c_location loc ON bpl.c_location_id = loc.c_location_id',Updated=TO_TIMESTAMP('2024-08-19 13:16:19','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_ViewComponent_ID=200116
+;
+
+-- Aug 19, 2024, 1:16:27 PM CEST
+CREATE OR REPLACE VIEW RV_C_Invoice(C_Invoice_ID, AD_Client_ID, AD_Org_ID, IsActive, Created, CreatedBy, Updated, UpdatedBy, IsSOTrx, DocumentNo, DocStatus, DocAction, IsPrinted, IsDiscountPrinted, Processing, Processed, IsTransferred, IsPaid, C_DocType_ID, C_DocTypeTarget_ID, C_Order_ID, Description, IsApproved, SalesRep_ID, DateInvoiced, DatePrinted, DateAcct, C_BPartner_ID, C_BPartner_Location_ID, AD_User_ID, C_BP_Group_ID, POReference, DateOrdered, C_Currency_ID, C_ConversionType_ID, PaymentRule, C_PaymentTerm_ID, M_PriceList_ID, C_Campaign_ID, C_Project_ID, C_Activity_ID, IsPayScheduleValid, InvoiceCollectionType, C_Country_ID, C_Region_ID, Postal, City, C_Charge_ID, ChargeAmt, TotalLines, GrandTotal, Multiplier, C_Invoice_AD_OrgTrx_ID, C_Invoice_C_ConversionType_ID, C_DunningLevel_ID, C_Payment_ID, c_invoice_dateordered, DunningGrace, GenerateTo, IsInDispute, c_invoice_ispayschedulevalid, c_invoice_isselfservice, IsTaxIncluded, M_RMA_ID, Posted, ProcessedOn, Ref_Invoice_ID, Reversal_ID, SendEMail, User1_ID, User2_ID, c_bp_acqusitioncost, c_bp_actuallifetimevalue, c_bp_ad_language, C_BP_AD_OrgBP_ID, C_BP_AD_Org_ID, C_BP_BPartner_Parent_ID, C_BP_C_Dunning_ID, C_BP_C_Greeting_ID, C_BP_C_InvoiceSchedule_ID, C_BP_C_PaymentTerm_ID, c_bp_created, C_BP_CreatedBy, C_BP_C_TaxGroup_ID, c_bp_deliveryrule, c_bp_deliveryviarule, c_bp_description, c_bp_dunninggrace, c_bp_duns, c_bp_firstsale, c_bp_flatdiscount, c_bp_freightcostrule, c_bp_invoicerule, c_bp_isactive, c_bp_iscustomer, c_bp_isdiscountprinted, c_bp_isemployee, c_bp_ismanufacturer, c_bp_isonetime, c_bp_ispotaxexempt, c_bp_isprospect, c_bp_issalesrep, c_bp_issummary, c_bp_istaxexempt, c_bp_isvendor, C_BP_Logo_ID, C_BP_M_DiscountSchema_ID, C_BP_M_PriceList_ID, c_bp_naics, c_bp_name, c_bp_name2, c_bp_numberemployees, c_bp_paymentrule, c_bp_paymentrulepo, C_BP_PO_DiscountSchema_ID, C_BP_PO_PaymentTerm_ID, C_BP_PO_PriceList_ID, c_bp_poreference, c_bp_potentiallifetimevalue, c_bp_rating, c_bp_referenceno, C_BP_SalesRep_ID, c_bp_salesvolume, c_bp_sendemail, c_bp_shareofcustomer, c_bp_shelflifeminpct, c_bp_so_creditlimit, c_bp_socreditstatus, c_bp_so_creditused, c_bp_so_description, TaxID, c_bp_totalopenbalance, c_bp_updated, C_BP_UpdatedBy, c_bp_url, c_bp_value, C_BP_Location_AD_Org_ID, C_BP_Location_C_BPartner_ID, C_BP_Location_C_Location_ID, c_bp_location_created, C_BP_Location_CreatedBy, C_SalesRegion_ID, c_bp_location_fax, c_bp_location_isactive, IsBillTo, ISDN, IsPayFrom, IsRemitTo, IsShipTo,
+c_bp_location_name, c_bp_location_phone, c_bp_location_phone2, c_bp_location_updated, C_BP_Location_UpdatedBy, Address1, Address2, Address3, Address4, C_Location_AD_Org_ID, C_City_ID, c_location_created, C_Location_CreatedBy, c_location_isactive, Postal_Add, RegionName, c_location_updated, C_Location_UpdatedBy) AS
+SELECT i.c_invoice_id AS C_Invoice_ID, i.ad_client_id AS AD_Client_ID, i.ad_org_id AS AD_Org_ID, i.isactive AS IsActive, i.created AS Created, i.createdby AS CreatedBy, i.updated AS Updated, i.updatedby AS UpdatedBy, i.issotrx AS IsSOTrx, i.documentno AS DocumentNo, i.docstatus AS DocStatus, i.docaction AS DocAction, i.isprinted AS IsPrinted, i.isdiscountprinted AS IsDiscountPrinted, i.processing AS Processing, i.processed AS Processed, i.istransferred AS IsTransferred, i.ispaid AS IsPaid, i.c_doctype_id AS C_DocType_ID, i.c_doctypetarget_id AS C_DocTypeTarget_ID, i.c_order_id AS C_Order_ID, i.description AS Description, i.isapproved AS IsApproved, i.salesrep_id AS SalesRep_ID, i.dateinvoiced AS DateInvoiced, i.dateprinted AS DatePrinted, i.dateacct AS DateAcct, i.c_bpartner_id AS C_BPartner_ID, i.c_bpartner_location_id AS C_BPartner_Location_ID, i.ad_user_id AS AD_User_ID, b.c_bp_group_id AS C_BP_Group_ID, i.poreference AS POReference, i.dateordered AS DateOrdered, i.c_currency_id AS C_Currency_ID, i.c_conversiontype_id AS C_ConversionType_ID, i.paymentrule AS PaymentRule, i.c_paymentterm_id AS C_PaymentTerm_ID, i.m_pricelist_id AS M_PriceList_ID, i.c_campaign_id AS C_Campaign_ID, i.c_project_id AS C_Project_ID, i.c_activity_id AS C_Activity_ID, i.ispayschedulevalid AS IsPayScheduleValid, i.invoicecollectiontype AS InvoiceCollectionType, loc.c_country_id AS C_Country_ID, loc.c_region_id AS C_Region_ID, loc.postal AS Postal, loc.city AS City, i.c_charge_id AS C_Charge_ID, CASE WHEN charat(d.docbasetype, 3) = 'C' THEN i.chargeamt * '-1' ELSE i.chargeamt END AS ChargeAmt, CASE WHEN charat(d.docbasetype, 3) = 'C' THEN i.totallines * '-1' ELSE i.totallines END AS TotalLines, CASE WHEN charat(d.docbasetype, 3) = 'C' THEN i.grandtotal * '-1' ELSE i.grandtotal END AS GrandTotal, CASE WHEN charat(d.docbasetype, 3) = 'C' THEN -1 ELSE 1 END AS Multiplier, i.ad_orgtrx_id AS C_Invoice_AD_OrgTrx_ID, i.c_conversiontype_id AS C_Invoice_C_ConversionType_ID, i.c_dunninglevel_id AS C_DunningLevel_ID, i.c_payment_id AS C_Payment_ID, i.dateordered AS c_invoice_dateordered, i.dunninggrace AS DunningGrace, i.generateto AS GenerateTo, i.isindispute AS IsInDispute, i.ispayschedulevalid AS c_invoice_ispayschedulevalid, i.isselfservice AS c_invoice_isselfservice, i.istaxincluded AS IsTaxIncluded, i.m_rma_id AS M_RMA_ID, i.posted AS Posted, i.processedon AS ProcessedOn, i.ref_invoice_id AS Ref_Invoice_ID, i.reversal_id AS Reversal_ID, i.sendemail AS SendEMail,
+i.user1_id AS User1_ID, i.user2_id AS User2_ID, b.acqusitioncost AS c_bp_acqusitioncost, b.actuallifetimevalue AS c_bp_actuallifetimevalue, b.ad_language AS c_bp_ad_language, b.ad_orgbp_id AS C_BP_AD_OrgBP_ID, b.ad_org_id AS C_BP_AD_Org_ID, b.bpartner_parent_id AS C_BP_BPartner_Parent_ID, b.c_dunning_id AS C_BP_C_Dunning_ID, b.c_greeting_id AS C_BP_C_Greeting_ID, b.c_invoiceschedule_id AS C_BP_C_InvoiceSchedule_ID, b.c_paymentterm_id AS C_BP_C_PaymentTerm_ID, b.created AS c_bp_created, b.createdby AS C_BP_CreatedBy, b.c_taxgroup_id AS C_BP_C_TaxGroup_ID, b.deliveryrule AS c_bp_deliveryrule, b.deliveryviarule AS c_bp_deliveryviarule, b.description AS c_bp_description, b.dunninggrace AS c_bp_dunninggrace, b.duns AS c_bp_duns, b.firstsale AS c_bp_firstsale, b.flatdiscount AS c_bp_flatdiscount, b.freightcostrule AS c_bp_freightcostrule, b.invoicerule AS c_bp_invoicerule, b.isactive AS c_bp_isactive, b.iscustomer AS c_bp_iscustomer, b.isdiscountprinted AS c_bp_isdiscountprinted, b.isemployee AS c_bp_isemployee, b.ismanufacturer AS c_bp_ismanufacturer, b.isonetime AS c_bp_isonetime, b.ispotaxexempt AS c_bp_ispotaxexempt, b.isprospect AS c_bp_isprospect, b.issalesrep AS c_bp_issalesrep, b.issummary AS c_bp_issummary, b.istaxexempt AS c_bp_istaxexempt, b.isvendor AS c_bp_isvendor, b.logo_id AS C_BP_Logo_ID, b.m_discountschema_id AS C_BP_M_DiscountSchema_ID, b.m_pricelist_id AS C_BP_M_PriceList_ID, b.naics AS c_bp_naics, b.name AS c_bp_name, b.name2 AS c_bp_name2, b.numberemployees AS c_bp_numberemployees, b.paymentrule AS c_bp_paymentrule, b.paymentrulepo AS c_bp_paymentrulepo, b.po_discountschema_id AS C_BP_PO_DiscountSchema_ID, b.po_paymentterm_id AS C_BP_PO_PaymentTerm_ID, b.po_pricelist_id AS C_BP_PO_PriceList_ID, b.poreference AS c_bp_poreference, b.potentiallifetimevalue AS c_bp_potentiallifetimevalue, b.rating AS c_bp_rating, b.referenceno AS c_bp_referenceno, b.salesrep_id AS C_BP_SalesRep_ID, b.salesvolume AS c_bp_salesvolume, b.sendemail AS c_bp_sendemail, b.shareofcustomer AS c_bp_shareofcustomer, b.shelflifeminpct AS c_bp_shelflifeminpct, b.so_creditlimit AS c_bp_so_creditlimit, b.socreditstatus AS c_bp_socreditstatus, b.so_creditused AS c_bp_so_creditused, b.so_description AS c_bp_so_description, b.taxid AS TaxID, b.totalopenbalance AS c_bp_totalopenbalance, b.updated AS c_bp_updated, b.updatedby AS C_BP_UpdatedBy, b.url AS c_bp_url, b.value AS c_bp_value, bpl.ad_org_id AS C_BP_Location_AD_Org_ID, bpl.c_bpartner_id AS C_BP_Location_C_BPartner_ID,
+bpl.c_location_id AS C_BP_Location_C_Location_ID, bpl.created AS c_bp_location_created, bpl.createdby AS C_BP_Location_CreatedBy, bpl.c_salesregion_id AS C_SalesRegion_ID, bpl.fax AS c_bp_location_fax, bpl.isactive AS c_bp_location_isactive, bpl.isbillto AS IsBillTo, bpl.isdn AS ISDN, bpl.ispayfrom AS IsPayFrom, bpl.isremitto AS IsRemitTo, bpl.isshipto AS IsShipTo, bpl.name AS c_bp_location_name, bpl.phone AS c_bp_location_phone, bpl.phone2 AS c_bp_location_phone2, bpl.updated AS c_bp_location_updated, bpl.updatedby AS C_BP_Location_UpdatedBy, loc.address1 AS Address1, loc.address2 AS Address2, loc.address3 AS Address3, loc.address4 AS Address4, loc.ad_org_id AS C_Location_AD_Org_ID, loc.c_city_id AS C_City_ID, loc.created AS c_location_created, loc.createdby AS C_Location_CreatedBy, loc.isactive AS c_location_isactive, loc.postal_add AS Postal_Add, loc.regionname AS RegionName, loc.updated AS c_location_updated, loc.updatedby AS C_Location_UpdatedBy FROM c_invoice i
+LEFT JOIN c_doctype d ON i.c_doctype_id = d.c_doctype_id
+LEFT JOIN c_bpartner b ON i.c_bpartner_id = b.c_bpartner_id
+LEFT JOIN c_bpartner_location bpl ON i.c_bpartner_location_id = bpl.c_bpartner_location_id
+LEFT JOIN c_location loc ON bpl.c_location_id = loc.c_location_id
+;
+
diff --git a/migration/iD11/oracle/202408201236_IDEMPIERE-6216.sql b/migration/iD11/oracle/202408201236_IDEMPIERE-6216.sql
new file mode 100644
index 0000000000..1f7e7bc4fd
--- /dev/null
+++ b/migration/iD11/oracle/202408201236_IDEMPIERE-6216.sql
@@ -0,0 +1,10 @@
+-- IDEMPIERE-6216
+SELECT register_migration_script('202408201236_IDEMPIERE-6216.sql') FROM dual;
+
+SET SQLBLANKLINES ON
+SET DEFINE OFF
+
+-- Aug 20, 2024, 12:36:08 PM CEST
+UPDATE AD_Process_Para SET MandatoryLogic='@UseDefaultCoA@=N',Updated=TO_TIMESTAMP('2024-08-20 12:36:08','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Process_Para_ID=53296
+;
+
diff --git a/migration/iD11/oracle/202409041533_IDEMPIERE-6223.sql b/migration/iD11/oracle/202409041533_IDEMPIERE-6223.sql
new file mode 100644
index 0000000000..e7fe46ff9a
--- /dev/null
+++ b/migration/iD11/oracle/202409041533_IDEMPIERE-6223.sql
@@ -0,0 +1,94 @@
+-- Adding new fields to AD_PInstance and AD_PInstance_Log
+SELECT register_migration_script('202409041533_IDEMPIERE-6223.sql') FROM dual;
+
+SET SQLBLANKLINES ON
+SET DEFINE OFF
+
+-- Sep 4, 2024, 3:33:12 PM BRT
+INSERT INTO AD_Column (AD_Column_ID,Version,Name,Description,AD_Table_ID,ColumnName,FieldLength,IsKey,IsParent,IsMandatory,IsTranslated,IsIdentifier,SeqNo,IsEncrypted,AD_Reference_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Element_ID,IsUpdateable,IsSelectionColumn,EntityType,IsSyncDatabase,IsAlwaysUpdateable,IsAutocomplete,IsAllowLogging,AD_Column_UU,IsAllowCopy,SeqNoSelection,IsToolbarButton,IsSecure,FKConstraintType,IsHtml,IsPartitionKey) VALUES (216788,0,'JSON Data','The json field stores json data.',282,'JsonData',0,'N','N','N','N','N',0,'N',200267,0,0,'Y',TO_TIMESTAMP('2024-09-04 15:33:12','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-09-04 15:33:12','YYYY-MM-DD HH24:MI:SS'),100,203924,'Y','N','D','N','N','N','Y','6ec3e423-205d-482f-b7e2-3dea6b66d5d0','Y',0,'N','N','N','N','N')
+;
+
+-- Sep 4, 2024, 3:33:16 PM BRT
+ALTER TABLE AD_PInstance ADD JsonData CLOB DEFAULT NULL CONSTRAINT AD_PInstance_JsonData_isjson CHECK (JsonData IS JSON)
+;
+
+-- Sep 4, 2024, 3:33:39 PM BRT
+INSERT INTO AD_Field (AD_Field_ID,Name,Description,AD_Tab_ID,AD_Column_ID,IsDisplayed,DisplayLength,SeqNo,IsSameLine,IsHeading,IsFieldOnly,IsEncrypted,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,IsReadOnly,IsCentrallyMaintained,EntityType,AD_Field_UU,IsDisplayedGrid,SeqNoGrid,ColumnSpan,NumLines) VALUES (208511,'JSON Data','The json field stores json data.',663,216788,'Y',0,210,'N','N','N','N',0,0,'Y',TO_TIMESTAMP('2024-09-04 15:33:39','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-09-04 15:33:39','YYYY-MM-DD HH24:MI:SS'),100,'N','Y','D','2455e488-36a8-48d2-8410-2ff0808915e4','Y',200,2,5)
+;
+
+-- Sep 4, 2024, 3:34:12 PM BRT
+UPDATE AD_Field SET IsDisplayed='Y', SeqNo=110, XPosition=1, ColumnSpan=5,Updated=TO_TIMESTAMP('2024-09-04 15:34:12','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=208511
+;
+
+-- Sep 4, 2024, 3:34:12 PM BRT
+UPDATE AD_Field SET SeqNo=120,Updated=TO_TIMESTAMP('2024-09-04 15:34:12','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=10501
+;
+
+-- Sep 4, 2024, 3:34:12 PM BRT
+UPDATE AD_Field SET SeqNo=130,Updated=TO_TIMESTAMP('2024-09-04 15:34:12','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=207416
+;
+
+-- Sep 4, 2024, 3:34:12 PM BRT
+UPDATE AD_Field SET SeqNo=140,Updated=TO_TIMESTAMP('2024-09-04 15:34:12','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=10495
+;
+
+-- Sep 4, 2024, 3:34:12 PM BRT
+UPDATE AD_Field SET SeqNo=150,Updated=TO_TIMESTAMP('2024-09-04 15:34:12','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=202845
+;
+
+-- Sep 4, 2024, 3:34:12 PM BRT
+UPDATE AD_Field SET SeqNo=160,Updated=TO_TIMESTAMP('2024-09-04 15:34:12','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=202847
+;
+
+-- Sep 4, 2024, 3:34:12 PM BRT
+UPDATE AD_Field SET SeqNo=170,Updated=TO_TIMESTAMP('2024-09-04 15:34:12','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=207405
+;
+
+-- Sep 4, 2024, 3:34:12 PM BRT
+UPDATE AD_Field SET SeqNo=180,Updated=TO_TIMESTAMP('2024-09-04 15:34:12','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=207407
+;
+
+-- Sep 4, 2024, 3:34:12 PM BRT
+UPDATE AD_Field SET SeqNo=190,Updated=TO_TIMESTAMP('2024-09-04 15:34:12','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=207406
+;
+
+-- Sep 4, 2024, 3:34:12 PM BRT
+UPDATE AD_Field SET SeqNo=200,Updated=TO_TIMESTAMP('2024-09-04 15:34:12','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=207408
+;
+
+-- Sep 4, 2024, 3:34:12 PM BRT
+UPDATE AD_Field SET SeqNo=210,Updated=TO_TIMESTAMP('2024-09-04 15:34:12','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=207720
+;
+
+-- Sep 4, 2024, 3:34:40 PM BRT
+INSERT INTO AD_Column (AD_Column_ID,Version,Name,Description,AD_Table_ID,ColumnName,FieldLength,IsKey,IsParent,IsMandatory,IsTranslated,IsIdentifier,SeqNo,IsEncrypted,AD_Reference_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Element_ID,IsUpdateable,IsSelectionColumn,EntityType,IsSyncDatabase,IsAlwaysUpdateable,IsAutocomplete,IsAllowLogging,AD_Column_UU,IsAllowCopy,SeqNoSelection,IsToolbarButton,IsSecure,FKConstraintType,IsHtml,IsPartitionKey) VALUES (216789,0,'JSON Data','The json field stores json data.',578,'JsonData',0,'N','N','N','N','N',0,'N',200267,0,0,'Y',TO_TIMESTAMP('2024-09-04 15:34:40','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-09-04 15:34:40','YYYY-MM-DD HH24:MI:SS'),100,203924,'Y','N','D','N','N','N','Y','7539f67f-922d-4e27-881f-c04f0dc8c160','Y',0,'N','N','N','N','N')
+;
+
+-- Sep 4, 2024, 3:34:43 PM BRT
+ALTER TABLE AD_PInstance_Log ADD JsonData CLOB DEFAULT NULL CONSTRAINT AD_PInstance_Log_JsonData_isjson CHECK (JsonData IS JSON)
+;
+
+-- Sep 4, 2024, 3:35:54 PM BRT
+INSERT INTO AD_Field (AD_Field_ID,Name,Description,AD_Tab_ID,AD_Column_ID,IsDisplayed,DisplayLength,SeqNo,IsSameLine,IsHeading,IsFieldOnly,IsEncrypted,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,IsReadOnly,IsCentrallyMaintained,EntityType,AD_Field_UU,IsDisplayedGrid,SeqNoGrid,ColumnSpan,NumLines) VALUES (208512,'JSON Data','The json field stores json data.',665,216789,'Y',0,100,'N','N','N','N',0,0,'Y',TO_TIMESTAMP('2024-09-04 15:35:54','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-09-04 15:35:54','YYYY-MM-DD HH24:MI:SS'),100,'N','Y','D','27e958ae-9e31-4e8b-824e-e571df7da15c','Y',100,2,5)
+;
+
+-- Sep 4, 2024, 3:36:15 PM BRT
+UPDATE AD_Field SET IsDisplayed='Y', SeqNo=70, XPosition=1, ColumnSpan=5,Updated=TO_TIMESTAMP('2024-09-04 15:36:15','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=208512
+;
+
+-- Sep 4, 2024, 3:36:15 PM BRT
+UPDATE AD_Field SET SeqNo=80,Updated=TO_TIMESTAMP('2024-09-04 15:36:15','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=200309
+;
+
+-- Sep 4, 2024, 3:36:15 PM BRT
+UPDATE AD_Field SET SeqNo=90,Updated=TO_TIMESTAMP('2024-09-04 15:36:15','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=200310
+;
+
+-- Sep 4, 2024, 3:36:15 PM BRT
+UPDATE AD_Field SET SeqNo=100,Updated=TO_TIMESTAMP('2024-09-04 15:36:15','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=207622
+;
+
+-- Sep 4, 2024, 3:36:15 PM BRT
+UPDATE AD_Field SET SeqNo=0,Updated=TO_TIMESTAMP('2024-09-04 15:36:15','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=204554
+;
+
diff --git a/migration/iD11/oracle/202409141654_IDEMPIERE-5567.sql b/migration/iD11/oracle/202409141654_IDEMPIERE-5567.sql
new file mode 100644
index 0000000000..e0f93c97e0
--- /dev/null
+++ b/migration/iD11/oracle/202409141654_IDEMPIERE-5567.sql
@@ -0,0 +1,22 @@
+-- IDEMPIERE-5567 Permalink for UUID multi-key tables
+SELECT register_migration_script('202409141654_IDEMPIERE-5567.sql') FROM dual;
+
+SET SQLBLANKLINES ON
+SET DEFINE OFF
+
+-- Sep 14, 2024, 4:54:43 PM CEST
+INSERT INTO AD_ZoomCondition (AD_Client_ID,AD_Org_ID,AD_Table_ID,AD_Window_ID,AD_ZoomCondition_ID,Created,CreatedBy,IsActive,Updated,UpdatedBy,SeqNo,Name,AD_ZoomCondition_UU,ZoomLogic,EntityType) VALUES (0,0,201,111,200009,TO_TIMESTAMP('2024-09-14 16:54:42','YYYY-MM-DD HH24:MI:SS'),100,'Y',TO_TIMESTAMP('2024-09-14 16:54:42','YYYY-MM-DD HH24:MI:SS'),100,10,'Zoom to role on tenants','f8ddf8c5-4bf6-4343-a2f3-c88c1f007344','@#AD_Client_ID@>0','D')
+;
+
+-- Sep 14, 2024, 4:56:45 PM CEST
+INSERT INTO AD_ZoomCondition (AD_Client_ID,AD_Org_ID,AD_Table_ID,AD_Window_ID,AD_ZoomCondition_ID,Created,CreatedBy,IsActive,Updated,UpdatedBy,SeqNo,Name,AD_ZoomCondition_UU,ZoomLogic,EntityType) VALUES (0,0,378,111,200010,TO_TIMESTAMP('2024-09-14 16:56:45','YYYY-MM-DD HH24:MI:SS'),100,'Y',TO_TIMESTAMP('2024-09-14 16:56:45','YYYY-MM-DD HH24:MI:SS'),100,10,'Zoom to role on tenants','32bab358-373f-476a-a52c-5c700044478d','@#AD_Client_ID@>0','D')
+;
+
+-- Sep 14, 2024, 4:57:50 PM CEST
+INSERT INTO AD_ZoomCondition (AD_Client_ID,AD_Org_ID,AD_Table_ID,AD_Window_ID,AD_ZoomCondition_ID,Created,CreatedBy,IsActive,Updated,UpdatedBy,SeqNo,Name,AD_ZoomCondition_UU,ZoomLogic,EntityType) VALUES (0,0,197,111,200011,TO_TIMESTAMP('2024-09-14 16:57:50','YYYY-MM-DD HH24:MI:SS'),100,'Y',TO_TIMESTAMP('2024-09-14 16:57:50','YYYY-MM-DD HH24:MI:SS'),100,10,'Zoom to role on tenants','ebea9694-6348-444e-8b7f-f5f1c75a3c65','@#AD_Client_ID@>0','D')
+;
+
+-- Sep 14, 2024, 4:58:46 PM CEST
+INSERT INTO AD_ZoomCondition (AD_Client_ID,AD_Org_ID,AD_Table_ID,AD_Window_ID,AD_ZoomCondition_ID,Created,CreatedBy,IsActive,Updated,UpdatedBy,SeqNo,Name,AD_ZoomCondition_UU,ZoomLogic,EntityType) VALUES (0,0,199,111,200012,TO_TIMESTAMP('2024-09-14 16:58:46','YYYY-MM-DD HH24:MI:SS'),100,'Y',TO_TIMESTAMP('2024-09-14 16:58:46','YYYY-MM-DD HH24:MI:SS'),100,10,'Zoom to role on tenants','91ce33c0-fb91-4c9e-894e-8e8f49d7677f','@#AD_Client_ID@>0','D')
+;
+
diff --git a/migration/iD11/oracle/202409141807_IDEMPIERE-6007.sql b/migration/iD11/oracle/202409141807_IDEMPIERE-6007.sql
new file mode 100644
index 0000000000..b214d9f4a6
--- /dev/null
+++ b/migration/iD11/oracle/202409141807_IDEMPIERE-6007.sql
@@ -0,0 +1,10 @@
+-- IDEMPIERE-6007
+SELECT register_migration_script('202409141807_IDEMPIERE-6007.sql') FROM dual;
+
+SET SQLBLANKLINES ON
+SET DEFINE OFF
+
+-- Sep 14, 2024, 6:07:20 PM CEST
+UPDATE AD_Field SET ReadOnlyLogic='@SQL=SELECT 1 FROM AD_Field WHERE AD_Tab_ID=@AD_Tab_ID:0@',Updated=TO_TIMESTAMP('2024-09-14 18:07:20','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=131
+;
+
diff --git a/migration/iD11/oracle/202409231820_IDEMPIERE-6248.sql b/migration/iD11/oracle/202409231820_IDEMPIERE-6248.sql
new file mode 100644
index 0000000000..5872cf9c4b
--- /dev/null
+++ b/migration/iD11/oracle/202409231820_IDEMPIERE-6248.sql
@@ -0,0 +1,10 @@
+-- IDEMPIERE-6248
+SELECT register_migration_script('202409231820_IDEMPIERE-6248.sql') FROM dual;
+
+SET SQLBLANKLINES ON
+SET DEFINE OFF
+
+-- Sep 23, 2024, 6:20:30 PM CEST
+INSERT INTO AD_SysConfig (AD_SysConfig_ID,AD_Client_ID,AD_Org_ID,Created,Updated,CreatedBy,UpdatedBy,IsActive,Name,Value,Description,EntityType,ConfigurationLevel,AD_SysConfig_UU) VALUES (200250,0,0,TO_TIMESTAMP('2024-09-23 18:20:29','YYYY-MM-DD HH24:MI:SS'),TO_TIMESTAMP('2024-09-23 18:20:29','YYYY-MM-DD HH24:MI:SS'),10,10,'Y','FULL_EXCEPTION_TRACE_IN_LOG','N','If set to Y, the stack trace is not cut in the log (see https://idempiere.atlassian.net/browse/IDEMPIERE-6248)','D','S','8308ac18-d0a6-479f-ab59-7edd0bcb0b54')
+;
+
diff --git a/migration/iD11/oracle/202409231821_IDEMPIERE-6248_DelSysConfig.sql b/migration/iD11/oracle/202409231821_IDEMPIERE-6248_DelSysConfig.sql
new file mode 100644
index 0000000000..df393c1950
--- /dev/null
+++ b/migration/iD11/oracle/202409231821_IDEMPIERE-6248_DelSysConfig.sql
@@ -0,0 +1,8 @@
+-- IDEMPIERE-6248
+SELECT register_migration_script('202409231821_IDEMPIERE-6248_DelSysConfig.sql') FROM dual;
+
+SET SQLBLANKLINES ON
+SET DEFINE OFF
+
+DELETE FROM AD_SysConfig WHERE AD_SysConfig_ID = 200250
+;
diff --git a/migration/iD11/oracle/202409241248_IDEMPIERE-5760.sql b/migration/iD11/oracle/202409241248_IDEMPIERE-5760.sql
new file mode 100644
index 0000000000..1720ded3ec
--- /dev/null
+++ b/migration/iD11/oracle/202409241248_IDEMPIERE-5760.sql
@@ -0,0 +1,14 @@
+-- IDEMPIERE-5760 Manage mail.smtp.timeout using SysConfig
+SELECT register_migration_script('202409241248_IDEMPIERE-5760.sql') FROM dual;
+
+SET SQLBLANKLINES ON
+SET DEFINE OFF
+
+-- Sep 24, 2024, 12:48:56 PM CEST
+INSERT INTO AD_SysConfig (AD_SysConfig_ID,AD_Client_ID,AD_Org_ID,Created,Updated,CreatedBy,UpdatedBy,IsActive,Name,Value,Description,EntityType,ConfigurationLevel,AD_SysConfig_UU) VALUES (200253,0,0,TO_TIMESTAMP('2024-09-24 12:48:55','YYYY-MM-DD HH24:MI:SS'),TO_TIMESTAMP('2024-09-24 12:48:55','YYYY-MM-DD HH24:MI:SS'),100,100,'Y','MAIL_SMTP_CONNECTIONTIMEOUT','-1','Timeout in milliseconds to wait for SMTP connection, -1 leaves the java default','D','C','36be68b7-7b4b-4725-9a51-aeb11bb7b699')
+;
+
+-- Sep 24, 2024, 12:49:10 PM CEST
+INSERT INTO AD_SysConfig (AD_SysConfig_ID,AD_Client_ID,AD_Org_ID,Created,Updated,CreatedBy,UpdatedBy,IsActive,Name,Value,Description,EntityType,ConfigurationLevel,AD_SysConfig_UU) VALUES (200254,0,0,TO_TIMESTAMP('2024-09-24 12:49:09','YYYY-MM-DD HH24:MI:SS'),TO_TIMESTAMP('2024-09-24 12:49:09','YYYY-MM-DD HH24:MI:SS'),100,100,'Y','MAIL_SMTP_WRITETIMEOUT','-1','Timeout in milliseconds to wait for writing on SMTP connection, -1 leaves the java default','D','C','e11d83c4-8500-400a-b9d2-358c30c910fb')
+;
+
diff --git a/migration/iD11/oracle/202410071412_IDEMPIERE-6196.sql b/migration/iD11/oracle/202410071412_IDEMPIERE-6196.sql
new file mode 100644
index 0000000000..b733163f02
--- /dev/null
+++ b/migration/iD11/oracle/202410071412_IDEMPIERE-6196.sql
@@ -0,0 +1,18 @@
+-- IDEMPIERE-6196_SysConfig
+SELECT register_migration_script('202410071412_IDEMPIERE-6196.sql') FROM dual;
+
+SET SQLBLANKLINES ON
+SET DEFINE OFF
+
+-- Oct 7, 2024, 2:12:19 PM CEST
+UPDATE AD_Column SET SeqNoSelection=10,Updated=TO_TIMESTAMP('2024-10-07 14:12:19','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=10 WHERE AD_Column_ID=50195
+;
+
+-- Oct 7, 2024, 2:12:23 PM CEST
+UPDATE AD_Column SET SeqNoSelection=20,Updated=TO_TIMESTAMP('2024-10-07 14:12:23','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=10 WHERE AD_Column_ID=50197
+;
+
+-- Oct 7, 2024, 2:12:27 PM CEST
+UPDATE AD_Column SET SeqNoSelection=30,Updated=TO_TIMESTAMP('2024-10-07 14:12:27','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=10 WHERE AD_Column_ID=50196
+;
+
diff --git a/migration/iD11/oracle/202411260821_IDEMPIERE-6317.sql b/migration/iD11/oracle/202411260821_IDEMPIERE-6317.sql
new file mode 100644
index 0000000000..2a508f6f1d
--- /dev/null
+++ b/migration/iD11/oracle/202411260821_IDEMPIERE-6317.sql
@@ -0,0 +1,18 @@
+-- IDEMPIERE-6317
+SELECT register_migration_script('202411260821_IDEMPIERE-6317.sql') FROM dual;
+
+SET SQLBLANKLINES ON
+SET DEFINE OFF
+
+-- Nov 26, 2024, 8:34:46 AM BRT
+UPDATE AD_Message SET Value='Can''t Save Tenant Level',Updated=TO_TIMESTAMP('2024-11-26 08:34:46','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Message_ID=53010
+;
+
+-- Nov 26, 2024, 8:36:49 AM BRT
+INSERT INTO AD_Message (MsgType,MsgText,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Message_ID,Value,EntityType,AD_Message_UU) VALUES ('E','This is a system or tenant parameter, you can''t save it as organization parameter',0,0,'Y',TO_TIMESTAMP('2024-11-26 08:36:48','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-11-26 08:36:48','YYYY-MM-DD HH24:MI:SS'),100,200914,'ThisIsSystemOrTenantParameter','D','8980c935-1c23-4d83-8b26-70eb3a749797')
+;
+
+-- Nov 26, 2024, 8:38:56 AM BRT
+INSERT INTO AD_Message (MsgType,MsgText,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Message_ID,Value,EntityType,AD_Message_UU) VALUES ('E','This is a system parameter, you can''t save it as tenant parameter',0,0,'Y',TO_TIMESTAMP('2024-11-26 08:38:55','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-11-26 08:38:55','YYYY-MM-DD HH24:MI:SS'),100,200915,'ThisIsSystemParameter','D','6e6b2060-ec2d-4d87-b499-7250e93f7cec')
+;
+
diff --git a/migration/iD11/oracle/202411301155_IDEMPIERE-6329.sql b/migration/iD11/oracle/202411301155_IDEMPIERE-6329.sql
new file mode 100644
index 0000000000..b95223fa46
--- /dev/null
+++ b/migration/iD11/oracle/202411301155_IDEMPIERE-6329.sql
@@ -0,0 +1,676 @@
+-- IDEMPIERE-6329 Bug in BOM* SQL functions not getting the correct BOM children
+SELECT register_migration_script('202411301155_IDEMPIERE-6329.sql') FROM dual;
+
+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_Product_ID=p.M_Product_ID
+ AND b.M_Product_ID=Product_ID
+ AND b.M_ProductBOM_ID != Product_ID
+ AND p.IsVerified='Y'
+ AND b.IsActive='Y';
+ --
+BEGIN
+ -- Try to get price from PriceList directly
+ SELECT COALESCE (SUM(PriceLimit), 0)
+ INTO v_Price
+ FROM M_PRODUCTPRICE
+ WHERE IsActive='Y' AND 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_Product_ID=p.M_Product_ID
+ AND b.M_Product_ID=Product_ID
+ AND b.M_ProductBOM_ID != Product_ID
+ AND p.IsVerified='Y'
+ AND b.IsActive='Y';
+ --
+BEGIN
+ -- Try to get price from pricelist directly
+ SELECT COALESCE (SUM(PriceList), 0)
+ INTO v_Price
+ FROM M_PRODUCTPRICE
+ WHERE IsActive='Y' AND 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_Product_ID=p.M_Product_ID
+ AND b.M_Product_ID=Product_ID
+ AND b.M_ProductBOM_ID != Product_ID
+ AND p.IsVerified='Y'
+ AND b.IsActive='Y';
+ --
+BEGIN
+ -- Try to get price from pricelist directly
+ SELECT COALESCE(SUM(PriceStd), 0)
+ INTO v_Price
+ FROM M_PRODUCTPRICE
+ WHERE IsActive='Y' AND 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 BOMQTYONHANDFORRESERVATION
+(
+ 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_Product_ID=p.M_Product_ID
+ AND b.M_Product_ID=Product_ID
+ AND b.M_ProductBOM_ID != Product_ID
+ AND p.IsBOM='Y'
+ AND p.IsVerified='Y'
+ AND b.IsActive='Y';
+ --
+BEGIN
+ -- Check Parameters
+ myWarehouse_ID := Warehouse_ID;
+ IF (myWarehouse_ID IS NULL) THEN
+ IF (Locator_ID IS NULL) THEN
+ RETURN 0;
+ ELSE
+ SELECT SUM(M_Warehouse_ID) INTO myWarehouse_ID
+ FROM M_LOCATOR
+ WHERE M_Locator_ID=Locator_ID;
+ END IF;
+ END IF;
+ IF (myWarehouse_ID IS NULL) THEN
+ RETURN 0;
+ END IF;
+-- DBMS_OUTPUT.PUT_LINE('Warehouse=' || myWarehouse_ID);
+
+ -- Check, if product exists and if it is stocked
+ BEGIN
+ SELECT IsBOM, ProductType, IsStocked
+ INTO IsBOM, ProductType, IsStocked
+ FROM M_PRODUCT
+ WHERE M_Product_ID=Product_ID;
+ --
+ EXCEPTION -- not found
+ WHEN OTHERS THEN
+ RETURN 0;
+ END;
+ -- Unlimited 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_Storageonhand s
+ JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)
+ LEFT JOIN M_LocatorType lt ON (l.M_LocatorType_ID=lt.M_LocatorType_ID)
+ WHERE s.M_Product_ID=Product_ID AND l.M_Warehouse_ID=myWarehouse_ID
+ AND COALESCE(lt.IsAvailableForReservation,'Y')='Y';
+ --
+ -- DBMS_OUTPUT.PUT_LINE('Qty=' || ProductQty);
+ RETURN ProductQty;
+ END IF;
+
+ -- Go through 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_Storageonhand s
+ JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)
+ LEFT JOIN M_LocatorType lt ON (l.M_LocatorType_ID=lt.M_LocatorType_ID)
+ WHERE s.M_Product_ID=bom.M_ProductBOM_ID AND l.M_Warehouse_ID=myWarehouse_ID
+ AND COALESCE(lt.IsAvailableForReservation,'Y')='Y';
+ -- 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 := BomqtyonhandForReservation (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 BOMQTYONHANDFORRESERVATION;
+/
+
+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_Product_ID=p.M_Product_ID
+ AND b.M_Product_ID=Product_ID
+ AND b.M_ProductBOM_ID != Product_ID
+ AND p.IsBOM='Y'
+ AND p.IsVerified='Y'
+ AND b.IsActive='Y';
+ --
+BEGIN
+ -- Check Parameters
+ myWarehouse_ID := Warehouse_ID;
+ IF (myWarehouse_ID IS NULL) THEN
+ IF (Locator_ID IS NULL) THEN
+ RETURN 0;
+ ELSE
+ SELECT SUM(M_Warehouse_ID) INTO myWarehouse_ID
+ FROM M_LOCATOR
+ WHERE M_Locator_ID=Locator_ID;
+ END IF;
+ END IF;
+ IF (myWarehouse_ID IS NULL) THEN
+ RETURN 0;
+ END IF;
+-- DBMS_OUTPUT.PUT_LINE('Warehouse=' || myWarehouse_ID);
+
+ -- Check, if product exists and if it is stocked
+ BEGIN
+ SELECT IsBOM, ProductType, IsStocked
+ INTO IsBOM, ProductType, IsStocked
+ FROM M_PRODUCT
+ WHERE M_Product_ID=Product_ID;
+ --
+ EXCEPTION -- not found
+ WHEN OTHERS THEN
+ RETURN 0;
+ END;
+ -- Unlimited 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_Storageonhand s
+ JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)
+ WHERE s.M_Product_ID=Product_ID AND l.M_Warehouse_ID=myWarehouse_ID;
+ --
+ -- DBMS_OUTPUT.PUT_LINE('Qty=' || ProductQty);
+ RETURN ProductQty;
+ END IF;
+
+ -- Go through 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_Storageonhand s
+ JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)
+ WHERE s.M_Product_ID=bom.M_ProductBOM_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_Product_ID=p.M_Product_ID
+ AND b.M_Product_ID=p_Product_ID
+ AND b.M_ProductBOM_ID != p_Product_ID
+ AND p.IsBOM='Y'
+ AND p.IsVerified='Y'
+ AND b.IsActive='Y';
+ --
+BEGIN
+ -- Check Parameters
+ v_Warehouse_ID := p_Warehouse_ID;
+ IF (v_Warehouse_ID IS NULL) THEN
+ IF (p_Locator_ID IS NULL) THEN
+ RETURN 0;
+ ELSE
+ SELECT MAX(M_Warehouse_ID) INTO v_Warehouse_ID
+ FROM M_LOCATOR
+ WHERE M_Locator_ID=p_Locator_ID;
+ END IF;
+ END IF;
+ IF (v_Warehouse_ID IS NULL) THEN
+ RETURN 0;
+ END IF;
+-- DBMS_OUTPUT.PUT_LINE('Warehouse=' || v_Warehouse_ID);
+
+ -- Check, if product exists and if it is stocked
+ BEGIN
+ SELECT IsBOM, ProductType, IsStocked
+ INTO v_IsBOM, v_ProductType, v_IsStocked
+ FROM M_PRODUCT
+ WHERE M_Product_ID=p_Product_ID;
+ --
+ EXCEPTION -- not found
+ WHEN OTHERS THEN
+ RETURN 0;
+ END;
+
+ -- No reservation for non-stocked
+ IF (v_IsBOM='N' AND (v_ProductType<>'I' OR v_IsStocked='N')) THEN
+ RETURN 0;
+ -- Stocked item
+ ELSIF (v_IsStocked='Y') THEN
+ -- Get ProductQty
+ SELECT NVL(SUM(Qty), 0)
+ INTO v_ProductQty
+ FROM M_StorageReservation
+ WHERE M_Product_ID=p_Product_ID
+ AND M_Warehouse_ID=v_Warehouse_ID
+ AND IsSOTrx='N'
+ AND IsActive='Y';
+ --
+ RETURN v_ProductQty;
+ END IF;
+
+ -- Go though BOM
+-- DBMS_OUTPUT.PUT_LINE('BOM');
+ FOR bom IN CUR_BOM LOOP
+ -- Stocked Items "leaf node"
+ IF (bom.ProductType = 'I' AND bom.IsStocked = 'Y') THEN
+ -- Get ProductQty
+ SELECT NVL(SUM(Qty), 0)
+ INTO v_ProductQty
+ FROM M_StorageReservation
+ WHERE M_Product_ID=p_Product_ID
+ AND M_Warehouse_ID=v_Warehouse_ID
+ AND IsSOTrx='N'
+ AND IsActive='Y';
+ -- Get Rounding Precision
+ SELECT NVL(MAX(u.StdPrecision), 0)
+ INTO v_StdPrecision
+ FROM C_UOM u, M_PRODUCT p
+ WHERE u.C_UOM_ID=p.C_UOM_ID AND p.M_Product_ID=bom.M_ProductBOM_ID;
+ -- How much can we make with this product
+ v_ProductQty := ROUND (v_ProductQty/bom.BOMQty, v_StdPrecision);
+ -- How much can we make overall
+ IF (v_ProductQty < v_Quantity) THEN
+ v_Quantity := v_ProductQty;
+ END IF;
+ -- Another BOM
+ ELSIF (bom.IsBOM = 'Y') THEN
+ v_ProductQty := Bomqtyordered (bom.M_ProductBOM_ID, v_Warehouse_ID, p_Locator_ID);
+ -- How much can we make overall
+ IF (v_ProductQty < v_Quantity) THEN
+ v_Quantity := v_ProductQty;
+ END IF;
+ END IF;
+ END LOOP; -- BOM
+
+ -- Unlimited (e.g. only services)
+ IF (v_Quantity = 99999) THEN
+ RETURN 0;
+ END IF;
+
+ IF (v_Quantity > 0) THEN
+ -- Get Rounding Precision for Product
+ SELECT NVL(MAX(u.StdPrecision), 0)
+ INTO v_StdPrecision
+ FROM C_UOM u, M_PRODUCT p
+ WHERE u.C_UOM_ID=p.C_UOM_ID AND p.M_Product_ID=p_Product_ID;
+ --
+ RETURN ROUND (v_Quantity, v_StdPrecision);
+ END IF;
+ --
+ RETURN 0;
+END Bomqtyordered;
+/
+
+CREATE OR REPLACE FUNCTION BOMQTYRESERVED
+(
+ p_Product_ID IN NUMBER,
+ p_Warehouse_ID IN NUMBER,
+ p_Locator_ID IN NUMBER -- Only used, if warehouse is null
+)
+RETURN NUMBER
+/******************************************************************************
+ * ** Compiere Product ** Copyright (c) 1999-2001 Accorto, Inc. USA
+ * Open Source Software Provided "AS IS" without warranty or liability
+ * When you use any parts (changed or unchanged), add "Powered by Compiere" to
+ * your product name; See license details http://www.compiere.org/license.html
+ ******************************************************************************
+ * Return quantity reserved for BOM
+ */
+AS
+ v_Warehouse_ID NUMBER;
+ v_Quantity NUMBER := 99999; -- unlimited
+ v_IsBOM CHAR(1);
+ v_IsStocked CHAR(1);
+ v_ProductType CHAR(1);
+ v_ProductQty NUMBER;
+ v_StdPrecision NUMBER;
+ -- Get BOM Product info
+ CURSOR CUR_BOM IS
+ SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM, p.IsStocked, p.ProductType
+ FROM M_PRODUCT_BOM b, M_PRODUCT p
+ WHERE b.M_Product_ID=p.M_Product_ID
+ AND b.M_Product_ID=p_Product_ID
+ AND b.M_ProductBOM_ID != p_Product_ID
+ AND p.IsBOM='Y'
+ AND p.IsVerified='Y'
+ AND b.IsActive='Y';
+ --
+BEGIN
+ -- Check Parameters
+ v_Warehouse_ID := p_Warehouse_ID;
+ IF (v_Warehouse_ID IS NULL) THEN
+ IF (p_Locator_ID IS NULL) THEN
+ RETURN 0;
+ ELSE
+ SELECT MAX(M_Warehouse_ID) INTO v_Warehouse_ID
+ FROM M_LOCATOR
+ WHERE M_Locator_ID=p_Locator_ID;
+ END IF;
+ END IF;
+ IF (v_Warehouse_ID IS NULL) THEN
+ RETURN 0;
+ END IF;
+-- DBMS_OUTPUT.PUT_LINE('Warehouse=' || v_Warehouse_ID);
+
+ -- Check, if product exists and if it is stocked
+ BEGIN
+ SELECT IsBOM, ProductType, IsStocked
+ INTO v_IsBOM, v_ProductType, v_IsStocked
+ FROM M_PRODUCT
+ WHERE M_Product_ID=p_Product_ID;
+ --
+ EXCEPTION -- not found
+ WHEN OTHERS THEN
+ RETURN 0;
+ END;
+
+ -- No reservation for non-stocked
+ IF (v_IsBOM='N' AND (v_ProductType<>'I' OR v_IsStocked='N')) THEN
+ RETURN 0;
+ -- Stocked item
+ ELSIF (v_IsStocked='Y') THEN
+ -- Get ProductQty
+ SELECT NVL(SUM(Qty), 0)
+ INTO v_ProductQty
+ FROM M_StorageReservation
+ WHERE M_Product_ID=p_Product_ID
+ AND M_Warehouse_ID=v_Warehouse_ID
+ AND IsSOTrx='Y'
+ AND IsActive='Y';
+ --
+ RETURN v_ProductQty;
+ END IF;
+
+ -- Go though BOM
+-- DBMS_OUTPUT.PUT_LINE('BOM');
+ FOR bom IN CUR_BOM LOOP
+ -- Stocked Items "leaf node"
+ IF (bom.ProductType = 'I' AND bom.IsStocked = 'Y') THEN
+ -- Get ProductQty
+ SELECT NVL(SUM(Qty), 0)
+ INTO v_ProductQty
+ FROM M_StorageReservation
+ WHERE M_Product_ID=bom.M_ProductBOM_ID
+ AND M_Warehouse_ID=v_Warehouse_ID
+ AND IsSOTrx='Y'
+ AND IsActive='Y';
+ -- Get Rounding Precision
+ SELECT NVL(MAX(u.StdPrecision), 0)
+ INTO v_StdPrecision
+ FROM C_UOM u, M_PRODUCT p
+ WHERE u.C_UOM_ID=p.C_UOM_ID AND p.M_Product_ID=bom.M_ProductBOM_ID;
+ -- How much can we make with this product
+ v_ProductQty := ROUND (v_ProductQty/bom.BOMQty, v_StdPrecision);
+ -- How much can we make overall
+ IF (v_ProductQty < v_Quantity) THEN
+ v_Quantity := v_ProductQty;
+ END IF;
+ -- Another BOM
+ ELSIF (bom.IsBOM = 'Y') THEN
+ v_ProductQty := Bomqtyreserved (bom.M_ProductBOM_ID, v_Warehouse_ID, p_Locator_ID);
+ -- How much can we make overall
+ IF (v_ProductQty < v_Quantity) THEN
+ v_Quantity := v_ProductQty;
+ END IF;
+ END IF;
+ END LOOP; -- BOM
+
+ -- Unlimited (e.g. only services)
+ IF (v_Quantity = 99999) THEN
+ RETURN 0;
+ END IF;
+
+ IF (v_Quantity > 0) THEN
+ -- Get Rounding Precision for Product
+ SELECT NVL(MAX(u.StdPrecision), 0)
+ INTO v_StdPrecision
+ FROM C_UOM u, M_PRODUCT p
+ WHERE u.C_UOM_ID=p.C_UOM_ID AND p.M_Product_ID=p_Product_ID;
+ --
+ RETURN ROUND (v_Quantity, v_StdPrecision);
+ END IF;
+ RETURN 0;
+END Bomqtyreserved;
+/
+
diff --git a/migration/iD11/oracle/202412021125_IDEMPIERE-6327.sql b/migration/iD11/oracle/202412021125_IDEMPIERE-6327.sql
new file mode 100644
index 0000000000..b2d9f52249
--- /dev/null
+++ b/migration/iD11/oracle/202412021125_IDEMPIERE-6327.sql
@@ -0,0 +1,14 @@
+-- IDEMPIERE-6327 Interest Area Window: Subscription tab should not enable "Insert Record"
+SELECT register_migration_script('202412021125_IDEMPIERE-6327.sql') FROM dual;
+
+SET SQLBLANKLINES ON
+SET DEFINE OFF
+
+-- Dec 2, 2024, 11:25:16 AM MYT
+UPDATE AD_Tab SET IsInsertRecord='N',Updated=TO_TIMESTAMP('2024-12-02 11:25:16','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Tab_ID=536
+;
+
+-- Dec 2, 2024, 11:25:32 AM MYT
+UPDATE AD_Field SET IsReadOnly='Y',Updated=TO_TIMESTAMP('2024-12-02 11:25:32','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=7625
+;
+
diff --git a/migration/iD11/oracle/202412041755_IDEMPIERE-6334.sql b/migration/iD11/oracle/202412041755_IDEMPIERE-6334.sql
new file mode 100644
index 0000000000..679a90a59e
--- /dev/null
+++ b/migration/iD11/oracle/202412041755_IDEMPIERE-6334.sql
@@ -0,0 +1,10 @@
+--
+SELECT register_migration_script('202412041755_IDEMPIERE-6334.sql') FROM dual;
+
+SET SQLBLANKLINES ON
+SET DEFINE OFF
+
+-- Dec 4, 2024, 5:55:11 PM CET
+UPDATE AD_Tab SET IsAdvancedTab='Y',Updated=TO_TIMESTAMP('2024-12-04 17:55:11','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Tab_ID=200329
+;
+
diff --git a/migration/iD11/oracle/202412091327_IDEMPIERE-6329.sql b/migration/iD11/oracle/202412091327_IDEMPIERE-6329.sql
new file mode 100644
index 0000000000..574576593d
--- /dev/null
+++ b/migration/iD11/oracle/202412091327_IDEMPIERE-6329.sql
@@ -0,0 +1,3245 @@
+-- IDEMPIERE-6329 Bug in BOM* SQL functions not getting the correct BOM children
+SELECT register_migration_script('202412091327_IDEMPIERE-6329.sql') FROM dual;
+
+CREATE OR REPLACE FUNCTION BOMPRICELIMIT
+(
+ Product_ID IN NUMBER,
+ PriceList_Version_ID IN NUMBER
+)
+RETURN NUMBER
+/*************************************************************************
+ * The contents of this file are subject to the Compiere License. You may
+ * obtain a copy of the License at http://www.compiere.org/license.html
+ * Software is on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
+ * express or implied. See the License for details. Code: Compiere ERP+CRM
+ * Copyright (C) 1999-2002 Jorg Janke, ComPiere, Inc. All Rights Reserved.
+ *************************************************************************
+ * $Id: BOM_PriceLimit.sql,v 1.1 2006/04/21 17:51:58 jjanke Exp $
+ ***
+ * Title: Return Limit Price of Product/BOM
+ * Description:
+ * if not found: 0
+ ************************************************************************/
+AS
+ v_Price NUMBER;
+ v_ProductPrice NUMBER;
+ -- Get BOM Product info
+ CURSOR CUR_BOM IS
+ SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM
+ FROM M_PRODUCT_BOM b, M_PRODUCT p
+ WHERE b.M_ProductBOM_ID=p.M_Product_ID
+ AND b.M_Product_ID=Product_ID
+ AND b.M_ProductBOM_ID != Product_ID
+ AND (p.IsBOM='N' OR p.IsVerified='Y')
+ AND b.IsActive='Y';
+ --
+BEGIN
+ -- Try to get price from PriceList directly
+ SELECT COALESCE (SUM(PriceLimit), 0)
+ INTO v_Price
+ FROM M_PRODUCTPRICE
+ WHERE IsActive='Y' AND M_PriceList_Version_ID=PriceList_Version_ID AND M_Product_ID=Product_ID;
+-- DBMS_OUTPUT.PUT_LINE('Price=' || v_Price);
+
+ -- No Price - Check if BOM
+ IF (v_Price = 0) THEN
+ FOR bom IN CUR_BOM LOOP
+ v_ProductPrice := Bompricelimit (bom.M_ProductBOM_ID, PriceList_Version_ID);
+ v_Price := v_Price + (bom.BOMQty * v_ProductPrice);
+ END LOOP;
+ END IF;
+ --
+ RETURN v_Price;
+END Bompricelimit;
+/
+
+CREATE OR REPLACE FUNCTION BOMPRICELIST
+(
+ Product_ID IN NUMBER,
+ PriceList_Version_ID IN NUMBER
+)
+RETURN NUMBER
+/*************************************************************************
+ * The contents of this file are subject to the Compiere License. You may
+ * obtain a copy of the License at http://www.compiere.org/license.html
+ * Software is on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
+ * express or implied. See the License for details. Code: Compiere ERP+CRM
+ * Copyright (C) 1999-2002 Jorg Janke, ComPiere, Inc. All Rights Reserved.
+ *************************************************************************
+ * $Id: BOM_PriceList.sql,v 1.1 2006/04/21 17:51:58 jjanke Exp $
+ ***
+ * Title: Return List Price of Product/BOM
+ * Description:
+ * if not found: 0
+ ************************************************************************/
+AS
+ v_Price NUMBER;
+ v_ProductPrice NUMBER;
+ -- Get BOM Product info
+ CURSOR CUR_BOM IS
+ SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM
+ FROM M_PRODUCT_BOM b, M_PRODUCT p
+ WHERE b.M_ProductBOM_ID=p.M_Product_ID
+ AND b.M_Product_ID=Product_ID
+ AND b.M_ProductBOM_ID != Product_ID
+ AND (p.IsBOM='N' OR p.IsVerified='Y')
+ AND b.IsActive='Y';
+ --
+BEGIN
+ -- Try to get price from pricelist directly
+ SELECT COALESCE (SUM(PriceList), 0)
+ INTO v_Price
+ FROM M_PRODUCTPRICE
+ WHERE IsActive='Y' AND M_PriceList_Version_ID=PriceList_Version_ID AND M_Product_ID=Product_ID;
+-- DBMS_OUTPUT.PUT_LINE('Price=' || Price);
+
+ -- No Price - Check if BOM
+ IF (v_Price = 0) THEN
+ FOR bom IN CUR_BOM LOOP
+ v_ProductPrice := Bompricelist (bom.M_ProductBOM_ID, PriceList_Version_ID);
+ v_Price := v_Price + (bom.BOMQty * v_ProductPrice);
+ -- DBMS_OUTPUT.PUT_LINE('Qry=' || bom.BOMQty || ' @ ' || v_ProductPrice || ', Price=' || v_Price);
+ END LOOP; -- BOM
+ END IF;
+ --
+ RETURN v_Price;
+END Bompricelist;
+/
+
+CREATE OR REPLACE FUNCTION BOMPRICESTD
+(
+ Product_ID IN NUMBER,
+ PriceList_Version_ID IN NUMBER
+)
+RETURN NUMBER
+/*************************************************************************
+ * The contents of this file are subject to the Compiere License. You may
+ * obtain a copy of the License at http://www.compiere.org/license.html
+ * Software is on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
+ * express or implied. See the License for details. Code: Compiere ERP+CRM
+ * Copyright (C) 1999-2002 Jorg Janke, ComPiere, Inc. All Rights Reserved.
+ *************************************************************************
+ * $Id: BOM_PriceStd.sql,v 1.1 2006/04/21 17:51:58 jjanke Exp $
+ ***
+ * Title: Return Standard Price of Product/BOM
+ * Description:
+ * if not found: 0
+ ************************************************************************/
+AS
+ v_Price NUMBER;
+ v_ProductPrice NUMBER;
+ -- Get BOM Product info
+ CURSOR CUR_BOM IS
+ SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM
+ FROM M_PRODUCT_BOM b, M_PRODUCT p
+ WHERE b.M_ProductBOM_ID=p.M_Product_ID
+ AND b.M_Product_ID=Product_ID
+ AND b.M_ProductBOM_ID != Product_ID
+ AND (p.IsBOM='N' OR p.IsVerified='Y')
+ AND b.IsActive='Y';
+ --
+BEGIN
+ -- Try to get price from pricelist directly
+ SELECT COALESCE(SUM(PriceStd), 0)
+ INTO v_Price
+ FROM M_PRODUCTPRICE
+ WHERE IsActive='Y' AND 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 BOMQTYONHANDFORRESERVATION
+(
+ 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, p.IsVerified
+ FROM M_PRODUCT_BOM b, M_PRODUCT p
+ WHERE b.M_ProductBOM_ID=p.M_Product_ID
+ AND b.M_Product_ID=Product_ID
+ AND b.M_ProductBOM_ID != Product_ID
+ AND b.IsActive='Y';
+ --
+BEGIN
+ -- Check Parameters
+ myWarehouse_ID := Warehouse_ID;
+ IF (myWarehouse_ID IS NULL) THEN
+ IF (Locator_ID IS NULL) THEN
+ RETURN 0;
+ ELSE
+ SELECT SUM(M_Warehouse_ID) INTO myWarehouse_ID
+ FROM M_LOCATOR
+ WHERE M_Locator_ID=Locator_ID;
+ END IF;
+ END IF;
+ IF (myWarehouse_ID IS NULL) THEN
+ RETURN 0;
+ END IF;
+-- DBMS_OUTPUT.PUT_LINE('Warehouse=' || myWarehouse_ID);
+
+ -- Check, if product exists and if it is stocked
+ BEGIN
+ SELECT IsBOM, ProductType, IsStocked
+ INTO IsBOM, ProductType, IsStocked
+ FROM M_PRODUCT
+ WHERE M_Product_ID=Product_ID;
+ --
+ EXCEPTION -- not found
+ WHEN OTHERS THEN
+ RETURN 0;
+ END;
+ -- Unlimited 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_Storageonhand s
+ JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)
+ LEFT JOIN M_LocatorType lt ON (l.M_LocatorType_ID=lt.M_LocatorType_ID)
+ WHERE s.M_Product_ID=Product_ID AND l.M_Warehouse_ID=myWarehouse_ID
+ AND COALESCE(lt.IsAvailableForReservation,'Y')='Y';
+ --
+ -- DBMS_OUTPUT.PUT_LINE('Qty=' || ProductQty);
+ RETURN ProductQty;
+ END IF;
+
+ -- Go through 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_Storageonhand s
+ JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)
+ LEFT JOIN M_LocatorType lt ON (l.M_LocatorType_ID=lt.M_LocatorType_ID)
+ WHERE s.M_Product_ID=bom.M_ProductBOM_ID AND l.M_Warehouse_ID=myWarehouse_ID
+ AND COALESCE(lt.IsAvailableForReservation,'Y')='Y';
+ -- 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' AND bom.IsVerified = 'Y') THEN
+ ProductQty := BomqtyonhandForReservation (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 BOMQTYONHANDFORRESERVATION;
+/
+
+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, p.IsVerified
+ FROM M_PRODUCT_BOM b, M_PRODUCT p
+ WHERE b.M_ProductBOM_ID=p.M_Product_ID
+ AND b.M_Product_ID=Product_ID
+ AND b.M_ProductBOM_ID != Product_ID
+ AND b.IsActive='Y';
+ --
+BEGIN
+ -- Check Parameters
+ myWarehouse_ID := Warehouse_ID;
+ IF (myWarehouse_ID IS NULL) THEN
+ IF (Locator_ID IS NULL) THEN
+ RETURN 0;
+ ELSE
+ SELECT SUM(M_Warehouse_ID) INTO myWarehouse_ID
+ FROM M_LOCATOR
+ WHERE M_Locator_ID=Locator_ID;
+ END IF;
+ END IF;
+ IF (myWarehouse_ID IS NULL) THEN
+ RETURN 0;
+ END IF;
+-- DBMS_OUTPUT.PUT_LINE('Warehouse=' || myWarehouse_ID);
+
+ -- Check, if product exists and if it is stocked
+ BEGIN
+ SELECT IsBOM, ProductType, IsStocked
+ INTO IsBOM, ProductType, IsStocked
+ FROM M_PRODUCT
+ WHERE M_Product_ID=Product_ID;
+ --
+ EXCEPTION -- not found
+ WHEN OTHERS THEN
+ RETURN 0;
+ END;
+ -- Unlimited 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_Storageonhand s
+ JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)
+ WHERE s.M_Product_ID=Product_ID AND l.M_Warehouse_ID=myWarehouse_ID;
+ --
+ -- DBMS_OUTPUT.PUT_LINE('Qty=' || ProductQty);
+ RETURN ProductQty;
+ END IF;
+
+ -- Go through 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_Storageonhand s
+ JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)
+ WHERE s.M_Product_ID=bom.M_ProductBOM_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' AND BOM.IsVerified = '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, p.IsVerified
+ FROM M_PRODUCT_BOM b, M_PRODUCT p
+ WHERE b.M_ProductBOM_ID=p.M_Product_ID
+ AND b.M_Product_ID=p_Product_ID
+ AND b.M_ProductBOM_ID != p_Product_ID
+ AND b.IsActive='Y';
+ --
+BEGIN
+ -- Check Parameters
+ v_Warehouse_ID := p_Warehouse_ID;
+ IF (v_Warehouse_ID IS NULL) THEN
+ IF (p_Locator_ID IS NULL) THEN
+ RETURN 0;
+ ELSE
+ SELECT MAX(M_Warehouse_ID) INTO v_Warehouse_ID
+ FROM M_LOCATOR
+ WHERE M_Locator_ID=p_Locator_ID;
+ END IF;
+ END IF;
+ IF (v_Warehouse_ID IS NULL) THEN
+ RETURN 0;
+ END IF;
+-- DBMS_OUTPUT.PUT_LINE('Warehouse=' || v_Warehouse_ID);
+
+ -- Check, if product exists and if it is stocked
+ BEGIN
+ SELECT IsBOM, ProductType, IsStocked
+ INTO v_IsBOM, v_ProductType, v_IsStocked
+ FROM M_PRODUCT
+ WHERE M_Product_ID=p_Product_ID;
+ --
+ EXCEPTION -- not found
+ WHEN OTHERS THEN
+ RETURN 0;
+ END;
+
+ -- No reservation for non-stocked
+ IF (v_IsBOM='N' AND (v_ProductType<>'I' OR v_IsStocked='N')) THEN
+ RETURN 0;
+ -- Stocked item
+ ELSIF (v_IsStocked='Y') THEN
+ -- Get ProductQty
+ SELECT NVL(SUM(Qty), 0)
+ INTO v_ProductQty
+ FROM M_StorageReservation
+ WHERE M_Product_ID=p_Product_ID
+ AND M_Warehouse_ID=v_Warehouse_ID
+ AND IsSOTrx='N'
+ AND IsActive='Y';
+ --
+ RETURN v_ProductQty;
+ END IF;
+
+ -- Go though BOM
+-- DBMS_OUTPUT.PUT_LINE('BOM');
+ FOR bom IN CUR_BOM LOOP
+ -- Stocked Items "leaf node"
+ IF (bom.ProductType = 'I' AND bom.IsStocked = 'Y') THEN
+ -- Get ProductQty
+ SELECT NVL(SUM(Qty), 0)
+ INTO v_ProductQty
+ FROM M_StorageReservation
+ WHERE M_Product_ID=p_Product_ID
+ AND M_Warehouse_ID=v_Warehouse_ID
+ AND IsSOTrx='N'
+ AND IsActive='Y';
+ -- Get Rounding Precision
+ SELECT NVL(MAX(u.StdPrecision), 0)
+ INTO v_StdPrecision
+ FROM C_UOM u, M_PRODUCT p
+ WHERE u.C_UOM_ID=p.C_UOM_ID AND p.M_Product_ID=bom.M_ProductBOM_ID;
+ -- How much can we make with this product
+ v_ProductQty := ROUND (v_ProductQty/bom.BOMQty, v_StdPrecision);
+ -- How much can we make overall
+ IF (v_ProductQty < v_Quantity) THEN
+ v_Quantity := v_ProductQty;
+ END IF;
+ -- Another BOM
+ ELSIF (bom.IsBOM = 'Y' AND BOM.IsVerified = '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, p.IsVerified
+ FROM M_PRODUCT_BOM b, M_PRODUCT p
+ WHERE b.M_ProductBOM_ID=p.M_Product_ID
+ AND b.M_Product_ID=p_Product_ID
+ AND b.M_ProductBOM_ID != p_Product_ID
+ AND b.IsActive='Y';
+ --
+BEGIN
+ -- Check Parameters
+ v_Warehouse_ID := p_Warehouse_ID;
+ IF (v_Warehouse_ID IS NULL) THEN
+ IF (p_Locator_ID IS NULL) THEN
+ RETURN 0;
+ ELSE
+ SELECT MAX(M_Warehouse_ID) INTO v_Warehouse_ID
+ FROM M_LOCATOR
+ WHERE M_Locator_ID=p_Locator_ID;
+ END IF;
+ END IF;
+ IF (v_Warehouse_ID IS NULL) THEN
+ RETURN 0;
+ END IF;
+-- DBMS_OUTPUT.PUT_LINE('Warehouse=' || v_Warehouse_ID);
+
+ -- Check, if product exists and if it is stocked
+ BEGIN
+ SELECT IsBOM, ProductType, IsStocked
+ INTO v_IsBOM, v_ProductType, v_IsStocked
+ FROM M_PRODUCT
+ WHERE M_Product_ID=p_Product_ID;
+ --
+ EXCEPTION -- not found
+ WHEN OTHERS THEN
+ RETURN 0;
+ END;
+
+ -- No reservation for non-stocked
+ IF (v_IsBOM='N' AND (v_ProductType<>'I' OR v_IsStocked='N')) THEN
+ RETURN 0;
+ -- Stocked item
+ ELSIF (v_IsStocked='Y') THEN
+ -- Get ProductQty
+ SELECT NVL(SUM(Qty), 0)
+ INTO v_ProductQty
+ FROM M_StorageReservation
+ WHERE M_Product_ID=p_Product_ID
+ AND M_Warehouse_ID=v_Warehouse_ID
+ AND IsSOTrx='Y'
+ AND IsActive='Y';
+ --
+ RETURN v_ProductQty;
+ END IF;
+
+ -- Go though BOM
+-- DBMS_OUTPUT.PUT_LINE('BOM');
+ FOR bom IN CUR_BOM LOOP
+ -- Stocked Items "leaf node"
+ IF (bom.ProductType = 'I' AND bom.IsStocked = 'Y') THEN
+ -- Get ProductQty
+ SELECT NVL(SUM(Qty), 0)
+ INTO v_ProductQty
+ FROM M_StorageReservation
+ WHERE M_Product_ID=bom.M_ProductBOM_ID
+ AND M_Warehouse_ID=v_Warehouse_ID
+ AND IsSOTrx='Y'
+ AND IsActive='Y';
+ -- Get Rounding Precision
+ SELECT NVL(MAX(u.StdPrecision), 0)
+ INTO v_StdPrecision
+ FROM C_UOM u, M_PRODUCT p
+ WHERE u.C_UOM_ID=p.C_UOM_ID AND p.M_Product_ID=bom.M_ProductBOM_ID;
+ -- How much can we make with this product
+ v_ProductQty := ROUND (v_ProductQty/bom.BOMQty, v_StdPrecision);
+ -- How much can we make overall
+ IF (v_ProductQty < v_Quantity) THEN
+ v_Quantity := v_ProductQty;
+ END IF;
+ -- Another BOM
+ ELSIF (bom.IsBOM = 'Y' AND BOM.IsVerified = '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 BOMPRICELIMIT
+(
+ Product_ID IN NUMBER,
+ PriceList_Version_ID IN NUMBER
+)
+RETURN NUMBER
+/*************************************************************************
+ * The contents of this file are subject to the Compiere License. You may
+ * obtain a copy of the License at http://www.compiere.org/license.html
+ * Software is on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
+ * express or implied. See the License for details. Code: Compiere ERP+CRM
+ * Copyright (C) 1999-2002 Jorg Janke, ComPiere, Inc. All Rights Reserved.
+ *************************************************************************
+ * $Id: BOM_PriceLimit.sql,v 1.1 2006/04/21 17:51:58 jjanke Exp $
+ ***
+ * Title: Return Limit Price of Product/BOM
+ * Description:
+ * if not found: 0
+ ************************************************************************/
+AS
+ v_Price NUMBER;
+ v_ProductPrice NUMBER;
+ -- Get BOM Product info
+ CURSOR CUR_BOM IS
+ SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM
+ FROM M_PRODUCT_BOM b, M_PRODUCT p
+ WHERE b.M_ProductBOM_ID=p.M_Product_ID
+ AND b.M_Product_ID=Product_ID
+ AND b.M_ProductBOM_ID != Product_ID
+ AND (p.IsBOM='N' OR p.IsVerified='Y')
+ AND b.IsActive='Y';
+ --
+BEGIN
+ -- Try to get price from PriceList directly
+ SELECT COALESCE (SUM(PriceLimit), 0)
+ INTO v_Price
+ FROM M_PRODUCTPRICE
+ WHERE IsActive='Y' AND M_PriceList_Version_ID=PriceList_Version_ID AND M_Product_ID=Product_ID;
+-- DBMS_OUTPUT.PUT_LINE('Price=' || v_Price);
+
+ -- No Price - Check if BOM
+ IF (v_Price = 0) THEN
+ FOR bom IN CUR_BOM LOOP
+ v_ProductPrice := Bompricelimit (bom.M_ProductBOM_ID, PriceList_Version_ID);
+ v_Price := v_Price + (bom.BOMQty * v_ProductPrice);
+ END LOOP;
+ END IF;
+ --
+ RETURN v_Price;
+END Bompricelimit;
+/
+
+CREATE OR REPLACE FUNCTION BOMPRICELIST
+(
+ Product_ID IN NUMBER,
+ PriceList_Version_ID IN NUMBER
+)
+RETURN NUMBER
+/*************************************************************************
+ * The contents of this file are subject to the Compiere License. You may
+ * obtain a copy of the License at http://www.compiere.org/license.html
+ * Software is on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
+ * express or implied. See the License for details. Code: Compiere ERP+CRM
+ * Copyright (C) 1999-2002 Jorg Janke, ComPiere, Inc. All Rights Reserved.
+ *************************************************************************
+ * $Id: BOM_PriceList.sql,v 1.1 2006/04/21 17:51:58 jjanke Exp $
+ ***
+ * Title: Return List Price of Product/BOM
+ * Description:
+ * if not found: 0
+ ************************************************************************/
+AS
+ v_Price NUMBER;
+ v_ProductPrice NUMBER;
+ -- Get BOM Product info
+ CURSOR CUR_BOM IS
+ SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM
+ FROM M_PRODUCT_BOM b, M_PRODUCT p
+ WHERE b.M_ProductBOM_ID=p.M_Product_ID
+ AND b.M_Product_ID=Product_ID
+ AND b.M_ProductBOM_ID != Product_ID
+ AND (p.IsBOM='N' OR p.IsVerified='Y')
+ AND b.IsActive='Y';
+ --
+BEGIN
+ -- Try to get price from pricelist directly
+ SELECT COALESCE (SUM(PriceList), 0)
+ INTO v_Price
+ FROM M_PRODUCTPRICE
+ WHERE IsActive='Y' AND M_PriceList_Version_ID=PriceList_Version_ID AND M_Product_ID=Product_ID;
+-- DBMS_OUTPUT.PUT_LINE('Price=' || Price);
+
+ -- No Price - Check if BOM
+ IF (v_Price = 0) THEN
+ FOR bom IN CUR_BOM LOOP
+ v_ProductPrice := Bompricelist (bom.M_ProductBOM_ID, PriceList_Version_ID);
+ v_Price := v_Price + (bom.BOMQty * v_ProductPrice);
+ -- DBMS_OUTPUT.PUT_LINE('Qry=' || bom.BOMQty || ' @ ' || v_ProductPrice || ', Price=' || v_Price);
+ END LOOP; -- BOM
+ END IF;
+ --
+ RETURN v_Price;
+END Bompricelist;
+/
+
+CREATE OR REPLACE FUNCTION BOMPRICESTD
+(
+ Product_ID IN NUMBER,
+ PriceList_Version_ID IN NUMBER
+)
+RETURN NUMBER
+/*************************************************************************
+ * The contents of this file are subject to the Compiere License. You may
+ * obtain a copy of the License at http://www.compiere.org/license.html
+ * Software is on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
+ * express or implied. See the License for details. Code: Compiere ERP+CRM
+ * Copyright (C) 1999-2002 Jorg Janke, ComPiere, Inc. All Rights Reserved.
+ *************************************************************************
+ * $Id: BOM_PriceStd.sql,v 1.1 2006/04/21 17:51:58 jjanke Exp $
+ ***
+ * Title: Return Standard Price of Product/BOM
+ * Description:
+ * if not found: 0
+ ************************************************************************/
+AS
+ v_Price NUMBER;
+ v_ProductPrice NUMBER;
+ -- Get BOM Product info
+ CURSOR CUR_BOM IS
+ SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM
+ FROM M_PRODUCT_BOM b, M_PRODUCT p
+ WHERE b.M_ProductBOM_ID=p.M_Product_ID
+ AND b.M_Product_ID=Product_ID
+ AND b.M_ProductBOM_ID != Product_ID
+ AND (p.IsBOM='N' OR p.IsVerified='Y')
+ AND b.IsActive='Y';
+ --
+BEGIN
+ -- Try to get price from pricelist directly
+ SELECT COALESCE(SUM(PriceStd), 0)
+ INTO v_Price
+ FROM M_PRODUCTPRICE
+ WHERE IsActive='Y' AND 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 BOMQTYONHANDFORRESERVATION
+(
+ 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, p.IsVerified
+ FROM M_PRODUCT_BOM b, M_PRODUCT p
+ WHERE b.M_ProductBOM_ID=p.M_Product_ID
+ AND b.M_Product_ID=Product_ID
+ AND b.M_ProductBOM_ID != Product_ID
+ AND b.IsActive='Y';
+ --
+BEGIN
+ -- Check Parameters
+ myWarehouse_ID := Warehouse_ID;
+ IF (myWarehouse_ID IS NULL) THEN
+ IF (Locator_ID IS NULL) THEN
+ RETURN 0;
+ ELSE
+ SELECT SUM(M_Warehouse_ID) INTO myWarehouse_ID
+ FROM M_LOCATOR
+ WHERE M_Locator_ID=Locator_ID;
+ END IF;
+ END IF;
+ IF (myWarehouse_ID IS NULL) THEN
+ RETURN 0;
+ END IF;
+-- DBMS_OUTPUT.PUT_LINE('Warehouse=' || myWarehouse_ID);
+
+ -- Check, if product exists and if it is stocked
+ BEGIN
+ SELECT IsBOM, ProductType, IsStocked
+ INTO IsBOM, ProductType, IsStocked
+ FROM M_PRODUCT
+ WHERE M_Product_ID=Product_ID;
+ --
+ EXCEPTION -- not found
+ WHEN OTHERS THEN
+ RETURN 0;
+ END;
+ -- Unlimited 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_Storageonhand s
+ JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)
+ LEFT JOIN M_LocatorType lt ON (l.M_LocatorType_ID=lt.M_LocatorType_ID)
+ WHERE s.M_Product_ID=Product_ID AND l.M_Warehouse_ID=myWarehouse_ID
+ AND COALESCE(lt.IsAvailableForReservation,'Y')='Y';
+ --
+ -- DBMS_OUTPUT.PUT_LINE('Qty=' || ProductQty);
+ RETURN ProductQty;
+ END IF;
+
+ -- Go through 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_Storageonhand s
+ JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)
+ LEFT JOIN M_LocatorType lt ON (l.M_LocatorType_ID=lt.M_LocatorType_ID)
+ WHERE s.M_Product_ID=bom.M_ProductBOM_ID AND l.M_Warehouse_ID=myWarehouse_ID
+ -- How much can we make with this product
+ ProductQty := ProductQty/bom.BOMQty;
+ -- How much can we make overall
+ IF (ProductQty < Quantity) THEN
+ Quantity := ProductQty;
+ END IF;
+ -- Another BOM
+ ELSIF (bom.IsBOM = 'Y' AND bom.IsVerified = 'Y') THEN
+ ProductQty := BomqtyonhandForReservation (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 TRUNC(v_Quantity, v_StdPrecision); -- RoundDown
+ END IF;
+ RETURN 0;
+END BOMQTYONHANDFORRESERVATION;
+/
+
+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, p.IsVerified
+ FROM M_PRODUCT_BOM b, M_PRODUCT p
+ WHERE b.M_ProductBOM_ID=p.M_Product_ID
+ AND b.M_Product_ID=Product_ID
+ AND b.M_ProductBOM_ID != Product_ID
+ AND b.IsActive='Y';
+ --
+BEGIN
+ -- Check Parameters
+ myWarehouse_ID := Warehouse_ID;
+ IF (myWarehouse_ID IS NULL) THEN
+ IF (Locator_ID IS NULL) THEN
+ RETURN 0;
+ ELSE
+ SELECT SUM(M_Warehouse_ID) INTO myWarehouse_ID
+ FROM M_LOCATOR
+ WHERE M_Locator_ID=Locator_ID;
+ END IF;
+ END IF;
+ IF (myWarehouse_ID IS NULL) THEN
+ RETURN 0;
+ END IF;
+-- DBMS_OUTPUT.PUT_LINE('Warehouse=' || myWarehouse_ID);
+
+ -- Check, if product exists and if it is stocked
+ BEGIN
+ SELECT IsBOM, ProductType, IsStocked
+ INTO IsBOM, ProductType, IsStocked
+ FROM M_PRODUCT
+ WHERE M_Product_ID=Product_ID;
+ --
+ EXCEPTION -- not found
+ WHEN OTHERS THEN
+ RETURN 0;
+ END;
+ -- Unlimited 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_Storageonhand s
+ JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)
+ WHERE s.M_Product_ID=Product_ID AND l.M_Warehouse_ID=myWarehouse_ID;
+ --
+ -- DBMS_OUTPUT.PUT_LINE('Qty=' || ProductQty);
+ RETURN ProductQty;
+ END IF;
+
+ -- Go through 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_Storageonhand s
+ JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)
+ WHERE s.M_Product_ID=bom.M_ProductBOM_ID AND l.M_Warehouse_ID=myWarehouse_ID;
+ -- How much can we make with this product
+ ProductQty := ProductQty/bom.BOMQty;
+ -- How much can we make overall
+ IF (ProductQty < Quantity) THEN
+ Quantity := ProductQty;
+ END IF;
+ -- Another BOM
+ ELSIF (bom.IsBOM = 'Y' AND BOM.IsVerified = '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 TRUNC(v_Quantity, v_StdPrecision); -- RoundDown
+ 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, p.IsVerified
+ FROM M_PRODUCT_BOM b, M_PRODUCT p
+ WHERE b.M_ProductBOM_ID=p.M_Product_ID
+ AND b.M_Product_ID=p_Product_ID
+ AND b.M_ProductBOM_ID != p_Product_ID
+ AND b.IsActive='Y';
+ --
+BEGIN
+ -- Check Parameters
+ v_Warehouse_ID := p_Warehouse_ID;
+ IF (v_Warehouse_ID IS NULL) THEN
+ IF (p_Locator_ID IS NULL) THEN
+ RETURN 0;
+ ELSE
+ SELECT MAX(M_Warehouse_ID) INTO v_Warehouse_ID
+ FROM M_LOCATOR
+ WHERE M_Locator_ID=p_Locator_ID;
+ END IF;
+ END IF;
+ IF (v_Warehouse_ID IS NULL) THEN
+ RETURN 0;
+ END IF;
+-- DBMS_OUTPUT.PUT_LINE('Warehouse=' || v_Warehouse_ID);
+
+ -- Check, if product exists and if it is stocked
+ BEGIN
+ SELECT IsBOM, ProductType, IsStocked
+ INTO v_IsBOM, v_ProductType, v_IsStocked
+ FROM M_PRODUCT
+ WHERE M_Product_ID=p_Product_ID;
+ --
+ EXCEPTION -- not found
+ WHEN OTHERS THEN
+ RETURN 0;
+ END;
+
+ -- No reservation for non-stocked
+ IF (v_IsBOM='N' AND (v_ProductType<>'I' OR v_IsStocked='N')) THEN
+ RETURN 0;
+ -- Stocked item
+ ELSIF (v_IsStocked='Y') THEN
+ -- Get ProductQty
+ SELECT NVL(SUM(Qty), 0)
+ INTO v_ProductQty
+ FROM M_StorageReservation
+ WHERE M_Product_ID=p_Product_ID
+ AND M_Warehouse_ID=v_Warehouse_ID
+ AND IsSOTrx='N'
+ AND IsActive='Y';
+ --
+ RETURN v_ProductQty;
+ END IF;
+
+ -- Go though BOM
+-- DBMS_OUTPUT.PUT_LINE('BOM');
+ FOR bom IN CUR_BOM LOOP
+ -- Stocked Items "leaf node"
+ IF (bom.ProductType = 'I' AND bom.IsStocked = 'Y') THEN
+ -- Get ProductQty
+ SELECT NVL(SUM(Qty), 0)
+ INTO v_ProductQty
+ FROM M_StorageReservation
+ WHERE M_Product_ID=p_Product_ID
+ AND M_Warehouse_ID=v_Warehouse_ID
+ AND IsSOTrx='N'
+ AND IsActive='Y';
+ -- How much can we make with this product
+ v_ProductQty := v_ProductQty/bom.BOMQty;
+ -- How much can we make overall
+ IF (v_ProductQty < v_Quantity) THEN
+ v_Quantity := v_ProductQty;
+ END IF;
+ -- Another BOM
+ ELSIF (bom.IsBOM = 'Y' AND BOM.IsVerified = '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 TRUNC(v_Quantity, v_StdPrecision); -- RoundDown
+ 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, p.IsVerified
+ FROM M_PRODUCT_BOM b, M_PRODUCT p
+ WHERE b.M_ProductBOM_ID=p.M_Product_ID
+ AND b.M_Product_ID=p_Product_ID
+ AND b.M_ProductBOM_ID != p_Product_ID
+ AND b.IsActive='Y';
+ --
+BEGIN
+ -- Check Parameters
+ v_Warehouse_ID := p_Warehouse_ID;
+ IF (v_Warehouse_ID IS NULL) THEN
+ IF (p_Locator_ID IS NULL) THEN
+ RETURN 0;
+ ELSE
+ SELECT MAX(M_Warehouse_ID) INTO v_Warehouse_ID
+ FROM M_LOCATOR
+ WHERE M_Locator_ID=p_Locator_ID;
+ END IF;
+ END IF;
+ IF (v_Warehouse_ID IS NULL) THEN
+ RETURN 0;
+ END IF;
+-- DBMS_OUTPUT.PUT_LINE('Warehouse=' || v_Warehouse_ID);
+
+ -- Check, if product exists and if it is stocked
+ BEGIN
+ SELECT IsBOM, ProductType, IsStocked
+ INTO v_IsBOM, v_ProductType, v_IsStocked
+ FROM M_PRODUCT
+ WHERE M_Product_ID=p_Product_ID;
+ --
+ EXCEPTION -- not found
+ WHEN OTHERS THEN
+ RETURN 0;
+ END;
+
+ -- No reservation for non-stocked
+ IF (v_IsBOM='N' AND (v_ProductType<>'I' OR v_IsStocked='N')) THEN
+ RETURN 0;
+ -- Stocked item
+ ELSIF (v_IsStocked='Y') THEN
+ -- Get ProductQty
+ SELECT NVL(SUM(Qty), 0)
+ INTO v_ProductQty
+ FROM M_StorageReservation
+ WHERE M_Product_ID=p_Product_ID
+ AND M_Warehouse_ID=v_Warehouse_ID
+ AND IsSOTrx='Y'
+ AND IsActive='Y';
+ --
+ RETURN v_ProductQty;
+ END IF;
+
+ -- Go though BOM
+-- DBMS_OUTPUT.PUT_LINE('BOM');
+ FOR bom IN CUR_BOM LOOP
+ -- Stocked Items "leaf node"
+ IF (bom.ProductType = 'I' AND bom.IsStocked = 'Y') THEN
+ -- Get ProductQty
+ SELECT NVL(SUM(Qty), 0)
+ INTO v_ProductQty
+ FROM M_StorageReservation
+ WHERE M_Product_ID=bom.M_ProductBOM_ID
+ AND M_Warehouse_ID=v_Warehouse_ID
+ AND IsSOTrx='Y'
+ AND IsActive='Y';
+ -- How much can we make with this product
+ v_ProductQty := v_ProductQty/bom.BOMQty;
+ -- How much can we make overall
+ IF (v_ProductQty < v_Quantity) THEN
+ v_Quantity := v_ProductQty;
+ END IF;
+ -- Another BOM
+ ELSIF (bom.IsBOM = 'Y' AND BOM.IsVerified = '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 TRUNC(v_Quantity, v_StdPrecision); -- RoundDown
+ END IF;
+ RETURN 0;
+END Bomqtyreserved;
+/
+
+CREATE OR REPLACE FUNCTION BOMPRICELIMIT
+(
+ Product_ID IN NUMBER,
+ PriceList_Version_ID IN NUMBER
+)
+RETURN NUMBER
+/*************************************************************************
+ * The contents of this file are subject to the Compiere License. You may
+ * obtain a copy of the License at http://www.compiere.org/license.html
+ * Software is on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
+ * express or implied. See the License for details. Code: Compiere ERP+CRM
+ * Copyright (C) 1999-2002 Jorg Janke, ComPiere, Inc. All Rights Reserved.
+ *************************************************************************
+ * $Id: BOM_PriceLimit.sql,v 1.1 2006/04/21 17:51:58 jjanke Exp $
+ ***
+ * Title: Return Limit Price of Product/BOM
+ * Description:
+ * if not found: 0
+ ************************************************************************/
+AS
+ v_Price NUMBER;
+ v_ProductPrice NUMBER;
+ -- Get BOM Product info
+ CURSOR CUR_BOM IS
+ SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM
+ FROM M_PRODUCT_BOM b, M_PRODUCT p
+ WHERE b.M_ProductBOM_ID=p.M_Product_ID
+ AND b.M_Product_ID=Product_ID
+ AND b.M_ProductBOM_ID != Product_ID
+ AND (p.IsBOM='N' OR p.IsVerified='Y')
+ AND b.IsActive='Y';
+ --
+BEGIN
+ -- Try to get price from PriceList directly
+ SELECT COALESCE (SUM(PriceLimit), 0)
+ INTO v_Price
+ FROM M_PRODUCTPRICE
+ WHERE IsActive='Y' AND M_PriceList_Version_ID=PriceList_Version_ID AND M_Product_ID=Product_ID;
+-- DBMS_OUTPUT.PUT_LINE('Price=' || v_Price);
+
+ -- No Price - Check if BOM
+ IF (v_Price = 0) THEN
+ FOR bom IN CUR_BOM LOOP
+ v_ProductPrice := Bompricelimit (bom.M_ProductBOM_ID, PriceList_Version_ID);
+ v_Price := v_Price + (bom.BOMQty * v_ProductPrice);
+ END LOOP;
+ END IF;
+ --
+ RETURN v_Price;
+END Bompricelimit;
+/
+
+CREATE OR REPLACE FUNCTION BOMPRICELIST
+(
+ Product_ID IN NUMBER,
+ PriceList_Version_ID IN NUMBER
+)
+RETURN NUMBER
+/*************************************************************************
+ * The contents of this file are subject to the Compiere License. You may
+ * obtain a copy of the License at http://www.compiere.org/license.html
+ * Software is on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
+ * express or implied. See the License for details. Code: Compiere ERP+CRM
+ * Copyright (C) 1999-2002 Jorg Janke, ComPiere, Inc. All Rights Reserved.
+ *************************************************************************
+ * $Id: BOM_PriceList.sql,v 1.1 2006/04/21 17:51:58 jjanke Exp $
+ ***
+ * Title: Return List Price of Product/BOM
+ * Description:
+ * if not found: 0
+ ************************************************************************/
+AS
+ v_Price NUMBER;
+ v_ProductPrice NUMBER;
+ -- Get BOM Product info
+ CURSOR CUR_BOM IS
+ SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM
+ FROM M_PRODUCT_BOM b, M_PRODUCT p
+ WHERE b.M_ProductBOM_ID=p.M_Product_ID
+ AND b.M_Product_ID=Product_ID
+ AND b.M_ProductBOM_ID != Product_ID
+ AND (p.IsBOM='N' OR p.IsVerified='Y')
+ AND b.IsActive='Y';
+ --
+BEGIN
+ -- Try to get price from pricelist directly
+ SELECT COALESCE (SUM(PriceList), 0)
+ INTO v_Price
+ FROM M_PRODUCTPRICE
+ WHERE IsActive='Y' AND M_PriceList_Version_ID=PriceList_Version_ID AND M_Product_ID=Product_ID;
+-- DBMS_OUTPUT.PUT_LINE('Price=' || Price);
+
+ -- No Price - Check if BOM
+ IF (v_Price = 0) THEN
+ FOR bom IN CUR_BOM LOOP
+ v_ProductPrice := Bompricelist (bom.M_ProductBOM_ID, PriceList_Version_ID);
+ v_Price := v_Price + (bom.BOMQty * v_ProductPrice);
+ -- DBMS_OUTPUT.PUT_LINE('Qry=' || bom.BOMQty || ' @ ' || v_ProductPrice || ', Price=' || v_Price);
+ END LOOP; -- BOM
+ END IF;
+ --
+ RETURN v_Price;
+END Bompricelist;
+/
+
+CREATE OR REPLACE FUNCTION BOMPRICESTD
+(
+ Product_ID IN NUMBER,
+ PriceList_Version_ID IN NUMBER
+)
+RETURN NUMBER
+/*************************************************************************
+ * The contents of this file are subject to the Compiere License. You may
+ * obtain a copy of the License at http://www.compiere.org/license.html
+ * Software is on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
+ * express or implied. See the License for details. Code: Compiere ERP+CRM
+ * Copyright (C) 1999-2002 Jorg Janke, ComPiere, Inc. All Rights Reserved.
+ *************************************************************************
+ * $Id: BOM_PriceStd.sql,v 1.1 2006/04/21 17:51:58 jjanke Exp $
+ ***
+ * Title: Return Standard Price of Product/BOM
+ * Description:
+ * if not found: 0
+ ************************************************************************/
+AS
+ v_Price NUMBER;
+ v_ProductPrice NUMBER;
+ -- Get BOM Product info
+ CURSOR CUR_BOM IS
+ SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM
+ FROM M_PRODUCT_BOM b, M_PRODUCT p
+ WHERE b.M_ProductBOM_ID=p.M_Product_ID
+ AND b.M_Product_ID=Product_ID
+ AND b.M_ProductBOM_ID != Product_ID
+ AND (p.IsBOM='N' OR p.IsVerified='Y')
+ AND b.IsActive='Y';
+ --
+BEGIN
+ -- Try to get price from pricelist directly
+ SELECT COALESCE(SUM(PriceStd), 0)
+ INTO v_Price
+ FROM M_PRODUCTPRICE
+ WHERE IsActive='Y' AND 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 BOMQTYONHANDFORRESERVATION
+(
+ 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, p.IsVerified
+ FROM M_PRODUCT_BOM b, M_PRODUCT p
+ WHERE b.M_ProductBOM_ID=p.M_Product_ID
+ AND b.M_Product_ID=Product_ID
+ AND b.M_ProductBOM_ID != Product_ID
+ AND b.IsActive='Y';
+ --
+BEGIN
+ -- Check Parameters
+ myWarehouse_ID := Warehouse_ID;
+ IF (myWarehouse_ID IS NULL) THEN
+ IF (Locator_ID IS NULL) THEN
+ RETURN 0;
+ ELSE
+ SELECT SUM(M_Warehouse_ID) INTO myWarehouse_ID
+ FROM M_LOCATOR
+ WHERE M_Locator_ID=Locator_ID;
+ END IF;
+ END IF;
+ IF (myWarehouse_ID IS NULL) THEN
+ RETURN 0;
+ END IF;
+-- DBMS_OUTPUT.PUT_LINE('Warehouse=' || myWarehouse_ID);
+
+ -- Check, if product exists and if it is stocked
+ BEGIN
+ SELECT IsBOM, ProductType, IsStocked
+ INTO IsBOM, ProductType, IsStocked
+ FROM M_PRODUCT
+ WHERE M_Product_ID=Product_ID;
+ --
+ EXCEPTION -- not found
+ WHEN OTHERS THEN
+ RETURN 0;
+ END;
+ -- Unlimited 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_Storageonhand s
+ JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)
+ LEFT JOIN M_LocatorType lt ON (l.M_LocatorType_ID=lt.M_LocatorType_ID)
+ WHERE s.M_Product_ID=Product_ID AND l.M_Warehouse_ID=myWarehouse_ID
+ AND COALESCE(lt.IsAvailableForReservation,'Y')='Y';
+ --
+ -- DBMS_OUTPUT.PUT_LINE('Qty=' || ProductQty);
+ RETURN ProductQty;
+ END IF;
+
+ -- Go through 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_Storageonhand s
+ JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)
+ LEFT JOIN M_LocatorType lt ON (l.M_LocatorType_ID=lt.M_LocatorType_ID)
+ WHERE s.M_Product_ID=bom.M_ProductBOM_ID AND l.M_Warehouse_ID=myWarehouse_ID
+ -- How much can we make with this product
+ ProductQty := ProductQty/bom.BOMQty;
+ -- How much can we make overall
+ IF (ProductQty < Quantity) THEN
+ Quantity := ProductQty;
+ END IF;
+ -- Another BOM
+ ELSIF (bom.IsBOM = 'Y' AND bom.IsVerified = 'Y') THEN
+ ProductQty := BomqtyonhandForReservation (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 TRUNC(v_Quantity, v_StdPrecision); -- RoundDown
+ END IF;
+ RETURN 0;
+END BOMQTYONHANDFORRESERVATION;
+/
+
+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, p.IsVerified
+ FROM M_PRODUCT_BOM b, M_PRODUCT p
+ WHERE b.M_ProductBOM_ID=p.M_Product_ID
+ AND b.M_Product_ID=Product_ID
+ AND b.M_ProductBOM_ID != Product_ID
+ AND b.IsActive='Y';
+ --
+BEGIN
+ -- Check Parameters
+ myWarehouse_ID := Warehouse_ID;
+ IF (myWarehouse_ID IS NULL) THEN
+ IF (Locator_ID IS NULL) THEN
+ RETURN 0;
+ ELSE
+ SELECT SUM(M_Warehouse_ID) INTO myWarehouse_ID
+ FROM M_LOCATOR
+ WHERE M_Locator_ID=Locator_ID;
+ END IF;
+ END IF;
+ IF (myWarehouse_ID IS NULL) THEN
+ RETURN 0;
+ END IF;
+-- DBMS_OUTPUT.PUT_LINE('Warehouse=' || myWarehouse_ID);
+
+ -- Check, if product exists and if it is stocked
+ BEGIN
+ SELECT IsBOM, ProductType, IsStocked
+ INTO IsBOM, ProductType, IsStocked
+ FROM M_PRODUCT
+ WHERE M_Product_ID=Product_ID;
+ --
+ EXCEPTION -- not found
+ WHEN OTHERS THEN
+ RETURN 0;
+ END;
+ -- Unlimited 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_Storageonhand s
+ JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)
+ WHERE s.M_Product_ID=Product_ID AND l.M_Warehouse_ID=myWarehouse_ID;
+ --
+ -- DBMS_OUTPUT.PUT_LINE('Qty=' || ProductQty);
+ RETURN ProductQty;
+ END IF;
+
+ -- Go through 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_Storageonhand s
+ JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)
+ WHERE s.M_Product_ID=bom.M_ProductBOM_ID AND l.M_Warehouse_ID=myWarehouse_ID;
+ -- How much can we make with this product
+ ProductQty := ProductQty/bom.BOMQty;
+ -- How much can we make overall
+ IF (ProductQty < Quantity) THEN
+ Quantity := ProductQty;
+ END IF;
+ -- Another BOM
+ ELSIF (bom.IsBOM = 'Y' AND BOM.IsVerified = '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 TRUNC(v_Quantity, v_StdPrecision); -- RoundDown
+ 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, p.IsVerified
+ FROM M_PRODUCT_BOM b, M_PRODUCT p
+ WHERE b.M_ProductBOM_ID=p.M_Product_ID
+ AND b.M_Product_ID=p_Product_ID
+ AND b.M_ProductBOM_ID != p_Product_ID
+ AND b.IsActive='Y';
+ --
+BEGIN
+ -- Check Parameters
+ v_Warehouse_ID := p_Warehouse_ID;
+ IF (v_Warehouse_ID IS NULL) THEN
+ IF (p_Locator_ID IS NULL) THEN
+ RETURN 0;
+ ELSE
+ SELECT MAX(M_Warehouse_ID) INTO v_Warehouse_ID
+ FROM M_LOCATOR
+ WHERE M_Locator_ID=p_Locator_ID;
+ END IF;
+ END IF;
+ IF (v_Warehouse_ID IS NULL) THEN
+ RETURN 0;
+ END IF;
+-- DBMS_OUTPUT.PUT_LINE('Warehouse=' || v_Warehouse_ID);
+
+ -- Check, if product exists and if it is stocked
+ BEGIN
+ SELECT IsBOM, ProductType, IsStocked
+ INTO v_IsBOM, v_ProductType, v_IsStocked
+ FROM M_PRODUCT
+ WHERE M_Product_ID=p_Product_ID;
+ --
+ EXCEPTION -- not found
+ WHEN OTHERS THEN
+ RETURN 0;
+ END;
+
+ -- No reservation for non-stocked
+ IF (v_IsBOM='N' AND (v_ProductType<>'I' OR v_IsStocked='N')) THEN
+ RETURN 0;
+ -- Stocked item
+ ELSIF (v_IsStocked='Y') THEN
+ -- Get ProductQty
+ SELECT NVL(SUM(Qty), 0)
+ INTO v_ProductQty
+ FROM M_StorageReservation
+ WHERE M_Product_ID=p_Product_ID
+ AND M_Warehouse_ID=v_Warehouse_ID
+ AND IsSOTrx='N'
+ AND IsActive='Y';
+ --
+ RETURN v_ProductQty;
+ END IF;
+
+ -- Go though BOM
+-- DBMS_OUTPUT.PUT_LINE('BOM');
+ FOR bom IN CUR_BOM LOOP
+ -- Stocked Items "leaf node"
+ IF (bom.ProductType = 'I' AND bom.IsStocked = 'Y') THEN
+ -- Get ProductQty
+ SELECT NVL(SUM(Qty), 0)
+ INTO v_ProductQty
+ FROM M_StorageReservation
+ WHERE M_Product_ID=p_Product_ID
+ AND M_Warehouse_ID=v_Warehouse_ID
+ AND IsSOTrx='N'
+ AND IsActive='Y';
+ -- How much can we make with this product
+ v_ProductQty := v_ProductQty/bom.BOMQty;
+ -- How much can we make overall
+ IF (v_ProductQty < v_Quantity) THEN
+ v_Quantity := v_ProductQty;
+ END IF;
+ -- Another BOM
+ ELSIF (bom.IsBOM = 'Y' AND BOM.IsVerified = '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 TRUNC(v_Quantity, v_StdPrecision); -- RoundDown
+ 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, p.IsVerified
+ FROM M_PRODUCT_BOM b, M_PRODUCT p
+ WHERE b.M_ProductBOM_ID=p.M_Product_ID
+ AND b.M_Product_ID=p_Product_ID
+ AND b.M_ProductBOM_ID != p_Product_ID
+ AND b.IsActive='Y';
+ --
+BEGIN
+ -- Check Parameters
+ v_Warehouse_ID := p_Warehouse_ID;
+ IF (v_Warehouse_ID IS NULL) THEN
+ IF (p_Locator_ID IS NULL) THEN
+ RETURN 0;
+ ELSE
+ SELECT MAX(M_Warehouse_ID) INTO v_Warehouse_ID
+ FROM M_LOCATOR
+ WHERE M_Locator_ID=p_Locator_ID;
+ END IF;
+ END IF;
+ IF (v_Warehouse_ID IS NULL) THEN
+ RETURN 0;
+ END IF;
+-- DBMS_OUTPUT.PUT_LINE('Warehouse=' || v_Warehouse_ID);
+
+ -- Check, if product exists and if it is stocked
+ BEGIN
+ SELECT IsBOM, ProductType, IsStocked
+ INTO v_IsBOM, v_ProductType, v_IsStocked
+ FROM M_PRODUCT
+ WHERE M_Product_ID=p_Product_ID;
+ --
+ EXCEPTION -- not found
+ WHEN OTHERS THEN
+ RETURN 0;
+ END;
+
+ -- No reservation for non-stocked
+ IF (v_IsBOM='N' AND (v_ProductType<>'I' OR v_IsStocked='N')) THEN
+ RETURN 0;
+ -- Stocked item
+ ELSIF (v_IsStocked='Y') THEN
+ -- Get ProductQty
+ SELECT NVL(SUM(Qty), 0)
+ INTO v_ProductQty
+ FROM M_StorageReservation
+ WHERE M_Product_ID=p_Product_ID
+ AND M_Warehouse_ID=v_Warehouse_ID
+ AND IsSOTrx='Y'
+ AND IsActive='Y';
+ --
+ RETURN v_ProductQty;
+ END IF;
+
+ -- Go though BOM
+-- DBMS_OUTPUT.PUT_LINE('BOM');
+ FOR bom IN CUR_BOM LOOP
+ -- Stocked Items "leaf node"
+ IF (bom.ProductType = 'I' AND bom.IsStocked = 'Y') THEN
+ -- Get ProductQty
+ SELECT NVL(SUM(Qty), 0)
+ INTO v_ProductQty
+ FROM M_StorageReservation
+ WHERE M_Product_ID=bom.M_ProductBOM_ID
+ AND M_Warehouse_ID=v_Warehouse_ID
+ AND IsSOTrx='Y'
+ AND IsActive='Y';
+ -- How much can we make with this product
+ v_ProductQty := v_ProductQty/bom.BOMQty;
+ -- How much can we make overall
+ IF (v_ProductQty < v_Quantity) THEN
+ v_Quantity := v_ProductQty;
+ END IF;
+ -- Another BOM
+ ELSIF (bom.IsBOM = 'Y' AND BOM.IsVerified = '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 TRUNC(v_Quantity, v_StdPrecision); -- RoundDown
+ END IF;
+ RETURN 0;
+END Bomqtyreserved;
+/
+
+CREATE OR REPLACE FUNCTION BOMPRICELIMIT
+(
+ Product_ID IN NUMBER,
+ PriceList_Version_ID IN NUMBER
+)
+RETURN NUMBER
+/*************************************************************************
+ * The contents of this file are subject to the Compiere License. You may
+ * obtain a copy of the License at http://www.compiere.org/license.html
+ * Software is on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
+ * express or implied. See the License for details. Code: Compiere ERP+CRM
+ * Copyright (C) 1999-2002 Jorg Janke, ComPiere, Inc. All Rights Reserved.
+ *************************************************************************
+ * $Id: BOM_PriceLimit.sql,v 1.1 2006/04/21 17:51:58 jjanke Exp $
+ ***
+ * Title: Return Limit Price of Product/BOM
+ * Description:
+ * if not found: 0
+ ************************************************************************/
+AS
+ v_Price NUMBER;
+ v_ProductPrice NUMBER;
+ -- Get BOM Product info
+ CURSOR CUR_BOM IS
+ SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM
+ FROM M_PRODUCT_BOM b, M_PRODUCT p
+ WHERE b.M_ProductBOM_ID=p.M_Product_ID
+ AND b.M_Product_ID=Product_ID
+ AND b.M_ProductBOM_ID != Product_ID
+ AND (p.IsBOM='N' OR p.IsVerified='Y')
+ AND b.IsActive='Y';
+ --
+BEGIN
+ -- Try to get price from PriceList directly
+ SELECT COALESCE (SUM(PriceLimit), 0)
+ INTO v_Price
+ FROM M_PRODUCTPRICE
+ WHERE IsActive='Y' AND M_PriceList_Version_ID=PriceList_Version_ID AND M_Product_ID=Product_ID;
+-- DBMS_OUTPUT.PUT_LINE('Price=' || v_Price);
+
+ -- No Price - Check if BOM
+ IF (v_Price = 0) THEN
+ FOR bom IN CUR_BOM LOOP
+ v_ProductPrice := Bompricelimit (bom.M_ProductBOM_ID, PriceList_Version_ID);
+ v_Price := v_Price + (bom.BOMQty * v_ProductPrice);
+ END LOOP;
+ END IF;
+ --
+ RETURN v_Price;
+END Bompricelimit;
+/
+
+CREATE OR REPLACE FUNCTION BOMPRICELIST
+(
+ Product_ID IN NUMBER,
+ PriceList_Version_ID IN NUMBER
+)
+RETURN NUMBER
+/*************************************************************************
+ * The contents of this file are subject to the Compiere License. You may
+ * obtain a copy of the License at http://www.compiere.org/license.html
+ * Software is on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
+ * express or implied. See the License for details. Code: Compiere ERP+CRM
+ * Copyright (C) 1999-2002 Jorg Janke, ComPiere, Inc. All Rights Reserved.
+ *************************************************************************
+ * $Id: BOM_PriceList.sql,v 1.1 2006/04/21 17:51:58 jjanke Exp $
+ ***
+ * Title: Return List Price of Product/BOM
+ * Description:
+ * if not found: 0
+ ************************************************************************/
+AS
+ v_Price NUMBER;
+ v_ProductPrice NUMBER;
+ -- Get BOM Product info
+ CURSOR CUR_BOM IS
+ SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM
+ FROM M_PRODUCT_BOM b, M_PRODUCT p
+ WHERE b.M_ProductBOM_ID=p.M_Product_ID
+ AND b.M_Product_ID=Product_ID
+ AND b.M_ProductBOM_ID != Product_ID
+ AND (p.IsBOM='N' OR p.IsVerified='Y')
+ AND b.IsActive='Y';
+ --
+BEGIN
+ -- Try to get price from pricelist directly
+ SELECT COALESCE (SUM(PriceList), 0)
+ INTO v_Price
+ FROM M_PRODUCTPRICE
+ WHERE IsActive='Y' AND M_PriceList_Version_ID=PriceList_Version_ID AND M_Product_ID=Product_ID;
+-- DBMS_OUTPUT.PUT_LINE('Price=' || Price);
+
+ -- No Price - Check if BOM
+ IF (v_Price = 0) THEN
+ FOR bom IN CUR_BOM LOOP
+ v_ProductPrice := Bompricelist (bom.M_ProductBOM_ID, PriceList_Version_ID);
+ v_Price := v_Price + (bom.BOMQty * v_ProductPrice);
+ -- DBMS_OUTPUT.PUT_LINE('Qry=' || bom.BOMQty || ' @ ' || v_ProductPrice || ', Price=' || v_Price);
+ END LOOP; -- BOM
+ END IF;
+ --
+ RETURN v_Price;
+END Bompricelist;
+/
+
+CREATE OR REPLACE FUNCTION BOMPRICESTD
+(
+ Product_ID IN NUMBER,
+ PriceList_Version_ID IN NUMBER
+)
+RETURN NUMBER
+/*************************************************************************
+ * The contents of this file are subject to the Compiere License. You may
+ * obtain a copy of the License at http://www.compiere.org/license.html
+ * Software is on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
+ * express or implied. See the License for details. Code: Compiere ERP+CRM
+ * Copyright (C) 1999-2002 Jorg Janke, ComPiere, Inc. All Rights Reserved.
+ *************************************************************************
+ * $Id: BOM_PriceStd.sql,v 1.1 2006/04/21 17:51:58 jjanke Exp $
+ ***
+ * Title: Return Standard Price of Product/BOM
+ * Description:
+ * if not found: 0
+ ************************************************************************/
+AS
+ v_Price NUMBER;
+ v_ProductPrice NUMBER;
+ -- Get BOM Product info
+ CURSOR CUR_BOM IS
+ SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM
+ FROM M_PRODUCT_BOM b, M_PRODUCT p
+ WHERE b.M_ProductBOM_ID=p.M_Product_ID
+ AND b.M_Product_ID=Product_ID
+ AND b.M_ProductBOM_ID != Product_ID
+ AND (p.IsBOM='N' OR p.IsVerified='Y')
+ AND b.IsActive='Y';
+ --
+BEGIN
+ -- Try to get price from pricelist directly
+ SELECT COALESCE(SUM(PriceStd), 0)
+ INTO v_Price
+ FROM M_PRODUCTPRICE
+ WHERE IsActive='Y' AND 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 BOMQTYONHANDFORRESERVATION
+(
+ 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, p.IsVerified
+ FROM M_PRODUCT_BOM b, M_PRODUCT p
+ WHERE b.M_ProductBOM_ID=p.M_Product_ID
+ AND b.M_Product_ID=Product_ID
+ AND b.M_ProductBOM_ID != Product_ID
+ AND b.IsActive='Y';
+ --
+BEGIN
+ -- Check Parameters
+ myWarehouse_ID := Warehouse_ID;
+ IF (myWarehouse_ID IS NULL) THEN
+ IF (Locator_ID IS NULL) THEN
+ RETURN 0;
+ ELSE
+ SELECT SUM(M_Warehouse_ID) INTO myWarehouse_ID
+ FROM M_LOCATOR
+ WHERE M_Locator_ID=Locator_ID;
+ END IF;
+ END IF;
+ IF (myWarehouse_ID IS NULL) THEN
+ RETURN 0;
+ END IF;
+-- DBMS_OUTPUT.PUT_LINE('Warehouse=' || myWarehouse_ID);
+
+ -- Check, if product exists and if it is stocked
+ BEGIN
+ SELECT IsBOM, ProductType, IsStocked
+ INTO IsBOM, ProductType, IsStocked
+ FROM M_PRODUCT
+ WHERE M_Product_ID=Product_ID;
+ --
+ EXCEPTION -- not found
+ WHEN OTHERS THEN
+ RETURN 0;
+ END;
+ -- Unlimited 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_Storageonhand s
+ JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)
+ LEFT JOIN M_LocatorType lt ON (l.M_LocatorType_ID=lt.M_LocatorType_ID)
+ WHERE s.M_Product_ID=Product_ID AND l.M_Warehouse_ID=myWarehouse_ID
+ AND COALESCE(lt.IsAvailableForReservation,'Y')='Y';
+ --
+ -- DBMS_OUTPUT.PUT_LINE('Qty=' || ProductQty);
+ RETURN ProductQty;
+ END IF;
+
+ -- Go through 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_Storageonhand s
+ JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)
+ LEFT JOIN M_LocatorType lt ON (l.M_LocatorType_ID=lt.M_LocatorType_ID)
+ WHERE s.M_Product_ID=bom.M_ProductBOM_ID AND l.M_Warehouse_ID=myWarehouse_ID
+ -- How much can we make with this product
+ ProductQty := ProductQty/bom.BOMQty;
+ -- How much can we make overall
+ IF (ProductQty < Quantity) THEN
+ Quantity := ProductQty;
+ END IF;
+ -- Another BOM
+ ELSIF (bom.IsBOM = 'Y' AND bom.IsVerified = 'Y') THEN
+ ProductQty := BomqtyonhandForReservation (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 TRUNC(Quantity, StdPrecision); -- RoundDown
+ END IF;
+ RETURN 0;
+END BOMQTYONHANDFORRESERVATION;
+/
+
+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, p.IsVerified
+ FROM M_PRODUCT_BOM b, M_PRODUCT p
+ WHERE b.M_ProductBOM_ID=p.M_Product_ID
+ AND b.M_Product_ID=Product_ID
+ AND b.M_ProductBOM_ID != Product_ID
+ AND b.IsActive='Y';
+ --
+BEGIN
+ -- Check Parameters
+ myWarehouse_ID := Warehouse_ID;
+ IF (myWarehouse_ID IS NULL) THEN
+ IF (Locator_ID IS NULL) THEN
+ RETURN 0;
+ ELSE
+ SELECT SUM(M_Warehouse_ID) INTO myWarehouse_ID
+ FROM M_LOCATOR
+ WHERE M_Locator_ID=Locator_ID;
+ END IF;
+ END IF;
+ IF (myWarehouse_ID IS NULL) THEN
+ RETURN 0;
+ END IF;
+-- DBMS_OUTPUT.PUT_LINE('Warehouse=' || myWarehouse_ID);
+
+ -- Check, if product exists and if it is stocked
+ BEGIN
+ SELECT IsBOM, ProductType, IsStocked
+ INTO IsBOM, ProductType, IsStocked
+ FROM M_PRODUCT
+ WHERE M_Product_ID=Product_ID;
+ --
+ EXCEPTION -- not found
+ WHEN OTHERS THEN
+ RETURN 0;
+ END;
+ -- Unlimited 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_Storageonhand s
+ JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)
+ WHERE s.M_Product_ID=Product_ID AND l.M_Warehouse_ID=myWarehouse_ID;
+ --
+ -- DBMS_OUTPUT.PUT_LINE('Qty=' || ProductQty);
+ RETURN ProductQty;
+ END IF;
+
+ -- Go through 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_Storageonhand s
+ JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)
+ WHERE s.M_Product_ID=bom.M_ProductBOM_ID AND l.M_Warehouse_ID=myWarehouse_ID;
+ -- How much can we make with this product
+ ProductQty := ProductQty/bom.BOMQty;
+ -- How much can we make overall
+ IF (ProductQty < Quantity) THEN
+ Quantity := ProductQty;
+ END IF;
+ -- Another BOM
+ ELSIF (bom.IsBOM = 'Y' AND BOM.IsVerified = '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 TRUNC(Quantity, StdPrecision); -- RoundDown
+ 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, p.IsVerified
+ FROM M_PRODUCT_BOM b, M_PRODUCT p
+ WHERE b.M_ProductBOM_ID=p.M_Product_ID
+ AND b.M_Product_ID=p_Product_ID
+ AND b.M_ProductBOM_ID != p_Product_ID
+ AND b.IsActive='Y';
+ --
+BEGIN
+ -- Check Parameters
+ v_Warehouse_ID := p_Warehouse_ID;
+ IF (v_Warehouse_ID IS NULL) THEN
+ IF (p_Locator_ID IS NULL) THEN
+ RETURN 0;
+ ELSE
+ SELECT MAX(M_Warehouse_ID) INTO v_Warehouse_ID
+ FROM M_LOCATOR
+ WHERE M_Locator_ID=p_Locator_ID;
+ END IF;
+ END IF;
+ IF (v_Warehouse_ID IS NULL) THEN
+ RETURN 0;
+ END IF;
+-- DBMS_OUTPUT.PUT_LINE('Warehouse=' || v_Warehouse_ID);
+
+ -- Check, if product exists and if it is stocked
+ BEGIN
+ SELECT IsBOM, ProductType, IsStocked
+ INTO v_IsBOM, v_ProductType, v_IsStocked
+ FROM M_PRODUCT
+ WHERE M_Product_ID=p_Product_ID;
+ --
+ EXCEPTION -- not found
+ WHEN OTHERS THEN
+ RETURN 0;
+ END;
+
+ -- No reservation for non-stocked
+ IF (v_IsBOM='N' AND (v_ProductType<>'I' OR v_IsStocked='N')) THEN
+ RETURN 0;
+ -- Stocked item
+ ELSIF (v_IsStocked='Y') THEN
+ -- Get ProductQty
+ SELECT NVL(SUM(Qty), 0)
+ INTO v_ProductQty
+ FROM M_StorageReservation
+ WHERE M_Product_ID=p_Product_ID
+ AND M_Warehouse_ID=v_Warehouse_ID
+ AND IsSOTrx='N'
+ AND IsActive='Y';
+ --
+ RETURN v_ProductQty;
+ END IF;
+
+ -- Go though BOM
+-- DBMS_OUTPUT.PUT_LINE('BOM');
+ FOR bom IN CUR_BOM LOOP
+ -- Stocked Items "leaf node"
+ IF (bom.ProductType = 'I' AND bom.IsStocked = 'Y') THEN
+ -- Get ProductQty
+ SELECT NVL(SUM(Qty), 0)
+ INTO v_ProductQty
+ FROM M_StorageReservation
+ WHERE M_Product_ID=p_Product_ID
+ AND M_Warehouse_ID=v_Warehouse_ID
+ AND IsSOTrx='N'
+ AND IsActive='Y';
+ -- How much can we make with this product
+ v_ProductQty := v_ProductQty/bom.BOMQty;
+ -- How much can we make overall
+ IF (v_ProductQty < v_Quantity) THEN
+ v_Quantity := v_ProductQty;
+ END IF;
+ -- Another BOM
+ ELSIF (bom.IsBOM = 'Y' AND BOM.IsVerified = '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 TRUNC(v_Quantity, v_StdPrecision); -- RoundDown
+ 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, p.IsVerified
+ FROM M_PRODUCT_BOM b, M_PRODUCT p
+ WHERE b.M_ProductBOM_ID=p.M_Product_ID
+ AND b.M_Product_ID=p_Product_ID
+ AND b.M_ProductBOM_ID != p_Product_ID
+ AND b.IsActive='Y';
+ --
+BEGIN
+ -- Check Parameters
+ v_Warehouse_ID := p_Warehouse_ID;
+ IF (v_Warehouse_ID IS NULL) THEN
+ IF (p_Locator_ID IS NULL) THEN
+ RETURN 0;
+ ELSE
+ SELECT MAX(M_Warehouse_ID) INTO v_Warehouse_ID
+ FROM M_LOCATOR
+ WHERE M_Locator_ID=p_Locator_ID;
+ END IF;
+ END IF;
+ IF (v_Warehouse_ID IS NULL) THEN
+ RETURN 0;
+ END IF;
+-- DBMS_OUTPUT.PUT_LINE('Warehouse=' || v_Warehouse_ID);
+
+ -- Check, if product exists and if it is stocked
+ BEGIN
+ SELECT IsBOM, ProductType, IsStocked
+ INTO v_IsBOM, v_ProductType, v_IsStocked
+ FROM M_PRODUCT
+ WHERE M_Product_ID=p_Product_ID;
+ --
+ EXCEPTION -- not found
+ WHEN OTHERS THEN
+ RETURN 0;
+ END;
+
+ -- No reservation for non-stocked
+ IF (v_IsBOM='N' AND (v_ProductType<>'I' OR v_IsStocked='N')) THEN
+ RETURN 0;
+ -- Stocked item
+ ELSIF (v_IsStocked='Y') THEN
+ -- Get ProductQty
+ SELECT NVL(SUM(Qty), 0)
+ INTO v_ProductQty
+ FROM M_StorageReservation
+ WHERE M_Product_ID=p_Product_ID
+ AND M_Warehouse_ID=v_Warehouse_ID
+ AND IsSOTrx='Y'
+ AND IsActive='Y';
+ --
+ RETURN v_ProductQty;
+ END IF;
+
+ -- Go though BOM
+-- DBMS_OUTPUT.PUT_LINE('BOM');
+ FOR bom IN CUR_BOM LOOP
+ -- Stocked Items "leaf node"
+ IF (bom.ProductType = 'I' AND bom.IsStocked = 'Y') THEN
+ -- Get ProductQty
+ SELECT NVL(SUM(Qty), 0)
+ INTO v_ProductQty
+ FROM M_StorageReservation
+ WHERE M_Product_ID=bom.M_ProductBOM_ID
+ AND M_Warehouse_ID=v_Warehouse_ID
+ AND IsSOTrx='Y'
+ AND IsActive='Y';
+ -- How much can we make with this product
+ v_ProductQty := v_ProductQty/bom.BOMQty;
+ -- How much can we make overall
+ IF (v_ProductQty < v_Quantity) THEN
+ v_Quantity := v_ProductQty;
+ END IF;
+ -- Another BOM
+ ELSIF (bom.IsBOM = 'Y' AND BOM.IsVerified = '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 TRUNC(v_Quantity, v_StdPrecision); -- RoundDown
+ END IF;
+ RETURN 0;
+END Bomqtyreserved;
+/
+
+CREATE OR REPLACE FUNCTION BOMPRICELIMIT
+(
+ Product_ID IN NUMBER,
+ PriceList_Version_ID IN NUMBER
+)
+RETURN NUMBER
+/*************************************************************************
+ * The contents of this file are subject to the Compiere License. You may
+ * obtain a copy of the License at http://www.compiere.org/license.html
+ * Software is on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
+ * express or implied. See the License for details. Code: Compiere ERP+CRM
+ * Copyright (C) 1999-2002 Jorg Janke, ComPiere, Inc. All Rights Reserved.
+ *************************************************************************
+ * $Id: BOM_PriceLimit.sql,v 1.1 2006/04/21 17:51:58 jjanke Exp $
+ ***
+ * Title: Return Limit Price of Product/BOM
+ * Description:
+ * if not found: 0
+ ************************************************************************/
+AS
+ v_Price NUMBER;
+ v_ProductPrice NUMBER;
+ -- Get BOM Product info
+ CURSOR CUR_BOM IS
+ SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM
+ FROM M_PRODUCT_BOM b, M_PRODUCT p
+ WHERE b.M_ProductBOM_ID=p.M_Product_ID
+ AND b.M_Product_ID=Product_ID
+ AND b.M_ProductBOM_ID != Product_ID
+ AND (p.IsBOM='N' OR p.IsVerified='Y')
+ AND b.IsActive='Y';
+ --
+BEGIN
+ -- Try to get price from PriceList directly
+ SELECT COALESCE (SUM(PriceLimit), 0)
+ INTO v_Price
+ FROM M_PRODUCTPRICE
+ WHERE IsActive='Y' AND M_PriceList_Version_ID=PriceList_Version_ID AND M_Product_ID=Product_ID;
+-- DBMS_OUTPUT.PUT_LINE('Price=' || v_Price);
+
+ -- No Price - Check if BOM
+ IF (v_Price = 0) THEN
+ FOR bom IN CUR_BOM LOOP
+ v_ProductPrice := Bompricelimit (bom.M_ProductBOM_ID, PriceList_Version_ID);
+ v_Price := v_Price + (bom.BOMQty * v_ProductPrice);
+ END LOOP;
+ END IF;
+ --
+ RETURN v_Price;
+END Bompricelimit;
+/
+
+CREATE OR REPLACE FUNCTION BOMPRICELIST
+(
+ Product_ID IN NUMBER,
+ PriceList_Version_ID IN NUMBER
+)
+RETURN NUMBER
+/*************************************************************************
+ * The contents of this file are subject to the Compiere License. You may
+ * obtain a copy of the License at http://www.compiere.org/license.html
+ * Software is on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
+ * express or implied. See the License for details. Code: Compiere ERP+CRM
+ * Copyright (C) 1999-2002 Jorg Janke, ComPiere, Inc. All Rights Reserved.
+ *************************************************************************
+ * $Id: BOM_PriceList.sql,v 1.1 2006/04/21 17:51:58 jjanke Exp $
+ ***
+ * Title: Return List Price of Product/BOM
+ * Description:
+ * if not found: 0
+ ************************************************************************/
+AS
+ v_Price NUMBER;
+ v_ProductPrice NUMBER;
+ -- Get BOM Product info
+ CURSOR CUR_BOM IS
+ SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM
+ FROM M_PRODUCT_BOM b, M_PRODUCT p
+ WHERE b.M_ProductBOM_ID=p.M_Product_ID
+ AND b.M_Product_ID=Product_ID
+ AND b.M_ProductBOM_ID != Product_ID
+ AND (p.IsBOM='N' OR p.IsVerified='Y')
+ AND b.IsActive='Y';
+ --
+BEGIN
+ -- Try to get price from pricelist directly
+ SELECT COALESCE (SUM(PriceList), 0)
+ INTO v_Price
+ FROM M_PRODUCTPRICE
+ WHERE IsActive='Y' AND M_PriceList_Version_ID=PriceList_Version_ID AND M_Product_ID=Product_ID;
+-- DBMS_OUTPUT.PUT_LINE('Price=' || Price);
+
+ -- No Price - Check if BOM
+ IF (v_Price = 0) THEN
+ FOR bom IN CUR_BOM LOOP
+ v_ProductPrice := Bompricelist (bom.M_ProductBOM_ID, PriceList_Version_ID);
+ v_Price := v_Price + (bom.BOMQty * v_ProductPrice);
+ -- DBMS_OUTPUT.PUT_LINE('Qry=' || bom.BOMQty || ' @ ' || v_ProductPrice || ', Price=' || v_Price);
+ END LOOP; -- BOM
+ END IF;
+ --
+ RETURN v_Price;
+END Bompricelist;
+/
+
+CREATE OR REPLACE FUNCTION BOMPRICESTD
+(
+ Product_ID IN NUMBER,
+ PriceList_Version_ID IN NUMBER
+)
+RETURN NUMBER
+/*************************************************************************
+ * The contents of this file are subject to the Compiere License. You may
+ * obtain a copy of the License at http://www.compiere.org/license.html
+ * Software is on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
+ * express or implied. See the License for details. Code: Compiere ERP+CRM
+ * Copyright (C) 1999-2002 Jorg Janke, ComPiere, Inc. All Rights Reserved.
+ *************************************************************************
+ * $Id: BOM_PriceStd.sql,v 1.1 2006/04/21 17:51:58 jjanke Exp $
+ ***
+ * Title: Return Standard Price of Product/BOM
+ * Description:
+ * if not found: 0
+ ************************************************************************/
+AS
+ v_Price NUMBER;
+ v_ProductPrice NUMBER;
+ -- Get BOM Product info
+ CURSOR CUR_BOM IS
+ SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM
+ FROM M_PRODUCT_BOM b, M_PRODUCT p
+ WHERE b.M_ProductBOM_ID=p.M_Product_ID
+ AND b.M_Product_ID=Product_ID
+ AND b.M_ProductBOM_ID != Product_ID
+ AND (p.IsBOM='N' OR p.IsVerified='Y')
+ AND b.IsActive='Y';
+ --
+BEGIN
+ -- Try to get price from pricelist directly
+ SELECT COALESCE(SUM(PriceStd), 0)
+ INTO v_Price
+ FROM M_PRODUCTPRICE
+ WHERE IsActive='Y' AND 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 BOMQTYONHANDFORRESERVATION
+(
+ 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, p.IsVerified
+ FROM M_PRODUCT_BOM b, M_PRODUCT p
+ WHERE b.M_ProductBOM_ID=p.M_Product_ID
+ AND b.M_Product_ID=Product_ID
+ AND b.M_ProductBOM_ID != Product_ID
+ AND b.IsActive='Y';
+ --
+BEGIN
+ -- Check Parameters
+ myWarehouse_ID := Warehouse_ID;
+ IF (myWarehouse_ID IS NULL) THEN
+ IF (Locator_ID IS NULL) THEN
+ RETURN 0;
+ ELSE
+ SELECT SUM(M_Warehouse_ID) INTO myWarehouse_ID
+ FROM M_LOCATOR
+ WHERE M_Locator_ID=Locator_ID;
+ END IF;
+ END IF;
+ IF (myWarehouse_ID IS NULL) THEN
+ RETURN 0;
+ END IF;
+-- DBMS_OUTPUT.PUT_LINE('Warehouse=' || myWarehouse_ID);
+
+ -- Check, if product exists and if it is stocked
+ BEGIN
+ SELECT IsBOM, ProductType, IsStocked
+ INTO IsBOM, ProductType, IsStocked
+ FROM M_PRODUCT
+ WHERE M_Product_ID=Product_ID;
+ --
+ EXCEPTION -- not found
+ WHEN OTHERS THEN
+ RETURN 0;
+ END;
+ -- Unlimited 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_Storageonhand s
+ JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)
+ LEFT JOIN M_LocatorType lt ON (l.M_LocatorType_ID=lt.M_LocatorType_ID)
+ WHERE s.M_Product_ID=Product_ID AND l.M_Warehouse_ID=myWarehouse_ID
+ AND COALESCE(lt.IsAvailableForReservation,'Y')='Y';
+ --
+ -- DBMS_OUTPUT.PUT_LINE('Qty=' || ProductQty);
+ RETURN ProductQty;
+ END IF;
+
+ -- Go through 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_Storageonhand s
+ JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)
+ LEFT JOIN M_LocatorType lt ON (l.M_LocatorType_ID=lt.M_LocatorType_ID)
+ WHERE s.M_Product_ID=bom.M_ProductBOM_ID AND l.M_Warehouse_ID=myWarehouse_ID
+ AND COALESCE(lt.IsAvailableForReservation,'Y')='Y';
+ -- How much can we make with this product
+ ProductQty := ProductQty/bom.BOMQty;
+ -- How much can we make overall
+ IF (ProductQty < Quantity) THEN
+ Quantity := ProductQty;
+ END IF;
+ -- Another BOM
+ ELSIF (bom.IsBOM = 'Y' AND bom.IsVerified = 'Y') THEN
+ ProductQty := BomqtyonhandForReservation (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 TRUNC(Quantity, StdPrecision); -- RoundDown
+ END IF;
+ RETURN 0;
+END BOMQTYONHANDFORRESERVATION;
+/
+
+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, p.IsVerified
+ FROM M_PRODUCT_BOM b, M_PRODUCT p
+ WHERE b.M_ProductBOM_ID=p.M_Product_ID
+ AND b.M_Product_ID=Product_ID
+ AND b.M_ProductBOM_ID != Product_ID
+ AND b.IsActive='Y';
+ --
+BEGIN
+ -- Check Parameters
+ myWarehouse_ID := Warehouse_ID;
+ IF (myWarehouse_ID IS NULL) THEN
+ IF (Locator_ID IS NULL) THEN
+ RETURN 0;
+ ELSE
+ SELECT SUM(M_Warehouse_ID) INTO myWarehouse_ID
+ FROM M_LOCATOR
+ WHERE M_Locator_ID=Locator_ID;
+ END IF;
+ END IF;
+ IF (myWarehouse_ID IS NULL) THEN
+ RETURN 0;
+ END IF;
+-- DBMS_OUTPUT.PUT_LINE('Warehouse=' || myWarehouse_ID);
+
+ -- Check, if product exists and if it is stocked
+ BEGIN
+ SELECT IsBOM, ProductType, IsStocked
+ INTO IsBOM, ProductType, IsStocked
+ FROM M_PRODUCT
+ WHERE M_Product_ID=Product_ID;
+ --
+ EXCEPTION -- not found
+ WHEN OTHERS THEN
+ RETURN 0;
+ END;
+ -- Unlimited 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_Storageonhand s
+ JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)
+ WHERE s.M_Product_ID=Product_ID AND l.M_Warehouse_ID=myWarehouse_ID;
+ --
+ -- DBMS_OUTPUT.PUT_LINE('Qty=' || ProductQty);
+ RETURN ProductQty;
+ END IF;
+
+ -- Go through 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_Storageonhand s
+ JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)
+ WHERE s.M_Product_ID=bom.M_ProductBOM_ID AND l.M_Warehouse_ID=myWarehouse_ID;
+ -- How much can we make with this product
+ ProductQty := ProductQty/bom.BOMQty;
+ -- How much can we make overall
+ IF (ProductQty < Quantity) THEN
+ Quantity := ProductQty;
+ END IF;
+ -- Another BOM
+ ELSIF (bom.IsBOM = 'Y' AND BOM.IsVerified = '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 TRUNC(Quantity, StdPrecision); -- RoundDown
+ 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, p.IsVerified
+ FROM M_PRODUCT_BOM b, M_PRODUCT p
+ WHERE b.M_ProductBOM_ID=p.M_Product_ID
+ AND b.M_Product_ID=p_Product_ID
+ AND b.M_ProductBOM_ID != p_Product_ID
+ AND b.IsActive='Y';
+ --
+BEGIN
+ -- Check Parameters
+ v_Warehouse_ID := p_Warehouse_ID;
+ IF (v_Warehouse_ID IS NULL) THEN
+ IF (p_Locator_ID IS NULL) THEN
+ RETURN 0;
+ ELSE
+ SELECT MAX(M_Warehouse_ID) INTO v_Warehouse_ID
+ FROM M_LOCATOR
+ WHERE M_Locator_ID=p_Locator_ID;
+ END IF;
+ END IF;
+ IF (v_Warehouse_ID IS NULL) THEN
+ RETURN 0;
+ END IF;
+-- DBMS_OUTPUT.PUT_LINE('Warehouse=' || v_Warehouse_ID);
+
+ -- Check, if product exists and if it is stocked
+ BEGIN
+ SELECT IsBOM, ProductType, IsStocked
+ INTO v_IsBOM, v_ProductType, v_IsStocked
+ FROM M_PRODUCT
+ WHERE M_Product_ID=p_Product_ID;
+ --
+ EXCEPTION -- not found
+ WHEN OTHERS THEN
+ RETURN 0;
+ END;
+
+ -- No reservation for non-stocked
+ IF (v_IsBOM='N' AND (v_ProductType<>'I' OR v_IsStocked='N')) THEN
+ RETURN 0;
+ -- Stocked item
+ ELSIF (v_IsStocked='Y') THEN
+ -- Get ProductQty
+ SELECT NVL(SUM(Qty), 0)
+ INTO v_ProductQty
+ FROM M_StorageReservation
+ WHERE M_Product_ID=p_Product_ID
+ AND M_Warehouse_ID=v_Warehouse_ID
+ AND IsSOTrx='N'
+ AND IsActive='Y';
+ --
+ RETURN v_ProductQty;
+ END IF;
+
+ -- Go though BOM
+-- DBMS_OUTPUT.PUT_LINE('BOM');
+ FOR bom IN CUR_BOM LOOP
+ -- Stocked Items "leaf node"
+ IF (bom.ProductType = 'I' AND bom.IsStocked = 'Y') THEN
+ -- Get ProductQty
+ SELECT NVL(SUM(Qty), 0)
+ INTO v_ProductQty
+ FROM M_StorageReservation
+ WHERE M_Product_ID=p_Product_ID
+ AND M_Warehouse_ID=v_Warehouse_ID
+ AND IsSOTrx='N'
+ AND IsActive='Y';
+ -- How much can we make with this product
+ v_ProductQty := v_ProductQty/bom.BOMQty;
+ -- How much can we make overall
+ IF (v_ProductQty < v_Quantity) THEN
+ v_Quantity := v_ProductQty;
+ END IF;
+ -- Another BOM
+ ELSIF (bom.IsBOM = 'Y' AND BOM.IsVerified = '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 TRUNC(v_Quantity, v_StdPrecision); -- RoundDown
+ 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, p.IsVerified
+ FROM M_PRODUCT_BOM b, M_PRODUCT p
+ WHERE b.M_ProductBOM_ID=p.M_Product_ID
+ AND b.M_Product_ID=p_Product_ID
+ AND b.M_ProductBOM_ID != p_Product_ID
+ AND b.IsActive='Y';
+ --
+BEGIN
+ -- Check Parameters
+ v_Warehouse_ID := p_Warehouse_ID;
+ IF (v_Warehouse_ID IS NULL) THEN
+ IF (p_Locator_ID IS NULL) THEN
+ RETURN 0;
+ ELSE
+ SELECT MAX(M_Warehouse_ID) INTO v_Warehouse_ID
+ FROM M_LOCATOR
+ WHERE M_Locator_ID=p_Locator_ID;
+ END IF;
+ END IF;
+ IF (v_Warehouse_ID IS NULL) THEN
+ RETURN 0;
+ END IF;
+-- DBMS_OUTPUT.PUT_LINE('Warehouse=' || v_Warehouse_ID);
+
+ -- Check, if product exists and if it is stocked
+ BEGIN
+ SELECT IsBOM, ProductType, IsStocked
+ INTO v_IsBOM, v_ProductType, v_IsStocked
+ FROM M_PRODUCT
+ WHERE M_Product_ID=p_Product_ID;
+ --
+ EXCEPTION -- not found
+ WHEN OTHERS THEN
+ RETURN 0;
+ END;
+
+ -- No reservation for non-stocked
+ IF (v_IsBOM='N' AND (v_ProductType<>'I' OR v_IsStocked='N')) THEN
+ RETURN 0;
+ -- Stocked item
+ ELSIF (v_IsStocked='Y') THEN
+ -- Get ProductQty
+ SELECT NVL(SUM(Qty), 0)
+ INTO v_ProductQty
+ FROM M_StorageReservation
+ WHERE M_Product_ID=p_Product_ID
+ AND M_Warehouse_ID=v_Warehouse_ID
+ AND IsSOTrx='Y'
+ AND IsActive='Y';
+ --
+ RETURN v_ProductQty;
+ END IF;
+
+ -- Go though BOM
+-- DBMS_OUTPUT.PUT_LINE('BOM');
+ FOR bom IN CUR_BOM LOOP
+ -- Stocked Items "leaf node"
+ IF (bom.ProductType = 'I' AND bom.IsStocked = 'Y') THEN
+ -- Get ProductQty
+ SELECT NVL(SUM(Qty), 0)
+ INTO v_ProductQty
+ FROM M_StorageReservation
+ WHERE M_Product_ID=bom.M_ProductBOM_ID
+ AND M_Warehouse_ID=v_Warehouse_ID
+ AND IsSOTrx='Y'
+ AND IsActive='Y';
+ -- How much can we make with this product
+ v_ProductQty := v_ProductQty/bom.BOMQty;
+ -- How much can we make overall
+ IF (v_ProductQty < v_Quantity) THEN
+ v_Quantity := v_ProductQty;
+ END IF;
+ -- Another BOM
+ ELSIF (bom.IsBOM = 'Y' AND BOM.IsVerified = '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 TRUNC(v_Quantity, v_StdPrecision); -- RoundDown
+ END IF;
+ RETURN 0;
+END Bomqtyreserved;
+/
+
diff --git a/migration/iD11/oracle/202412101102_IDEMPIERE-6335.sql b/migration/iD11/oracle/202412101102_IDEMPIERE-6335.sql
new file mode 100644
index 0000000000..57a2a7375d
--- /dev/null
+++ b/migration/iD11/oracle/202412101102_IDEMPIERE-6335.sql
@@ -0,0 +1,10 @@
+-- IDEMPIERE-6335
+SELECT register_migration_script('202412101102_IDEMPIERE-6335.sql') FROM dual;
+
+SET SQLBLANKLINES ON
+SET DEFINE OFF
+
+-- Dec 10, 2024, 11:06:45 AM BRT
+INSERT INTO AD_Process_Para (AD_Process_Para_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,Name,Description,Help,AD_Process_ID,SeqNo,AD_Reference_ID,IsRange,AD_Val_Rule_ID,FieldLength,IsMandatory,ColumnName,IsCentrallyMaintained,EntityType,AD_Element_ID,AD_Process_Para_UU,IsEncrypted,IsAutocomplete,DateRangeOption,IsShowNegateButton) VALUES (200484,0,0,'Y',TO_TIMESTAMP('2024-12-10 11:06:44','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-12-10 11:06:44','YYYY-MM-DD HH24:MI:SS'),100,'Document Type','Document type or rules','The Document Type determines document sequence and processing rules',142,20,19,'N',52054,0,'N','C_DocType_ID','Y','D',196,'2b9ac743-0893-474a-aa96-c1eafe8fba86','N','N','D','N')
+;
+
diff --git a/migration/iD11/postgresql/202402261300_IDEMPIERE-2981.sql b/migration/iD11/postgresql/202402261300_IDEMPIERE-2981.sql
new file mode 100644
index 0000000000..e12163649a
--- /dev/null
+++ b/migration/iD11/postgresql/202402261300_IDEMPIERE-2981.sql
@@ -0,0 +1,7 @@
+-- IDEMPIERE-2981 - Implement JSON Field type
+SELECT register_migration_script('202402261300_IDEMPIERE-2981.sql') FROM dual;
+
+-- Feb 26, 2024, 1:00:29 PM CET
+INSERT INTO AD_Reference (AD_Reference_ID,Name,Description,ValidationType,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,EntityType,IsOrderByValue,AD_Reference_UU,ShowInactive) VALUES (200267,'JSON','JSON format values','D',0,0,'Y',TO_TIMESTAMP('2024-02-26 13:00:28','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-02-26 13:00:28','YYYY-MM-DD HH24:MI:SS'),100,'D','N','b6fcc751-edd8-4421-acd0-3cde02a9576d','N')
+;
+
diff --git a/migration/iD11/postgresql/202402261354_IDEMPIERE-2981.sql b/migration/iD11/postgresql/202402261354_IDEMPIERE-2981.sql
new file mode 100644
index 0000000000..57daa577b4
--- /dev/null
+++ b/migration/iD11/postgresql/202402261354_IDEMPIERE-2981.sql
@@ -0,0 +1,27 @@
+-- IDEMPIERE-2981 - Implement JSON Field type
+SELECT register_migration_script('202402261354_IDEMPIERE-2981.sql') FROM dual;
+
+-- Feb 26, 2024, 1:54:35 PM CET
+INSERT INTO AD_Element (AD_Element_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,ColumnName,Name,Description,PrintName,EntityType,AD_Element_UU) VALUES (203924,0,0,'Y',TO_TIMESTAMP('2024-02-26 13:54:35','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-02-26 13:54:35','YYYY-MM-DD HH24:MI:SS'),100,'JsonData','JSON Data','The json field stores json data.','JSON Data','D','c4ea7a81-96a9-4a5d-bb87-e913e1c8ed48')
+;
+
+-- Feb 26, 2024, 1:55:37 PM CET
+INSERT INTO AD_Column (AD_Column_ID,Version,Name,Description,AD_Table_ID,ColumnName,FieldLength,IsKey,IsParent,IsMandatory,IsTranslated,IsIdentifier,SeqNo,IsEncrypted,AD_Reference_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Element_ID,IsUpdateable,IsSelectionColumn,EntityType,IsSyncDatabase,IsAlwaysUpdateable,IsAutocomplete,IsAllowLogging,AD_Column_UU,IsAllowCopy,SeqNoSelection,IsToolbarButton,IsSecure,IsHtml,IsPartitionKey) VALUES (216570,0,'JSON Data','The json field stores json data.',135,'JsonData',0,'N','N','N','N','N',0,'N',200267,0,0,'Y',TO_TIMESTAMP('2024-02-26 13:55:36','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-02-26 13:55:36','YYYY-MM-DD HH24:MI:SS'),100,203924,'Y','N','D','N','N','N','Y','927b83df-d161-4332-ad44-8ffed99e8cf4','Y',0,'N','N','N','N')
+;
+
+-- Feb 26, 2024, 1:55:55 PM CET
+ALTER TABLE Test ADD COLUMN JsonData JSON DEFAULT NULL
+;
+
+-- Feb 26, 2024, 1:56:08 PM CET
+INSERT INTO AD_Field (AD_Field_ID,Name,Description,AD_Tab_ID,AD_Column_ID,IsDisplayed,DisplayLength,SeqNo,IsSameLine,IsHeading,IsFieldOnly,IsEncrypted,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,IsReadOnly,IsCentrallyMaintained,EntityType,AD_Field_UU,IsDisplayedGrid,SeqNoGrid,ColumnSpan) VALUES (208472,'JSON Data','The json field stores json data.',152,216570,'Y',100,310,'N','N','N','N',0,0,'Y',TO_TIMESTAMP('2024-02-26 13:56:08','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-02-26 13:56:08','YYYY-MM-DD HH24:MI:SS'),100,'N','Y','D','e47ef529-71ba-4f9b-9014-b188e17e8ef4','Y',290,5)
+;
+
+-- Feb 29, 2024, 1:52:50 PM CET
+UPDATE AD_Field SET NumLines=5,Updated=TO_TIMESTAMP('2024-02-29 13:52:50','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=208472
+;
+
+-- Feb 29, 2024, 2:07:30 PM CET
+INSERT INTO AD_Message (MsgType,MsgText,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Message_ID,Value,EntityType,AD_Message_UU) VALUES ('E','Invalid JSON',0,0,'Y',TO_TIMESTAMP('2024-02-29 14:07:30','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-02-29 14:07:30','YYYY-MM-DD HH24:MI:SS'),100,200876,'InvalidJSON','D','a263376f-a12e-4943-92f1-7d7ce8a67a2b')
+;
+
diff --git a/migration/iD11/postgresql/202404171125_IDEMPIERE-6110.sql b/migration/iD11/postgresql/202404171125_IDEMPIERE-6110.sql
new file mode 100644
index 0000000000..8680472e4f
--- /dev/null
+++ b/migration/iD11/postgresql/202404171125_IDEMPIERE-6110.sql
@@ -0,0 +1,7 @@
+-- IDEMPIERE-6110
+SELECT register_migration_script('202404171125_IDEMPIERE-6110.sql') FROM dual;
+
+-- Apr 17, 2024, 11:25:53 AM BRT
+INSERT INTO AD_Message (MsgType,MsgText,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Message_ID,Value,EntityType,AD_Message_UU) VALUES ('E','Products or charges configured as ''Freight Product'' or ''Freight Charge'' cannot be added to this order due to the combination of delivery via rule and freight cost rule',0,0,'Y',TO_TIMESTAMP('2024-04-17 11:25:53','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-04-17 11:25:53','YYYY-MM-DD HH24:MI:SS'),100,200897,'FreightOrderLineNotAllowed','D','204c8bb9-d002-4beb-bbc3-1d1a11d7471d')
+;
+
diff --git a/migration/iD11/postgresql/202404220830_IDEMPIERE-5136_MissingDropIndex.sql b/migration/iD11/postgresql/202404220830_IDEMPIERE-5136_MissingDropIndex.sql
new file mode 100644
index 0000000000..ceb792daea
--- /dev/null
+++ b/migration/iD11/postgresql/202404220830_IDEMPIERE-5136_MissingDropIndex.sql
@@ -0,0 +1,4 @@
+-- IDEMPIERE-5136
+SELECT register_migration_script('202404220830_IDEMPIERE-5136_MissingDropIndex.sql') FROM dual;
+
+-- only for Oracle as there was a missing DROP INDEX instruction
diff --git a/migration/iD11/postgresql/202404302320_IDEMPIERE-6123.sql b/migration/iD11/postgresql/202404302320_IDEMPIERE-6123.sql
new file mode 100644
index 0000000000..ad27fcaac4
--- /dev/null
+++ b/migration/iD11/postgresql/202404302320_IDEMPIERE-6123.sql
@@ -0,0 +1,19 @@
+-- IDEMPIERE-6123 Query in search window causing slowness and load spikes in the database (FHCA-5356)
+SELECT register_migration_script('202404302320_IDEMPIERE-6123.sql') FROM dual;
+
+-- Apr 30, 2024, 11:20:08 PM CEST
+INSERT INTO AD_SysConfig (AD_SysConfig_ID,AD_Client_ID,AD_Org_ID,Created,Updated,CreatedBy,UpdatedBy,IsActive,Name,Value,Description,EntityType,ConfigurationLevel,AD_SysConfig_UU) VALUES (200244,0,0,TO_TIMESTAMP('2024-04-30 23:20:08','YYYY-MM-DD HH24:MI:SS'),TO_TIMESTAMP('2024-04-30 23:20:08','YYYY-MM-DD HH24:MI:SS'),100,100,'Y','GRIDTABLE_INITIAL_COUNT_TIMEOUT_IN_SECONDS','1','Timeout for the initial count on windows','D','C','5fae1af7-74ca-41d8-bbd3-d506c6c23b6a')
+;
+
+-- Apr 30, 2024, 11:22:16 PM CEST
+INSERT INTO AD_SysConfig (AD_SysConfig_ID,AD_Client_ID,AD_Org_ID,Created,Updated,CreatedBy,UpdatedBy,IsActive,Name,Value,Description,EntityType,ConfigurationLevel,AD_SysConfig_UU) VALUES (200245,0,0,TO_TIMESTAMP('2024-04-30 23:22:16','YYYY-MM-DD HH24:MI:SS'),TO_TIMESTAMP('2024-04-30 23:22:16','YYYY-MM-DD HH24:MI:SS'),100,100,'Y','GLOBAL_MAX_QUERY_RECORDS','100000','Maximum number of records allowed to search in a window, can be overriden per Role or Tab','D','C','840fb67c-4609-41f2-9e20-e0ea9d839065')
+;
+
+-- Apr 30, 2024, 11:23:28 PM CEST
+UPDATE AD_Message SET MsgText='The query returned more records than allowed, consider adding more filters.',Updated=TO_TIMESTAMP('2024-04-30 23:23:28','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Message_ID=852
+;
+
+-- Apr 30, 2024, 11:24:06 PM CEST
+INSERT INTO AD_Message (MsgType,MsgText,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Message_ID,Value,EntityType,AD_Message_UU) VALUES ('I','The initial count query timed out, loading records ...',0,0,'Y',TO_TIMESTAMP('2024-04-30 23:24:06','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-04-30 23:24:06','YYYY-MM-DD HH24:MI:SS'),100,200887,'CountQueryTimeoutLoadBackground','D','988292d7-175f-41c2-b560-43d62b8326a9')
+;
+
diff --git a/migration/iD11/postgresql/202405071219_IDEMPIERE-6137.sql b/migration/iD11/postgresql/202405071219_IDEMPIERE-6137.sql
new file mode 100644
index 0000000000..8ea63cd938
--- /dev/null
+++ b/migration/iD11/postgresql/202405071219_IDEMPIERE-6137.sql
@@ -0,0 +1,7 @@
+-- IDEMPIERE-6137 Payment Rule does not appear in reports from Sales Order
+SELECT register_migration_script('202405071219_IDEMPIERE-6137.sql') FROM dual;
+
+-- May 7, 2024, 12:19:00 PM CEST
+UPDATE AD_Column SET AD_Reference_Value_ID=195,Updated=TO_TIMESTAMP('2024-05-07 12:19:00','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Reference_ID=200012 AND COALESCE(AD_Reference_Value_ID,0)!=195
+;
+
diff --git a/migration/iD11/postgresql/202405080101_IDEMPIERE-6123.sql b/migration/iD11/postgresql/202405080101_IDEMPIERE-6123.sql
new file mode 100644
index 0000000000..be168edcfe
--- /dev/null
+++ b/migration/iD11/postgresql/202405080101_IDEMPIERE-6123.sql
@@ -0,0 +1,19 @@
+-- IDEMPIERE-6123 Query in search window causing slowness and load spikes in the database (FHCA-5356)
+SELECT register_migration_script('202405080101_IDEMPIERE-6123.sql') FROM dual;
+
+-- May 8, 2024, 1:01:16 AM CEST
+INSERT INTO AD_SysConfig (AD_SysConfig_ID,AD_Client_ID,AD_Org_ID,Created,Updated,CreatedBy,UpdatedBy,IsActive,Name,Value,Description,EntityType,ConfigurationLevel,AD_SysConfig_UU) VALUES (200247,0,0,TO_TIMESTAMP('2024-05-08 01:01:16','YYYY-MM-DD HH24:MI:SS'),TO_TIMESTAMP('2024-05-08 01:01:16','YYYY-MM-DD HH24:MI:SS'),100,100,'Y','REPORT_LOAD_TIMEOUT_IN_SECONDS','120','Timeout in seconds when loading a report','D','C','14e838b1-c25c-400e-b39c-61da9bf92099')
+;
+
+-- May 8, 2024, 1:01:42 AM CEST
+INSERT INTO AD_SysConfig (AD_SysConfig_ID,AD_Client_ID,AD_Org_ID,Created,Updated,CreatedBy,UpdatedBy,IsActive,Name,Value,Description,EntityType,ConfigurationLevel,AD_SysConfig_UU) VALUES (200248,0,0,TO_TIMESTAMP('2024-05-08 01:01:41','YYYY-MM-DD HH24:MI:SS'),TO_TIMESTAMP('2024-05-08 01:01:41','YYYY-MM-DD HH24:MI:SS'),100,100,'Y','GLOBAL_MAX_REPORT_RECORDS','100000','Max number of records allowed in a report','D','C','7030640a-1aa7-4ac7-a894-b4fe0dfde530')
+;
+
+-- May 8, 2024, 1:06:19 AM CEST
+INSERT INTO AD_Message (MsgType,MsgText,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Message_ID,Value,EntityType,AD_Message_UU) VALUES ('I','The data query for the report took too much time to execute (over {0} seconds) exceeding the allowed limit',0,0,'Y',TO_TIMESTAMP('2024-05-08 01:06:18','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-05-08 01:06:18','YYYY-MM-DD HH24:MI:SS'),100,200893,'ReportQueryTimeout','D','5f17f55f-adbe-4d97-bf83-9447983b4946')
+;
+
+-- May 8, 2024, 1:07:10 AM CEST
+INSERT INTO AD_Message (MsgType,MsgText,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Message_ID,Value,EntityType,AD_Message_UU) VALUES ('I','The report data exceeds the maximum limit of {0} rows',0,0,'Y',TO_TIMESTAMP('2024-05-08 01:07:09','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-05-08 01:07:09','YYYY-MM-DD HH24:MI:SS'),100,200894,'ReportMaxRowsReached','D','a4b55c31-0df0-4302-a62a-91cb7e79be0d')
+;
+
diff --git a/migration/iD11/postgresql/202405131534_IDEMPIERE-6040.sql b/migration/iD11/postgresql/202405131534_IDEMPIERE-6040.sql
new file mode 100644
index 0000000000..962d64dfb4
--- /dev/null
+++ b/migration/iD11/postgresql/202405131534_IDEMPIERE-6040.sql
@@ -0,0 +1,22 @@
+-- IDEMPIERE-6040 Improvements for CSV import template
+SELECT register_migration_script('202405131534_IDEMPIERE-6040.sql') FROM dual;
+
+-- May 13, 2024, 3:34:49 PM CEST
+UPDATE AD_Ref_List SET Name='Comma-separated values (CSV)',Updated=TO_TIMESTAMP('2024-05-13 15:34:49','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Ref_List_ID=200704
+;
+
+-- May 13, 2024, 3:35:58 PM CEST
+UPDATE AD_Ref_List SET Name='Excel (XLS/XLSX)',Updated=TO_TIMESTAMP('2024-05-13 15:35:58','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Ref_List_ID=200706
+;
+
+-- May 13, 2024, 3:36:02 PM CEST
+UPDATE AD_Ref_List SET IsActive='N',Updated=TO_TIMESTAMP('2024-05-13 15:36:02','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Ref_List_ID=200705
+;
+
+UPDATE AD_ImportTemplate SET ImportTemplateType='XLSX' WHERE ImportTemplateType='XLS'
+;
+
+-- May 13, 2024, 3:59:49 PM CEST
+UPDATE AD_Field SET SeqNo=120, ColumnSpan=2,Updated=TO_TIMESTAMP('2024-05-13 15:59:49','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=208476
+;
+
diff --git a/migration/iD11/postgresql/202405151228_IDEMPIERE-5728.sql b/migration/iD11/postgresql/202405151228_IDEMPIERE-5728.sql
new file mode 100644
index 0000000000..e5da84f8db
--- /dev/null
+++ b/migration/iD11/postgresql/202405151228_IDEMPIERE-5728.sql
@@ -0,0 +1,7 @@
+-- IDEMPIERE-5728
+SELECT register_migration_script('202405151228_IDEMPIERE-5728.sql') FROM dual;
+
+-- May 15, 2024, 12:28:30 PM CEST
+UPDATE AD_ViewColumn SET ColumnName='RV_UnPosted_UU',Updated=TO_TIMESTAMP('2024-05-15 12:28:30','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_ViewColumn_ID=217690
+;
+
diff --git a/migration/iD11/postgresql/202406072107_IDEMPIERE-6166.sql b/migration/iD11/postgresql/202406072107_IDEMPIERE-6166.sql
new file mode 100644
index 0000000000..9d12bb5b3d
--- /dev/null
+++ b/migration/iD11/postgresql/202406072107_IDEMPIERE-6166.sql
@@ -0,0 +1,30 @@
+-- IDEMPIERE-6166 PostgreSQL DUAL table with more than one record
+SELECT register_migration_script('202406072107_IDEMPIERE-6166.sql') FROM dual;
+
+DROP TABLE IF EXISTS dual
+;
+
+CREATE VIEW dual AS SELECT 'X'::varchar AS dummy
+;
+
+DROP RULE insert_dbreplicasyncverifier ON dbreplicasyncverifier
+;
+
+CREATE OR REPLACE FUNCTION forbid_multiple_rows_in_dbreplicasyncverifier()
+RETURNS TRIGGER AS $$
+BEGIN
+ -- Check if the table already contains a row
+ IF (SELECT COUNT(*) FROM dbreplicasyncverifier) > 0 THEN
+ RAISE EXCEPTION 'Table dbreplicasyncverifier can only contain one row.';
+ END IF;
+ RETURN NEW;
+END;
+$$ LANGUAGE plpgsql
+;
+
+CREATE TRIGGER single_row_only_trigger_dbreplicasyncverifier
+BEFORE INSERT ON dbreplicasyncverifier
+FOR EACH ROW
+EXECUTE FUNCTION forbid_multiple_rows_in_dbreplicasyncverifier()
+;
+
diff --git a/migration/iD11/postgresql/202406072206_IDEMPIERE-6167.sql b/migration/iD11/postgresql/202406072206_IDEMPIERE-6167.sql
new file mode 100644
index 0000000000..74210abf71
--- /dev/null
+++ b/migration/iD11/postgresql/202406072206_IDEMPIERE-6167.sql
@@ -0,0 +1,117 @@
+-- IDEMPIERE-6167 BOM Price List must search just for Verified BOMs
+SELECT register_migration_script('202406072206_IDEMPIERE-6167.sql') FROM dual;
+
+CREATE OR REPLACE FUNCTION bompricelimit (in product_id numeric, in pricelist_version_id numeric) RETURNS numeric AS
+$BODY$
+DECLARE
+ v_Price NUMERIC;
+ v_ProductPrice NUMERIC;
+ bom RECORD;
+
+BEGIN
+ -- Try to get price from PriceList directly
+ SELECT COALESCE (SUM(PriceLimit), 0)
+ INTO v_Price
+ FROM M_ProductPrice
+ WHERE M_PriceList_Version_ID=PriceList_Version_ID AND M_Product_ID=Product_ID;
+
+ -- No Price - Check if BOM
+ IF (v_Price = 0) THEN
+ FOR bom IN
+ SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM
+ FROM M_Product_BOM b, M_Product p
+ WHERE b.M_ProductBOM_ID=p.M_Product_ID
+ AND b.M_Product_ID=Product_ID
+ AND b.M_ProductBOM_ID != Product_ID
+ AND p.IsVerified='Y'
+ AND b.IsActive='Y'
+ LOOP
+ v_ProductPrice := bomPriceLimit (bom.M_ProductBOM_ID, PriceList_Version_ID);
+ v_Price := v_Price + (bom.BOMQty * v_ProductPrice);
+ END LOOP;
+ END IF;
+ --
+ RETURN v_Price;
+
+END;
+
+$BODY$
+LANGUAGE 'plpgsql' STABLE
+;
+
+CREATE OR REPLACE FUNCTION bompricelist (in product_id numeric, in pricelist_version_id numeric) RETURNS numeric AS
+$BODY$
+DECLARE
+ v_Price NUMERIC;
+ v_ProductPrice NUMERIC;
+ bom RECORD;
+
+BEGIN
+ -- Try to get price from pricelist directly
+ SELECT COALESCE (SUM(PriceList), 0)
+ INTO v_Price
+ FROM M_ProductPrice
+ WHERE M_PriceList_Version_ID=PriceList_Version_ID AND M_Product_ID=Product_ID;
+
+ -- No Price - Check if BOM
+ IF (v_Price = 0) THEN
+ FOR bom IN
+ SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM
+ FROM M_Product_BOM b, M_Product p
+ WHERE b.M_ProductBOM_ID=p.M_Product_ID
+ AND b.M_Product_ID=Product_ID
+ AND b.M_ProductBOM_ID != Product_ID
+ AND p.IsVerified='Y'
+ AND b.IsActive='Y'
+ LOOP
+ v_ProductPrice := bomPriceList (bom.M_ProductBOM_ID, PriceList_Version_ID);
+ v_Price := v_Price + (bom.BOMQty * v_ProductPrice);
+ END LOOP;
+ END IF;
+ --
+ RETURN v_Price;
+
+END;
+
+$BODY$
+LANGUAGE 'plpgsql' STABLE
+;
+
+CREATE OR REPLACE FUNCTION bompricestd (in product_id numeric, in pricelist_version_id numeric) RETURNS numeric AS
+$BODY$
+DECLARE
+ v_Price NUMERIC;
+ v_ProductPrice NUMERIC;
+ bom RECORD;
+
+BEGIN
+ -- Try to get price from PriceList directly
+ SELECT COALESCE(SUM(PriceStd), 0)
+ INTO v_Price
+ FROM M_ProductPrice
+ WHERE M_PriceList_Version_ID=PriceList_Version_ID AND M_Product_ID=Product_ID;
+
+ -- No Price - Check if BOM
+ IF (v_Price = 0) THEN
+ FOR bom IN
+ SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM
+ FROM M_Product_BOM b, M_Product p
+ WHERE b.M_ProductBOM_ID=p.M_Product_ID
+ AND b.M_Product_ID=Product_ID
+ AND b.M_ProductBOM_ID != Product_ID
+ AND p.IsVerified='Y'
+ AND b.IsActive='Y'
+ LOOP
+ v_ProductPrice := bomPriceStd (bom.M_ProductBOM_ID, PriceList_Version_ID);
+ v_Price := v_Price + (bom.BOMQty * v_ProductPrice);
+ END LOOP;
+ END IF;
+ --
+ RETURN v_Price;
+
+END;
+
+$BODY$
+LANGUAGE 'plpgsql' STABLE
+;
+
diff --git a/migration/iD11/postgresql/202406131736_IDEMPIERE-6169.sql b/migration/iD11/postgresql/202406131736_IDEMPIERE-6169.sql
new file mode 100644
index 0000000000..f1c233ddaf
--- /dev/null
+++ b/migration/iD11/postgresql/202406131736_IDEMPIERE-6169.sql
@@ -0,0 +1,9 @@
+-- IDEMPIERE-6169 Performance on AD_ChangeLog with Record_UU
+SELECT register_migration_script('202406131736_IDEMPIERE-6169.sql') FROM dual;
+
+ALTER TABLE ad_changelog DROP CONSTRAINT ad_changelog_pkey
+;
+
+ALTER TABLE ad_changelog ADD CONSTRAINT ad_changelog_pkey PRIMARY KEY (ad_session_id, ad_column_id, ad_changelog_id)
+;
+
diff --git a/migration/iD11/postgresql/202406180027_IDEMPIERE-6176.sql b/migration/iD11/postgresql/202406180027_IDEMPIERE-6176.sql
new file mode 100644
index 0000000000..3c5551609e
--- /dev/null
+++ b/migration/iD11/postgresql/202406180027_IDEMPIERE-6176.sql
@@ -0,0 +1,19 @@
+-- IDEMPIERE-6176 UUID indexes without constraint
+SELECT register_migration_script('202406180027_IDEMPIERE-6176.sql') FROM dual;
+
+-- Jun 18, 2024, 12:29:20 AM CEST
+UPDATE AD_IndexColumn SET IsActive='N',Updated=TO_TIMESTAMP('2024-06-18 00:29:20','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_IndexColumn_ID=200980
+;
+
+-- Jun 18, 2024, 12:29:27 AM CEST
+UPDATE AD_TableIndex SET IsActive='N', IsCreateConstraint='Y',Updated=TO_TIMESTAMP('2024-06-18 00:29:27','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_TableIndex_ID=200806
+;
+
+-- Jun 18, 2024, 12:30:44 AM CEST
+UPDATE AD_TableIndex SET IsCreateConstraint='Y',Updated=TO_TIMESTAMP('2024-06-18 00:30:44','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_TableIndex_ID=201272
+;
+
+-- Jun 18, 2024, 12:31:49 AM CEST
+UPDATE AD_TableIndex SET IsCreateConstraint='Y',Updated=TO_TIMESTAMP('2024-06-18 00:31:49','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_TableIndex_ID=201275
+;
+
diff --git a/migration/iD11/postgresql/202407061229_IDEMPIERE-6188.sql b/migration/iD11/postgresql/202407061229_IDEMPIERE-6188.sql
new file mode 100644
index 0000000000..069052d6ad
--- /dev/null
+++ b/migration/iD11/postgresql/202407061229_IDEMPIERE-6188.sql
@@ -0,0 +1,47 @@
+-- IDEMPIERE-6188 Read-Only Session
+SELECT register_migration_script('202407061229_IDEMPIERE-6188.sql') FROM dual;
+
+-- Jul 6, 2024, 12:29:56 PM CEST
+INSERT INTO AD_Element (AD_Element_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,ColumnName,Name,Description,Help,PrintName,EntityType,AD_Element_UU) VALUES (203938,0,0,'Y',TO_TIMESTAMP('2024-07-06 12:29:42','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-07-06 12:29:42','YYYY-MM-DD HH24:MI:SS'),100,'IsReadOnlySession','Read Only Session',NULL,NULL,'Read Only Session','D','6ee179e4-9573-4a3d-8815-23723ef241d7')
+;
+
+-- Jul 6, 2024, 12:30:19 PM CEST
+INSERT INTO AD_Column (AD_Column_ID,Version,Name,AD_Table_ID,ColumnName,DefaultValue,FieldLength,IsKey,IsParent,IsMandatory,IsTranslated,IsIdentifier,SeqNo,IsEncrypted,AD_Reference_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Element_ID,IsUpdateable,IsSelectionColumn,EntityType,IsSyncDatabase,IsAlwaysUpdateable,IsAutocomplete,IsAllowLogging,AD_Column_UU,IsAllowCopy,SeqNoSelection,IsToolbarButton,IsSecure,IsHtml,IsPartitionKey) VALUES (216614,0,'Read Only Session',200174,'IsReadOnlySession','N',1,'N','N','Y','N','N',0,'N',20,0,0,'Y',TO_TIMESTAMP('2024-07-06 12:30:18','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-07-06 12:30:18','YYYY-MM-DD HH24:MI:SS'),100,203938,'Y','N','D','N','N','N','Y','8c255baa-fbec-4a90-a43d-5461060368e9','Y',0,'N','N','N','N')
+;
+
+-- Jul 6, 2024, 12:30:29 PM CEST
+ALTER TABLE AD_UserPreference ADD COLUMN IsReadOnlySession CHAR(1) DEFAULT 'N' CHECK (IsReadOnlySession IN ('Y','N')) NOT NULL
+;
+
+-- Jul 6, 2024, 12:30:45 PM CEST
+INSERT INTO AD_Field (AD_Field_ID,Name,AD_Tab_ID,AD_Column_ID,IsDisplayed,DisplayLength,SeqNo,IsSameLine,IsHeading,IsFieldOnly,IsEncrypted,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,IsReadOnly,IsCentrallyMaintained,EntityType,AD_Field_UU,IsDisplayedGrid,SeqNoGrid,XPosition,ColumnSpan) VALUES (208494,'Read Only Session',200189,216614,'Y',1,170,'N','N','N','N',0,0,'Y',TO_TIMESTAMP('2024-07-06 12:30:45','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-07-06 12:30:45','YYYY-MM-DD HH24:MI:SS'),100,'N','Y','D','f69131e7-4fca-4011-89d5-650ca506c920','Y',170,2,2)
+;
+
+-- Jul 6, 2024, 12:31:06 PM CEST
+UPDATE AD_Field SET SeqNo=110,Updated=TO_TIMESTAMP('2024-07-06 12:31:06','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=206133
+;
+
+-- Jul 6, 2024, 12:31:06 PM CEST
+UPDATE AD_Field SET SeqNo=120,Updated=TO_TIMESTAMP('2024-07-06 12:31:06','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=206134
+;
+
+-- Jul 6, 2024, 12:31:06 PM CEST
+UPDATE AD_Field SET SeqNo=130,Updated=TO_TIMESTAMP('2024-07-06 12:31:06','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=206407
+;
+
+-- Jul 6, 2024, 12:31:06 PM CEST
+UPDATE AD_Field SET SeqNo=140,Updated=TO_TIMESTAMP('2024-07-06 12:31:06','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=208189
+;
+
+-- Jul 6, 2024, 12:31:06 PM CEST
+UPDATE AD_Field SET IsDisplayed='Y', SeqNo=150, XPosition=5,Updated=TO_TIMESTAMP('2024-07-06 12:31:06','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=208494
+;
+
+-- Jul 6, 2024, 12:31:25 PM CEST
+UPDATE AD_Field SET IsQuickEntry='Y',Updated=TO_TIMESTAMP('2024-07-06 12:31:25','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=208494
+;
+
+-- Jul 9, 2024, 7:48:40 PM CEST
+INSERT INTO AD_Message (MsgType,MsgText,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Message_ID,Value,EntityType,AD_Message_UU) VALUES ('I','Read-only session',0,0,'Y',TO_TIMESTAMP('2024-07-09 19:48:39','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-07-09 19:48:39','YYYY-MM-DD HH24:MI:SS'),100,200900,'ReadOnlySession','D','dc819f55-662d-4d30-b68d-0a93b46a8458')
+;
+
diff --git a/migration/iD11/postgresql/202407231647_IDEMPIERE-6196.sql b/migration/iD11/postgresql/202407231647_IDEMPIERE-6196.sql
new file mode 100644
index 0000000000..79820482c8
--- /dev/null
+++ b/migration/iD11/postgresql/202407231647_IDEMPIERE-6196.sql
@@ -0,0 +1,15 @@
+-- IDEMPIERE-6196
+SELECT register_migration_script('202407231647_IDEMPIERE-6196.sql') FROM dual;
+
+-- Jul 23, 2024, 4:47:40 PM CEST
+UPDATE AD_Column SET SeqNoSelection=30,Updated=TO_TIMESTAMP('2024-07-23 16:47:40','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=10 WHERE AD_Column_ID=103
+;
+
+-- Jul 23, 2024, 4:47:43 PM CEST
+UPDATE AD_Column SET SeqNoSelection=20,Updated=TO_TIMESTAMP('2024-07-23 16:47:43','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=10 WHERE AD_Column_ID=102
+;
+
+-- Jul 23, 2024, 4:47:52 PM CEST
+UPDATE AD_Column SET SeqNoSelection=10,Updated=TO_TIMESTAMP('2024-07-23 16:47:52','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=10 WHERE AD_Column_ID=107
+;
+
diff --git a/migration/iD11/postgresql/202408191316_IDEMPIERE-6189.sql b/migration/iD11/postgresql/202408191316_IDEMPIERE-6189.sql
new file mode 100644
index 0000000000..8ee6a91ad5
--- /dev/null
+++ b/migration/iD11/postgresql/202408191316_IDEMPIERE-6189.sql
@@ -0,0 +1,15 @@
+-- IDEMPIERE-6189 Performance: queries on RV_C_Invoice doing unnecesary index or seq scan
+SELECT register_migration_script('202408191316_IDEMPIERE-6189.sql') FROM dual;
+
+-- Aug 19, 2024, 1:16:19 PM CEST
+UPDATE AD_ViewComponent SET FromClause='FROM c_invoice i
+LEFT JOIN c_doctype d ON i.c_doctype_id = d.c_doctype_id
+LEFT JOIN c_bpartner b ON i.c_bpartner_id = b.c_bpartner_id
+LEFT JOIN c_bpartner_location bpl ON i.c_bpartner_location_id = bpl.c_bpartner_location_id
+LEFT JOIN c_location loc ON bpl.c_location_id = loc.c_location_id',Updated=TO_TIMESTAMP('2024-08-19 13:16:19','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_ViewComponent_ID=200116
+;
+
+-- Aug 19, 2024, 1:16:27 PM CEST
+CREATE OR REPLACE VIEW RV_C_Invoice(C_Invoice_ID, AD_Client_ID, AD_Org_ID, IsActive, Created, CreatedBy, Updated, UpdatedBy, IsSOTrx, DocumentNo, DocStatus, DocAction, IsPrinted, IsDiscountPrinted, Processing, Processed, IsTransferred, IsPaid, C_DocType_ID, C_DocTypeTarget_ID, C_Order_ID, Description, IsApproved, SalesRep_ID, DateInvoiced, DatePrinted, DateAcct, C_BPartner_ID, C_BPartner_Location_ID, AD_User_ID, C_BP_Group_ID, POReference, DateOrdered, C_Currency_ID, C_ConversionType_ID, PaymentRule, C_PaymentTerm_ID, M_PriceList_ID, C_Campaign_ID, C_Project_ID, C_Activity_ID, IsPayScheduleValid, InvoiceCollectionType, C_Country_ID, C_Region_ID, Postal, City, C_Charge_ID, ChargeAmt, TotalLines, GrandTotal, Multiplier, C_Invoice_AD_OrgTrx_ID, C_Invoice_C_ConversionType_ID, C_DunningLevel_ID, C_Payment_ID, c_invoice_dateordered, DunningGrace, GenerateTo, IsInDispute, c_invoice_ispayschedulevalid, c_invoice_isselfservice, IsTaxIncluded, M_RMA_ID, Posted, ProcessedOn, Ref_Invoice_ID, Reversal_ID, SendEMail, User1_ID, User2_ID, c_bp_acqusitioncost, c_bp_actuallifetimevalue, c_bp_ad_language, C_BP_AD_OrgBP_ID, C_BP_AD_Org_ID, C_BP_BPartner_Parent_ID, C_BP_C_Dunning_ID, C_BP_C_Greeting_ID, C_BP_C_InvoiceSchedule_ID, C_BP_C_PaymentTerm_ID, c_bp_created, C_BP_CreatedBy, C_BP_C_TaxGroup_ID, c_bp_deliveryrule, c_bp_deliveryviarule, c_bp_description, c_bp_dunninggrace, c_bp_duns, c_bp_firstsale, c_bp_flatdiscount, c_bp_freightcostrule, c_bp_invoicerule, c_bp_isactive, c_bp_iscustomer, c_bp_isdiscountprinted, c_bp_isemployee, c_bp_ismanufacturer, c_bp_isonetime, c_bp_ispotaxexempt, c_bp_isprospect, c_bp_issalesrep, c_bp_issummary, c_bp_istaxexempt, c_bp_isvendor, C_BP_Logo_ID, C_BP_M_DiscountSchema_ID, C_BP_M_PriceList_ID, c_bp_naics, c_bp_name, c_bp_name2, c_bp_numberemployees, c_bp_paymentrule, c_bp_paymentrulepo, C_BP_PO_DiscountSchema_ID, C_BP_PO_PaymentTerm_ID, C_BP_PO_PriceList_ID, c_bp_poreference, c_bp_potentiallifetimevalue, c_bp_rating, c_bp_referenceno, C_BP_SalesRep_ID, c_bp_salesvolume, c_bp_sendemail, c_bp_shareofcustomer, c_bp_shelflifeminpct, c_bp_so_creditlimit, c_bp_socreditstatus, c_bp_so_creditused, c_bp_so_description, TaxID, c_bp_totalopenbalance, c_bp_updated, C_BP_UpdatedBy, c_bp_url, c_bp_value, C_BP_Location_AD_Org_ID, C_BP_Location_C_BPartner_ID, C_BP_Location_C_Location_ID, c_bp_location_created, C_BP_Location_CreatedBy, C_SalesRegion_ID, c_bp_location_fax, c_bp_location_isactive, IsBillTo, ISDN, IsPayFrom, IsRemitTo, IsShipTo, c_bp_location_name, c_bp_location_phone, c_bp_location_phone2, c_bp_location_updated, C_BP_Location_UpdatedBy, Address1, Address2, Address3, Address4, C_Location_AD_Org_ID, C_City_ID, c_location_created, C_Location_CreatedBy, c_location_isactive, Postal_Add, RegionName, c_location_updated, C_Location_UpdatedBy) AS SELECT i.c_invoice_id AS C_Invoice_ID, i.ad_client_id AS AD_Client_ID, i.ad_org_id AS AD_Org_ID, i.isactive AS IsActive, i.created AS Created, i.createdby AS CreatedBy, i.updated AS Updated, i.updatedby AS UpdatedBy, i.issotrx AS IsSOTrx, i.documentno AS DocumentNo, i.docstatus AS DocStatus, i.docaction AS DocAction, i.isprinted AS IsPrinted, i.isdiscountprinted AS IsDiscountPrinted, i.processing AS Processing, i.processed AS Processed, i.istransferred AS IsTransferred, i.ispaid AS IsPaid, i.c_doctype_id AS C_DocType_ID, i.c_doctypetarget_id AS C_DocTypeTarget_ID, i.c_order_id AS C_Order_ID, i.description AS Description, i.isapproved AS IsApproved, i.salesrep_id AS SalesRep_ID, i.dateinvoiced AS DateInvoiced, i.dateprinted AS DatePrinted, i.dateacct AS DateAcct, i.c_bpartner_id AS C_BPartner_ID, i.c_bpartner_location_id AS C_BPartner_Location_ID, i.ad_user_id AS AD_User_ID, b.c_bp_group_id AS C_BP_Group_ID, i.poreference AS POReference, i.dateordered AS DateOrdered, i.c_currency_id AS C_Currency_ID, i.c_conversiontype_id AS C_ConversionType_ID, i.paymentrule AS PaymentRule, i.c_paymentterm_id AS C_PaymentTerm_ID, i.m_pricelist_id AS M_PriceList_ID, i.c_campaign_id AS C_Campaign_ID, i.c_project_id AS C_Project_ID, i.c_activity_id AS C_Activity_ID, i.ispayschedulevalid AS IsPayScheduleValid, i.invoicecollectiontype AS InvoiceCollectionType, loc.c_country_id AS C_Country_ID, loc.c_region_id AS C_Region_ID, loc.postal AS Postal, loc.city AS City, i.c_charge_id AS C_Charge_ID, CASE WHEN charat(d.docbasetype, 3) = 'C' THEN i.chargeamt * '-1' ELSE i.chargeamt END AS ChargeAmt, CASE WHEN charat(d.docbasetype, 3) = 'C' THEN i.totallines * '-1' ELSE i.totallines END AS TotalLines, CASE WHEN charat(d.docbasetype, 3) = 'C' THEN i.grandtotal * '-1' ELSE i.grandtotal END AS GrandTotal, CASE WHEN charat(d.docbasetype, 3) = 'C' THEN -1 ELSE 1 END AS Multiplier, i.ad_orgtrx_id AS C_Invoice_AD_OrgTrx_ID, i.c_conversiontype_id AS C_Invoice_C_ConversionType_ID, i.c_dunninglevel_id AS C_DunningLevel_ID, i.c_payment_id AS C_Payment_ID, i.dateordered AS c_invoice_dateordered, i.dunninggrace AS DunningGrace, i.generateto AS GenerateTo, i.isindispute AS IsInDispute, i.ispayschedulevalid AS c_invoice_ispayschedulevalid, i.isselfservice AS c_invoice_isselfservice, i.istaxincluded AS IsTaxIncluded, i.m_rma_id AS M_RMA_ID, i.posted AS Posted, i.processedon AS ProcessedOn, i.ref_invoice_id AS Ref_Invoice_ID, i.reversal_id AS Reversal_ID, i.sendemail AS SendEMail, i.user1_id AS User1_ID, i.user2_id AS User2_ID, b.acqusitioncost AS c_bp_acqusitioncost, b.actuallifetimevalue AS c_bp_actuallifetimevalue, b.ad_language AS c_bp_ad_language, b.ad_orgbp_id AS C_BP_AD_OrgBP_ID, b.ad_org_id AS C_BP_AD_Org_ID, b.bpartner_parent_id AS C_BP_BPartner_Parent_ID, b.c_dunning_id AS C_BP_C_Dunning_ID, b.c_greeting_id AS C_BP_C_Greeting_ID, b.c_invoiceschedule_id AS C_BP_C_InvoiceSchedule_ID, b.c_paymentterm_id AS C_BP_C_PaymentTerm_ID, b.created AS c_bp_created, b.createdby AS C_BP_CreatedBy, b.c_taxgroup_id AS C_BP_C_TaxGroup_ID, b.deliveryrule AS c_bp_deliveryrule, b.deliveryviarule AS c_bp_deliveryviarule, b.description AS c_bp_description, b.dunninggrace AS c_bp_dunninggrace, b.duns AS c_bp_duns, b.firstsale AS c_bp_firstsale, b.flatdiscount AS c_bp_flatdiscount, b.freightcostrule AS c_bp_freightcostrule, b.invoicerule AS c_bp_invoicerule, b.isactive AS c_bp_isactive, b.iscustomer AS c_bp_iscustomer, b.isdiscountprinted AS c_bp_isdiscountprinted, b.isemployee AS c_bp_isemployee, b.ismanufacturer AS c_bp_ismanufacturer, b.isonetime AS c_bp_isonetime, b.ispotaxexempt AS c_bp_ispotaxexempt, b.isprospect AS c_bp_isprospect, b.issalesrep AS c_bp_issalesrep, b.issummary AS c_bp_issummary, b.istaxexempt AS c_bp_istaxexempt, b.isvendor AS c_bp_isvendor, b.logo_id AS C_BP_Logo_ID, b.m_discountschema_id AS C_BP_M_DiscountSchema_ID, b.m_pricelist_id AS C_BP_M_PriceList_ID, b.naics AS c_bp_naics, b.name AS c_bp_name, b.name2 AS c_bp_name2, b.numberemployees AS c_bp_numberemployees, b.paymentrule AS c_bp_paymentrule, b.paymentrulepo AS c_bp_paymentrulepo, b.po_discountschema_id AS C_BP_PO_DiscountSchema_ID, b.po_paymentterm_id AS C_BP_PO_PaymentTerm_ID, b.po_pricelist_id AS C_BP_PO_PriceList_ID, b.poreference AS c_bp_poreference, b.potentiallifetimevalue AS c_bp_potentiallifetimevalue, b.rating AS c_bp_rating, b.referenceno AS c_bp_referenceno, b.salesrep_id AS C_BP_SalesRep_ID, b.salesvolume AS c_bp_salesvolume, b.sendemail AS c_bp_sendemail, b.shareofcustomer AS c_bp_shareofcustomer, b.shelflifeminpct AS c_bp_shelflifeminpct, b.so_creditlimit AS c_bp_so_creditlimit, b.socreditstatus AS c_bp_socreditstatus, b.so_creditused AS c_bp_so_creditused, b.so_description AS c_bp_so_description, b.taxid AS TaxID, b.totalopenbalance AS c_bp_totalopenbalance, b.updated AS c_bp_updated, b.updatedby AS C_BP_UpdatedBy, b.url AS c_bp_url, b.value AS c_bp_value, bpl.ad_org_id AS C_BP_Location_AD_Org_ID, bpl.c_bpartner_id AS C_BP_Location_C_BPartner_ID, bpl.c_location_id AS C_BP_Location_C_Location_ID, bpl.created AS c_bp_location_created, bpl.createdby AS C_BP_Location_CreatedBy, bpl.c_salesregion_id AS C_SalesRegion_ID, bpl.fax AS c_bp_location_fax, bpl.isactive AS c_bp_location_isactive, bpl.isbillto AS IsBillTo, bpl.isdn AS ISDN, bpl.ispayfrom AS IsPayFrom, bpl.isremitto AS IsRemitTo, bpl.isshipto AS IsShipTo, bpl.name AS c_bp_location_name, bpl.phone AS c_bp_location_phone, bpl.phone2 AS c_bp_location_phone2, bpl.updated AS c_bp_location_updated, bpl.updatedby AS C_BP_Location_UpdatedBy, loc.address1 AS Address1, loc.address2 AS Address2, loc.address3 AS Address3, loc.address4 AS Address4, loc.ad_org_id AS C_Location_AD_Org_ID, loc.c_city_id AS C_City_ID, loc.created AS c_location_created, loc.createdby AS C_Location_CreatedBy, loc.isactive AS c_location_isactive, loc.postal_add AS Postal_Add, loc.regionname AS RegionName, loc.updated AS c_location_updated, loc.updatedby AS C_Location_UpdatedBy FROM c_invoice i LEFT JOIN c_doctype d ON i.c_doctype_id = d.c_doctype_id LEFT JOIN c_bpartner b ON i.c_bpartner_id = b.c_bpartner_id LEFT JOIN c_bpartner_location bpl ON i.c_bpartner_location_id = bpl.c_bpartner_location_id LEFT JOIN c_location loc ON bpl.c_location_id = loc.c_location_id
+;
+
diff --git a/migration/iD11/postgresql/202408201236_IDEMPIERE-6216.sql b/migration/iD11/postgresql/202408201236_IDEMPIERE-6216.sql
new file mode 100644
index 0000000000..f88dfade9b
--- /dev/null
+++ b/migration/iD11/postgresql/202408201236_IDEMPIERE-6216.sql
@@ -0,0 +1,7 @@
+-- IDEMPIERE-6216
+SELECT register_migration_script('202408201236_IDEMPIERE-6216.sql') FROM dual;
+
+-- Aug 20, 2024, 12:36:08 PM CEST
+UPDATE AD_Process_Para SET MandatoryLogic='@UseDefaultCoA@=N',Updated=TO_TIMESTAMP('2024-08-20 12:36:08','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Process_Para_ID=53296
+;
+
diff --git a/migration/iD11/postgresql/202409041533_IDEMPIERE-6223.sql b/migration/iD11/postgresql/202409041533_IDEMPIERE-6223.sql
new file mode 100644
index 0000000000..faa51c3861
--- /dev/null
+++ b/migration/iD11/postgresql/202409041533_IDEMPIERE-6223.sql
@@ -0,0 +1,91 @@
+-- Adding new fields to AD_PInstance and AD_PInstance_Log
+SELECT register_migration_script('202409041533_IDEMPIERE-6223.sql') FROM dual;
+
+-- Sep 4, 2024, 3:33:12 PM BRT
+INSERT INTO AD_Column (AD_Column_ID,Version,Name,Description,AD_Table_ID,ColumnName,FieldLength,IsKey,IsParent,IsMandatory,IsTranslated,IsIdentifier,SeqNo,IsEncrypted,AD_Reference_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Element_ID,IsUpdateable,IsSelectionColumn,EntityType,IsSyncDatabase,IsAlwaysUpdateable,IsAutocomplete,IsAllowLogging,AD_Column_UU,IsAllowCopy,SeqNoSelection,IsToolbarButton,IsSecure,FKConstraintType,IsHtml,IsPartitionKey) VALUES (216788,0,'JSON Data','The json field stores json data.',282,'JsonData',0,'N','N','N','N','N',0,'N',200267,0,0,'Y',TO_TIMESTAMP('2024-09-04 15:33:12','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-09-04 15:33:12','YYYY-MM-DD HH24:MI:SS'),100,203924,'Y','N','D','N','N','N','Y','6ec3e423-205d-482f-b7e2-3dea6b66d5d0','Y',0,'N','N','N','N','N')
+;
+
+-- Sep 4, 2024, 3:33:16 PM BRT
+ALTER TABLE AD_PInstance ADD COLUMN JsonData JSON DEFAULT NULL
+;
+
+-- Sep 4, 2024, 3:33:39 PM BRT
+INSERT INTO AD_Field (AD_Field_ID,Name,Description,AD_Tab_ID,AD_Column_ID,IsDisplayed,DisplayLength,SeqNo,IsSameLine,IsHeading,IsFieldOnly,IsEncrypted,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,IsReadOnly,IsCentrallyMaintained,EntityType,AD_Field_UU,IsDisplayedGrid,SeqNoGrid,ColumnSpan,NumLines) VALUES (208511,'JSON Data','The json field stores json data.',663,216788,'Y',0,210,'N','N','N','N',0,0,'Y',TO_TIMESTAMP('2024-09-04 15:33:39','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-09-04 15:33:39','YYYY-MM-DD HH24:MI:SS'),100,'N','Y','D','2455e488-36a8-48d2-8410-2ff0808915e4','Y',200,2,5)
+;
+
+-- Sep 4, 2024, 3:34:12 PM BRT
+UPDATE AD_Field SET IsDisplayed='Y', SeqNo=110, XPosition=1, ColumnSpan=5,Updated=TO_TIMESTAMP('2024-09-04 15:34:12','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=208511
+;
+
+-- Sep 4, 2024, 3:34:12 PM BRT
+UPDATE AD_Field SET SeqNo=120,Updated=TO_TIMESTAMP('2024-09-04 15:34:12','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=10501
+;
+
+-- Sep 4, 2024, 3:34:12 PM BRT
+UPDATE AD_Field SET SeqNo=130,Updated=TO_TIMESTAMP('2024-09-04 15:34:12','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=207416
+;
+
+-- Sep 4, 2024, 3:34:12 PM BRT
+UPDATE AD_Field SET SeqNo=140,Updated=TO_TIMESTAMP('2024-09-04 15:34:12','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=10495
+;
+
+-- Sep 4, 2024, 3:34:12 PM BRT
+UPDATE AD_Field SET SeqNo=150,Updated=TO_TIMESTAMP('2024-09-04 15:34:12','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=202845
+;
+
+-- Sep 4, 2024, 3:34:12 PM BRT
+UPDATE AD_Field SET SeqNo=160,Updated=TO_TIMESTAMP('2024-09-04 15:34:12','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=202847
+;
+
+-- Sep 4, 2024, 3:34:12 PM BRT
+UPDATE AD_Field SET SeqNo=170,Updated=TO_TIMESTAMP('2024-09-04 15:34:12','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=207405
+;
+
+-- Sep 4, 2024, 3:34:12 PM BRT
+UPDATE AD_Field SET SeqNo=180,Updated=TO_TIMESTAMP('2024-09-04 15:34:12','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=207407
+;
+
+-- Sep 4, 2024, 3:34:12 PM BRT
+UPDATE AD_Field SET SeqNo=190,Updated=TO_TIMESTAMP('2024-09-04 15:34:12','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=207406
+;
+
+-- Sep 4, 2024, 3:34:12 PM BRT
+UPDATE AD_Field SET SeqNo=200,Updated=TO_TIMESTAMP('2024-09-04 15:34:12','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=207408
+;
+
+-- Sep 4, 2024, 3:34:12 PM BRT
+UPDATE AD_Field SET SeqNo=210,Updated=TO_TIMESTAMP('2024-09-04 15:34:12','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=207720
+;
+
+-- Sep 4, 2024, 3:34:40 PM BRT
+INSERT INTO AD_Column (AD_Column_ID,Version,Name,Description,AD_Table_ID,ColumnName,FieldLength,IsKey,IsParent,IsMandatory,IsTranslated,IsIdentifier,SeqNo,IsEncrypted,AD_Reference_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Element_ID,IsUpdateable,IsSelectionColumn,EntityType,IsSyncDatabase,IsAlwaysUpdateable,IsAutocomplete,IsAllowLogging,AD_Column_UU,IsAllowCopy,SeqNoSelection,IsToolbarButton,IsSecure,FKConstraintType,IsHtml,IsPartitionKey) VALUES (216789,0,'JSON Data','The json field stores json data.',578,'JsonData',0,'N','N','N','N','N',0,'N',200267,0,0,'Y',TO_TIMESTAMP('2024-09-04 15:34:40','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-09-04 15:34:40','YYYY-MM-DD HH24:MI:SS'),100,203924,'Y','N','D','N','N','N','Y','7539f67f-922d-4e27-881f-c04f0dc8c160','Y',0,'N','N','N','N','N')
+;
+
+-- Sep 4, 2024, 3:34:43 PM BRT
+ALTER TABLE AD_PInstance_Log ADD COLUMN JsonData JSON DEFAULT NULL
+;
+
+-- Sep 4, 2024, 3:35:54 PM BRT
+INSERT INTO AD_Field (AD_Field_ID,Name,Description,AD_Tab_ID,AD_Column_ID,IsDisplayed,DisplayLength,SeqNo,IsSameLine,IsHeading,IsFieldOnly,IsEncrypted,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,IsReadOnly,IsCentrallyMaintained,EntityType,AD_Field_UU,IsDisplayedGrid,SeqNoGrid,ColumnSpan,NumLines) VALUES (208512,'JSON Data','The json field stores json data.',665,216789,'Y',0,100,'N','N','N','N',0,0,'Y',TO_TIMESTAMP('2024-09-04 15:35:54','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-09-04 15:35:54','YYYY-MM-DD HH24:MI:SS'),100,'N','Y','D','27e958ae-9e31-4e8b-824e-e571df7da15c','Y',100,2,5)
+;
+
+-- Sep 4, 2024, 3:36:15 PM BRT
+UPDATE AD_Field SET IsDisplayed='Y', SeqNo=70, XPosition=1, ColumnSpan=5,Updated=TO_TIMESTAMP('2024-09-04 15:36:15','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=208512
+;
+
+-- Sep 4, 2024, 3:36:15 PM BRT
+UPDATE AD_Field SET SeqNo=80,Updated=TO_TIMESTAMP('2024-09-04 15:36:15','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=200309
+;
+
+-- Sep 4, 2024, 3:36:15 PM BRT
+UPDATE AD_Field SET SeqNo=90,Updated=TO_TIMESTAMP('2024-09-04 15:36:15','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=200310
+;
+
+-- Sep 4, 2024, 3:36:15 PM BRT
+UPDATE AD_Field SET SeqNo=100,Updated=TO_TIMESTAMP('2024-09-04 15:36:15','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=207622
+;
+
+-- Sep 4, 2024, 3:36:15 PM BRT
+UPDATE AD_Field SET SeqNo=0,Updated=TO_TIMESTAMP('2024-09-04 15:36:15','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=204554
+;
+
diff --git a/migration/iD11/postgresql/202409141654_IDEMPIERE-5567.sql b/migration/iD11/postgresql/202409141654_IDEMPIERE-5567.sql
new file mode 100644
index 0000000000..0d2ae8e785
--- /dev/null
+++ b/migration/iD11/postgresql/202409141654_IDEMPIERE-5567.sql
@@ -0,0 +1,19 @@
+-- IDEMPIERE-5567 Permalink for UUID multi-key tables
+SELECT register_migration_script('202409141654_IDEMPIERE-5567.sql') FROM dual;
+
+-- Sep 14, 2024, 4:54:43 PM CEST
+INSERT INTO AD_ZoomCondition (AD_Client_ID,AD_Org_ID,AD_Table_ID,AD_Window_ID,AD_ZoomCondition_ID,Created,CreatedBy,IsActive,Updated,UpdatedBy,SeqNo,Name,AD_ZoomCondition_UU,ZoomLogic,EntityType) VALUES (0,0,201,111,200009,TO_TIMESTAMP('2024-09-14 16:54:42','YYYY-MM-DD HH24:MI:SS'),100,'Y',TO_TIMESTAMP('2024-09-14 16:54:42','YYYY-MM-DD HH24:MI:SS'),100,10,'Zoom to role on tenants','f8ddf8c5-4bf6-4343-a2f3-c88c1f007344','@#AD_Client_ID@>0','D')
+;
+
+-- Sep 14, 2024, 4:56:45 PM CEST
+INSERT INTO AD_ZoomCondition (AD_Client_ID,AD_Org_ID,AD_Table_ID,AD_Window_ID,AD_ZoomCondition_ID,Created,CreatedBy,IsActive,Updated,UpdatedBy,SeqNo,Name,AD_ZoomCondition_UU,ZoomLogic,EntityType) VALUES (0,0,378,111,200010,TO_TIMESTAMP('2024-09-14 16:56:45','YYYY-MM-DD HH24:MI:SS'),100,'Y',TO_TIMESTAMP('2024-09-14 16:56:45','YYYY-MM-DD HH24:MI:SS'),100,10,'Zoom to role on tenants','32bab358-373f-476a-a52c-5c700044478d','@#AD_Client_ID@>0','D')
+;
+
+-- Sep 14, 2024, 4:57:50 PM CEST
+INSERT INTO AD_ZoomCondition (AD_Client_ID,AD_Org_ID,AD_Table_ID,AD_Window_ID,AD_ZoomCondition_ID,Created,CreatedBy,IsActive,Updated,UpdatedBy,SeqNo,Name,AD_ZoomCondition_UU,ZoomLogic,EntityType) VALUES (0,0,197,111,200011,TO_TIMESTAMP('2024-09-14 16:57:50','YYYY-MM-DD HH24:MI:SS'),100,'Y',TO_TIMESTAMP('2024-09-14 16:57:50','YYYY-MM-DD HH24:MI:SS'),100,10,'Zoom to role on tenants','ebea9694-6348-444e-8b7f-f5f1c75a3c65','@#AD_Client_ID@>0','D')
+;
+
+-- Sep 14, 2024, 4:58:46 PM CEST
+INSERT INTO AD_ZoomCondition (AD_Client_ID,AD_Org_ID,AD_Table_ID,AD_Window_ID,AD_ZoomCondition_ID,Created,CreatedBy,IsActive,Updated,UpdatedBy,SeqNo,Name,AD_ZoomCondition_UU,ZoomLogic,EntityType) VALUES (0,0,199,111,200012,TO_TIMESTAMP('2024-09-14 16:58:46','YYYY-MM-DD HH24:MI:SS'),100,'Y',TO_TIMESTAMP('2024-09-14 16:58:46','YYYY-MM-DD HH24:MI:SS'),100,10,'Zoom to role on tenants','91ce33c0-fb91-4c9e-894e-8e8f49d7677f','@#AD_Client_ID@>0','D')
+;
+
diff --git a/migration/iD11/postgresql/202409141807_IDEMPIERE-6007.sql b/migration/iD11/postgresql/202409141807_IDEMPIERE-6007.sql
new file mode 100644
index 0000000000..101ede3031
--- /dev/null
+++ b/migration/iD11/postgresql/202409141807_IDEMPIERE-6007.sql
@@ -0,0 +1,7 @@
+-- IDEMPIERE-6007
+SELECT register_migration_script('202409141807_IDEMPIERE-6007.sql') FROM dual;
+
+-- Sep 14, 2024, 6:07:20 PM CEST
+UPDATE AD_Field SET ReadOnlyLogic='@SQL=SELECT 1 FROM AD_Field WHERE AD_Tab_ID=@AD_Tab_ID:0@',Updated=TO_TIMESTAMP('2024-09-14 18:07:20','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=131
+;
+
diff --git a/migration/iD11/postgresql/202409231820_IDEMPIERE-6248.sql b/migration/iD11/postgresql/202409231820_IDEMPIERE-6248.sql
new file mode 100644
index 0000000000..0f4f5dc2ba
--- /dev/null
+++ b/migration/iD11/postgresql/202409231820_IDEMPIERE-6248.sql
@@ -0,0 +1,7 @@
+-- IDEMPIERE-6248
+SELECT register_migration_script('202409231820_IDEMPIERE-6248.sql') FROM dual;
+
+-- Sep 23, 2024, 6:20:30 PM CEST
+INSERT INTO AD_SysConfig (AD_SysConfig_ID,AD_Client_ID,AD_Org_ID,Created,Updated,CreatedBy,UpdatedBy,IsActive,Name,Value,Description,EntityType,ConfigurationLevel,AD_SysConfig_UU) VALUES (200250,0,0,TO_TIMESTAMP('2024-09-23 18:20:29','YYYY-MM-DD HH24:MI:SS'),TO_TIMESTAMP('2024-09-23 18:20:29','YYYY-MM-DD HH24:MI:SS'),10,10,'Y','FULL_EXCEPTION_TRACE_IN_LOG','N','If set to Y, the stack trace is not cut in the log (see https://idempiere.atlassian.net/browse/IDEMPIERE-6248)','D','S','8308ac18-d0a6-479f-ab59-7edd0bcb0b54')
+;
+
diff --git a/migration/iD11/postgresql/202409231821_IDEMPIERE-6248_DelSysConfig.sql b/migration/iD11/postgresql/202409231821_IDEMPIERE-6248_DelSysConfig.sql
new file mode 100644
index 0000000000..9146a8de45
--- /dev/null
+++ b/migration/iD11/postgresql/202409231821_IDEMPIERE-6248_DelSysConfig.sql
@@ -0,0 +1,5 @@
+-- IDEMPIERE-6248
+SELECT register_migration_script('202409231821_IDEMPIERE-6248_DelSysConfig.sql') FROM dual;
+
+DELETE FROM AD_SysConfig WHERE AD_SysConfig_ID = 200250
+;
diff --git a/migration/iD11/postgresql/202409241248_IDEMPIERE-5760.sql b/migration/iD11/postgresql/202409241248_IDEMPIERE-5760.sql
new file mode 100644
index 0000000000..23e65099c2
--- /dev/null
+++ b/migration/iD11/postgresql/202409241248_IDEMPIERE-5760.sql
@@ -0,0 +1,11 @@
+-- IDEMPIERE-5760 Manage mail.smtp.timeout using SysConfig
+SELECT register_migration_script('202409241248_IDEMPIERE-5760.sql') FROM dual;
+
+-- Sep 24, 2024, 12:48:56 PM CEST
+INSERT INTO AD_SysConfig (AD_SysConfig_ID,AD_Client_ID,AD_Org_ID,Created,Updated,CreatedBy,UpdatedBy,IsActive,Name,Value,Description,EntityType,ConfigurationLevel,AD_SysConfig_UU) VALUES (200253,0,0,TO_TIMESTAMP('2024-09-24 12:48:55','YYYY-MM-DD HH24:MI:SS'),TO_TIMESTAMP('2024-09-24 12:48:55','YYYY-MM-DD HH24:MI:SS'),100,100,'Y','MAIL_SMTP_CONNECTIONTIMEOUT','-1','Timeout in milliseconds to wait for SMTP connection, -1 leaves the java default','D','C','36be68b7-7b4b-4725-9a51-aeb11bb7b699')
+;
+
+-- Sep 24, 2024, 12:49:10 PM CEST
+INSERT INTO AD_SysConfig (AD_SysConfig_ID,AD_Client_ID,AD_Org_ID,Created,Updated,CreatedBy,UpdatedBy,IsActive,Name,Value,Description,EntityType,ConfigurationLevel,AD_SysConfig_UU) VALUES (200254,0,0,TO_TIMESTAMP('2024-09-24 12:49:09','YYYY-MM-DD HH24:MI:SS'),TO_TIMESTAMP('2024-09-24 12:49:09','YYYY-MM-DD HH24:MI:SS'),100,100,'Y','MAIL_SMTP_WRITETIMEOUT','-1','Timeout in milliseconds to wait for writing on SMTP connection, -1 leaves the java default','D','C','e11d83c4-8500-400a-b9d2-358c30c910fb')
+;
+
diff --git a/migration/iD11/postgresql/202410071412_IDEMPIERE-6196.sql b/migration/iD11/postgresql/202410071412_IDEMPIERE-6196.sql
new file mode 100644
index 0000000000..63bde69934
--- /dev/null
+++ b/migration/iD11/postgresql/202410071412_IDEMPIERE-6196.sql
@@ -0,0 +1,15 @@
+-- IDEMPIERE-6196_SysConfig
+SELECT register_migration_script('202410071412_IDEMPIERE-6196.sql') FROM dual;
+
+-- Oct 7, 2024, 2:12:19 PM CEST
+UPDATE AD_Column SET SeqNoSelection=10,Updated=TO_TIMESTAMP('2024-10-07 14:12:19','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=10 WHERE AD_Column_ID=50195
+;
+
+-- Oct 7, 2024, 2:12:23 PM CEST
+UPDATE AD_Column SET SeqNoSelection=20,Updated=TO_TIMESTAMP('2024-10-07 14:12:23','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=10 WHERE AD_Column_ID=50197
+;
+
+-- Oct 7, 2024, 2:12:27 PM CEST
+UPDATE AD_Column SET SeqNoSelection=30,Updated=TO_TIMESTAMP('2024-10-07 14:12:27','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=10 WHERE AD_Column_ID=50196
+;
+
diff --git a/migration/iD11/postgresql/202411260821_IDEMPIERE-6317.sql b/migration/iD11/postgresql/202411260821_IDEMPIERE-6317.sql
new file mode 100644
index 0000000000..46d56ebbba
--- /dev/null
+++ b/migration/iD11/postgresql/202411260821_IDEMPIERE-6317.sql
@@ -0,0 +1,15 @@
+-- IDEMPIERE-6317
+SELECT register_migration_script('202411260821_IDEMPIERE-6317.sql') FROM dual;
+
+-- Nov 26, 2024, 8:34:46 AM BRT
+UPDATE AD_Message SET Value='Can''t Save Tenant Level',Updated=TO_TIMESTAMP('2024-11-26 08:34:46','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Message_ID=53010
+;
+
+-- Nov 26, 2024, 8:36:49 AM BRT
+INSERT INTO AD_Message (MsgType,MsgText,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Message_ID,Value,EntityType,AD_Message_UU) VALUES ('E','This is a system or tenant parameter, you can''t save it as organization parameter',0,0,'Y',TO_TIMESTAMP('2024-11-26 08:36:48','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-11-26 08:36:48','YYYY-MM-DD HH24:MI:SS'),100,200914,'ThisIsSystemOrTenantParameter','D','8980c935-1c23-4d83-8b26-70eb3a749797')
+;
+
+-- Nov 26, 2024, 8:38:56 AM BRT
+INSERT INTO AD_Message (MsgType,MsgText,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Message_ID,Value,EntityType,AD_Message_UU) VALUES ('E','This is a system parameter, you can''t save it as tenant parameter',0,0,'Y',TO_TIMESTAMP('2024-11-26 08:38:55','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-11-26 08:38:55','YYYY-MM-DD HH24:MI:SS'),100,200915,'ThisIsSystemParameter','D','6e6b2060-ec2d-4d87-b499-7250e93f7cec')
+;
+
diff --git a/migration/iD11/postgresql/202411301155_IDEMPIERE-6329.sql b/migration/iD11/postgresql/202411301155_IDEMPIERE-6329.sql
new file mode 100644
index 0000000000..beb85d4cd2
--- /dev/null
+++ b/migration/iD11/postgresql/202411301155_IDEMPIERE-6329.sql
@@ -0,0 +1,576 @@
+-- IDEMPIERE-6329 Bug in BOM* SQL functions not getting the correct BOM children
+SELECT register_migration_script('202411301155_IDEMPIERE-6329.sql') FROM dual;
+
+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 IsActive='Y' AND 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_Product_ID=p.M_Product_ID
+ AND b.M_Product_ID=Product_ID
+ AND b.M_ProductBOM_ID != Product_ID
+ AND p.IsVerified='Y'
+ AND b.IsActive='Y'
+ LOOP
+ v_ProductPrice := bomPriceLimit (bom.M_ProductBOM_ID, PriceList_Version_ID);
+ v_Price := v_Price + (bom.BOMQty * v_ProductPrice);
+ END LOOP;
+ END IF;
+ --
+ RETURN v_Price;
+
+END;
+$BODY$
+LANGUAGE 'plpgsql' STABLE
+;
+
+CREATE OR REPLACE FUNCTION bompricelist (in product_id numeric, in pricelist_version_id numeric) RETURNS numeric AS
+$BODY$
+DECLARE
+ v_Price NUMERIC;
+ v_ProductPrice NUMERIC;
+ bom RECORD;
+
+BEGIN
+ -- Try to get price from pricelist directly
+ SELECT COALESCE (SUM(PriceList), 0)
+ INTO v_Price
+ FROM M_ProductPrice
+ WHERE IsActive='Y' AND 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_Product_ID=p.M_Product_ID
+ AND b.M_Product_ID=Product_ID
+ AND b.M_ProductBOM_ID != Product_ID
+ AND p.IsVerified='Y'
+ AND b.IsActive='Y'
+ LOOP
+ v_ProductPrice := bomPriceList (bom.M_ProductBOM_ID, PriceList_Version_ID);
+ v_Price := v_Price + (bom.BOMQty * v_ProductPrice);
+ END LOOP;
+ END IF;
+ --
+ RETURN v_Price;
+
+END;
+$BODY$
+LANGUAGE 'plpgsql' STABLE
+;
+
+CREATE OR REPLACE FUNCTION bompricestd (in product_id numeric, in pricelist_version_id numeric) RETURNS numeric AS
+$BODY$
+DECLARE
+ v_Price NUMERIC;
+ v_ProductPrice NUMERIC;
+ bom RECORD;
+
+BEGIN
+ -- Try to get price from PriceList directly
+ SELECT COALESCE(SUM(PriceStd), 0)
+ INTO v_Price
+ FROM M_ProductPrice
+ WHERE IsActive='Y' AND 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_Product_ID=p.M_Product_ID
+ AND b.M_Product_ID=Product_ID
+ AND b.M_ProductBOM_ID != Product_ID
+ AND p.IsVerified='Y'
+ AND b.IsActive='Y'
+ LOOP
+ v_ProductPrice := bomPriceStd (bom.M_ProductBOM_ID, PriceList_Version_ID);
+ v_Price := v_Price + (bom.BOMQty * v_ProductPrice);
+ END LOOP;
+ END IF;
+ --
+ RETURN v_Price;
+
+END;
+$BODY$
+LANGUAGE 'plpgsql' STABLE
+;
+
+CREATE OR REPLACE FUNCTION BOMQtyOnHandForReservation (in product_id numeric, in warehouse_id numeric, in locator_id numeric) RETURNS numeric AS
+$BODY$
+DECLARE
+ myWarehouse_ID numeric;
+ v_Quantity numeric := 99999; -- unlimited
+ v_IsBOM CHAR(1);
+ v_IsStocked CHAR(1);
+ v_ProductType CHAR(1);
+ v_ProductQty numeric;
+ v_StdPrecision int;
+ bom record;
+
+BEGIN
+ -- Check Parameters
+ myWarehouse_ID := Warehouse_ID;
+ IF (myWarehouse_ID IS NULL) THEN
+ IF (Locator_ID IS NULL) THEN
+ RETURN 0;
+ ELSE
+ SELECT SUM(M_Warehouse_ID) INTO myWarehouse_ID
+ FROM M_LOCATOR
+ WHERE M_Locator_ID=Locator_ID;
+ END IF;
+ END IF;
+ IF (myWarehouse_ID IS NULL) THEN
+ RETURN 0;
+ END IF;
+
+ -- Check, if product exists and if it is stocked
+ BEGIN
+ SELECT IsBOM, ProductType, IsStocked
+ INTO v_IsBOM, v_ProductType, v_IsStocked
+ FROM M_PRODUCT
+ WHERE M_Product_ID=Product_ID;
+ --
+ EXCEPTION -- not found
+ WHEN OTHERS THEN
+ RETURN 0;
+ END;
+ -- Unlimited 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_Storageonhand s
+ JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)
+ LEFT JOIN M_LocatorType lt ON (l.M_LocatorType_ID=lt.M_LocatorType_ID)
+ WHERE s.M_Product_ID=Product_ID AND l.M_Warehouse_ID=myWarehouse_ID
+ AND COALESCE(lt.IsAvailableForReservation,'Y')='Y';
+ --
+ RETURN v_ProductQty;
+ END IF;
+
+ -- Go through 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_Product_ID=p.M_Product_ID
+ AND b.M_Product_ID=product_ID
+ AND b.M_ProductBOM_ID != Product_ID
+ AND p.IsBOM='Y'
+ AND p.IsVerified='Y'
+ AND b.IsActive='Y'
+ LOOP
+ -- Stocked Items "leaf node"
+ IF (bom.ProductType = 'I' AND bom.IsStocked = 'Y') THEN
+ -- Get v_ProductQty
+ SELECT COALESCE(SUM(QtyOnHand), 0)
+ INTO v_ProductQty
+ FROM M_Storageonhand s
+ JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)
+ LEFT JOIN M_LocatorType lt ON (l.M_LocatorType_ID=lt.M_LocatorType_ID)
+ WHERE s.M_Product_ID=bom.M_ProductBOM_ID AND l.M_Warehouse_ID=myWarehouse_ID
+ AND COALESCE(lt.IsAvailableForReservation,'Y')='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 := BOMQtyOnHandForReservation (bom.M_ProductBOM_ID, myWarehouse_ID, Locator_ID);
+ -- How much can we make overall
+ IF (v_ProductQty < v_Quantity) THEN
+ v_Quantity := v_ProductQty;
+ END IF;
+ END IF;
+ END LOOP; -- BOM
+
+ IF (v_Quantity > 0) THEN
+ -- Get Rounding Precision for Product
+ SELECT COALESCE(MAX(u.StdPrecision), 0)
+ INTO v_StdPrecision
+ FROM C_UOM u, M_PRODUCT p
+ WHERE u.C_UOM_ID=p.C_UOM_ID AND p.M_Product_ID=Product_ID;
+ --
+ RETURN ROUND (v_Quantity, v_StdPrecision);
+ END IF;
+ RETURN 0;
+END;
+$BODY$
+LANGUAGE 'plpgsql' STABLE
+;
+
+CREATE OR REPLACE FUNCTION BOMQtyOnHand (in product_id numeric, in warehouse_id numeric, in locator_id numeric) RETURNS numeric AS
+$BODY$
+DECLARE
+ myWarehouse_ID numeric;
+ v_Quantity numeric := 99999; -- unlimited
+ v_IsBOM CHAR(1);
+ v_IsStocked CHAR(1);
+ v_ProductType CHAR(1);
+ v_ProductQty numeric;
+ v_StdPrecision int;
+ bom record;
+
+BEGIN
+ -- Check Parameters
+ myWarehouse_ID := Warehouse_ID;
+ IF (myWarehouse_ID IS NULL) THEN
+ IF (Locator_ID IS NULL) THEN
+ RETURN 0;
+ ELSE
+ SELECT SUM(M_Warehouse_ID) INTO myWarehouse_ID
+ FROM M_LOCATOR
+ WHERE M_Locator_ID=Locator_ID;
+ END IF;
+ END IF;
+ IF (myWarehouse_ID IS NULL) THEN
+ RETURN 0;
+ END IF;
+
+ -- Check, if product exists and if it is stocked
+ BEGIN
+ SELECT IsBOM, ProductType, IsStocked
+ INTO v_IsBOM, v_ProductType, v_IsStocked
+ FROM M_PRODUCT
+ WHERE M_Product_ID=Product_ID;
+ --
+ EXCEPTION -- not found
+ WHEN OTHERS THEN
+ RETURN 0;
+ END;
+ -- Unlimited 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_Storageonhand s
+ JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)
+ WHERE s.M_Product_ID=Product_ID AND l.M_Warehouse_ID=myWarehouse_ID;
+ --
+ RETURN v_ProductQty;
+ END IF;
+
+ -- Go through 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_Product_ID=p.M_Product_ID
+ AND b.M_Product_ID=product_ID
+ AND b.M_ProductBOM_ID != Product_ID
+ AND p.IsBOM='Y'
+ AND p.IsVerified='Y'
+ AND b.IsActive='Y'
+ LOOP
+ -- Stocked Items "leaf node"
+ IF (bom.ProductType = 'I' AND bom.IsStocked = 'Y') THEN
+ -- Get v_ProductQty
+ SELECT COALESCE(SUM(QtyOnHand), 0)
+ INTO v_ProductQty
+ FROM M_Storageonhand s
+ JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)
+ WHERE s.M_Product_ID=bom.M_ProductBOM_ID AND l.M_Warehouse_ID=myWarehouse_ID;
+ -- Get Rounding Precision
+ SELECT COALESCE(MAX(u.StdPrecision), 0)
+ INTO v_StdPrecision
+ FROM C_UOM u, M_PRODUCT p
+ WHERE u.C_UOM_ID=p.C_UOM_ID AND p.M_Product_ID=bom.M_ProductBOM_ID;
+ -- How much can we make with this product
+ v_ProductQty := ROUND (v_ProductQty/bom.BOMQty, v_StdPrecision);
+ -- How much can we make overall
+ IF (v_ProductQty < v_Quantity) THEN
+ v_Quantity := v_ProductQty;
+ END IF;
+ -- Another BOM
+ ELSIF (bom.IsBOM = 'Y') THEN
+ v_ProductQty := Bomqtyonhand (bom.M_ProductBOM_ID, myWarehouse_ID, Locator_ID);
+ -- How much can we make overall
+ IF (v_ProductQty < v_Quantity) THEN
+ v_Quantity := v_ProductQty;
+ END IF;
+ END IF;
+ END LOOP; -- BOM
+
+ IF (v_Quantity > 0) THEN
+ -- Get Rounding Precision for Product
+ SELECT COALESCE(MAX(u.StdPrecision), 0)
+ INTO v_StdPrecision
+ FROM C_UOM u, M_PRODUCT p
+ WHERE u.C_UOM_ID=p.C_UOM_ID AND p.M_Product_ID=Product_ID;
+ --
+ RETURN ROUND (v_Quantity, v_StdPrecision);
+ END IF;
+ RETURN 0;
+END;
+$BODY$
+LANGUAGE 'plpgsql' STABLE
+;
+
+CREATE OR REPLACE FUNCTION bomqtyordered (in p_product_id numeric, in p_warehouse_id numeric, in p_locator_id numeric) RETURNS numeric AS
+$BODY$
+DECLARE
+ v_Warehouse_ID numeric;
+ v_Quantity numeric := 99999; -- unlimited
+ v_IsBOM CHAR(1);
+ v_IsStocked CHAR(1);
+ v_ProductType CHAR(1);
+ v_ProductQty numeric;
+ v_StdPrecision int;
+ bom record;
+BEGIN
+ -- Check Parameters
+ v_Warehouse_ID := p_Warehouse_ID;
+ IF (v_Warehouse_ID IS NULL) THEN
+ IF (p_Locator_ID IS NULL) THEN
+ RETURN 0;
+ ELSE
+ SELECT MAX(M_Warehouse_ID) INTO v_Warehouse_ID
+ FROM M_LOCATOR
+ WHERE M_Locator_ID=p_Locator_ID;
+ END IF;
+ END IF;
+ IF (v_Warehouse_ID IS NULL) THEN
+ RETURN 0;
+ END IF;
+
+ -- Check, if product exists and if it is stocked
+ BEGIN
+ SELECT IsBOM, ProductType, IsStocked
+ INTO v_IsBOM, v_ProductType, v_IsStocked
+ FROM M_PRODUCT
+ WHERE M_Product_ID=p_Product_ID;
+ --
+ EXCEPTION -- not found
+ WHEN OTHERS THEN
+ RETURN 0;
+ END;
+
+ -- No reservation for non-stocked
+ IF (v_IsBOM='N' AND (v_ProductType<>'I' OR v_IsStocked='N')) THEN
+ RETURN 0;
+ -- Stocked item
+ ELSIF (v_IsStocked='Y') THEN
+ -- Get ProductQty
+ SELECT COALESCE(SUM(Qty), 0)
+ INTO v_ProductQty
+ FROM M_StorageReservation
+ WHERE M_Product_ID=p_Product_ID
+ AND M_Warehouse_ID=v_Warehouse_ID
+ AND IsSOTrx='N'
+ AND IsActive='Y';
+ --
+ RETURN v_ProductQty;
+ END IF;
+
+ -- Go though BOM
+ FOR bom IN
+ -- Get BOM Product info
+ SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM, p.IsStocked, p.ProductType
+ FROM M_PRODUCT_BOM b, M_PRODUCT p
+ WHERE b.M_Product_ID=p.M_Product_ID
+ AND b.M_Product_ID=p_Product_ID
+ AND b.M_ProductBOM_ID != p_Product_ID
+ AND p.IsBOM='Y'
+ AND p.IsVerified='Y'
+ AND b.IsActive='Y'
+ LOOP
+ -- Stocked Items "leaf node"
+ IF (bom.ProductType = 'I' AND bom.IsStocked = 'Y') THEN
+ -- Get ProductQty
+ SELECT COALESCE(SUM(Qty), 0)
+ INTO v_ProductQty
+ FROM M_StorageReservation
+ WHERE M_Product_ID=p_Product_ID
+ AND M_Warehouse_ID=v_Warehouse_ID
+ AND IsSOTrx='N'
+ AND IsActive='Y';
+ -- Get Rounding Precision
+ SELECT COALESCE(MAX(u.StdPrecision), 0)
+ INTO v_StdPrecision
+ FROM C_UOM u, M_PRODUCT p
+ WHERE u.C_UOM_ID=p.C_UOM_ID AND p.M_Product_ID=bom.M_ProductBOM_ID;
+ -- How much can we make with this product
+ v_ProductQty := ROUND (v_ProductQty/bom.BOMQty, v_StdPrecision );
+
+ -- How much can we make overall
+ IF (v_ProductQty < v_Quantity) THEN
+ v_Quantity := v_ProductQty;
+ END IF;
+ -- Another BOM
+ ELSIF (bom.IsBOM = 'Y') THEN
+ v_ProductQty := Bomqtyordered (bom.M_ProductBOM_ID, v_Warehouse_ID, p_Locator_ID);
+ -- How much can we make overall
+ IF (v_ProductQty < v_Quantity) THEN
+ v_Quantity := v_ProductQty;
+ END IF;
+ END IF;
+ END LOOP; -- BOM
+
+ -- Unlimited (e.g. only services)
+ IF (v_Quantity = 99999) THEN
+ RETURN 0;
+ END IF;
+
+ IF (v_Quantity > 0) THEN
+ -- Get Rounding Precision for Product
+ SELECT COALESCE(MAX(u.StdPrecision), 0)
+ INTO v_StdPrecision
+ FROM C_UOM u, M_PRODUCT p
+ WHERE u.C_UOM_ID=p.C_UOM_ID AND p.M_Product_ID=p_Product_ID;
+ --
+ RETURN ROUND (v_Quantity, v_StdPrecision );
+ END IF;
+ --
+ RETURN 0;
+END;
+$BODY$
+LANGUAGE 'plpgsql' STABLE
+;
+
+CREATE OR REPLACE FUNCTION bomqtyreserved (in p_product_id numeric, in p_warehouse_id numeric, in p_locator_id numeric) RETURNS numeric AS
+$BODY$
+DECLARE
+ v_Warehouse_ID numeric;
+ v_Quantity numeric := 99999; -- unlimited
+ v_IsBOM CHAR(1);
+ v_IsStocked CHAR(1);
+ v_ProductType CHAR(1);
+ v_ProductQty numeric;
+ v_StdPrecision int;
+ bom record;
+BEGIN
+ -- Check Parameters
+ v_Warehouse_ID := p_Warehouse_ID;
+ IF (v_Warehouse_ID IS NULL) THEN
+ IF (p_Locator_ID IS NULL) THEN
+ RETURN 0;
+ ELSE
+ SELECT MAX(M_Warehouse_ID) INTO v_Warehouse_ID
+ FROM M_LOCATOR
+ WHERE M_Locator_ID=p_Locator_ID;
+ END IF;
+ END IF;
+ IF (v_Warehouse_ID IS NULL) THEN
+ RETURN 0;
+ END IF;
+
+ -- Check, if product exists and if it is stocked
+ BEGIN
+ SELECT IsBOM, ProductType, IsStocked
+ INTO v_IsBOM, v_ProductType, v_IsStocked
+ FROM M_PRODUCT
+ WHERE M_Product_ID=p_Product_ID;
+ --
+ EXCEPTION -- not found
+ WHEN OTHERS THEN
+ RETURN 0;
+ END;
+
+ -- No reservation for non-stocked
+ IF (v_IsBOM='N' AND (v_ProductType<>'I' OR v_IsStocked='N')) THEN
+ RETURN 0;
+ -- Stocked item
+ ELSIF (v_IsStocked='Y') THEN
+ -- Get ProductQty
+ SELECT COALESCE(SUM(Qty), 0)
+ INTO v_ProductQty
+ FROM M_StorageReservation
+ WHERE M_Product_ID=p_Product_ID
+ AND M_Warehouse_ID=v_Warehouse_ID
+ AND IsSOTrx='Y'
+ AND IsActive='Y';
+ --
+ RETURN v_ProductQty;
+ END IF;
+
+ -- Go though BOM
+ FOR bom IN
+ -- Get BOM Product info
+ SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM, p.IsStocked, p.ProductType
+ FROM M_PRODUCT_BOM b, M_PRODUCT p
+ WHERE b.M_Product_ID=p.M_Product_ID
+ AND b.M_Product_ID=p_Product_ID
+ AND b.M_ProductBOM_ID != p_Product_ID
+ AND p.IsBOM='Y'
+ AND p.IsVerified='Y'
+ AND b.IsActive='Y'
+ LOOP
+ -- Stocked Items "leaf node"
+ IF (bom.ProductType = 'I' AND bom.IsStocked = 'Y') THEN
+ -- Get ProductQty
+ SELECT COALESCE(SUM(Qty), 0)
+ INTO v_ProductQty
+ FROM M_StorageReservation
+ WHERE M_Product_ID=bom.M_ProductBOM_ID
+ AND M_Warehouse_ID =v_Warehouse_ID
+ AND IsSOTrx='Y'
+ AND IsActive='Y';
+ -- Get Rounding Precision
+ SELECT COALESCE(MAX(u.StdPrecision), 0)
+ INTO v_StdPrecision
+ FROM C_UOM u, M_PRODUCT p
+ WHERE u.C_UOM_ID=p.C_UOM_ID AND p.M_Product_ID=bom.M_ProductBOM_ID;
+ -- How much can we make with this product
+ v_ProductQty := ROUND (v_ProductQty/bom.BOMQty, v_StdPrecision);
+ -- How much can we make overall
+ IF (v_ProductQty < v_Quantity) THEN
+ v_Quantity := v_ProductQty;
+ END IF;
+ -- Another BOM
+ ELSIF (bom.IsBOM = 'Y') THEN
+ v_ProductQty := Bomqtyreserved (bom.M_ProductBOM_ID, v_Warehouse_ID, p_Locator_ID);
+ -- How much can we make overall
+ IF (v_ProductQty < v_Quantity) THEN
+ v_Quantity := v_ProductQty;
+ END IF;
+ END IF;
+ END LOOP; -- BOM
+
+ -- Unlimited (e.g. only services)
+ IF (v_Quantity = 99999) THEN
+ RETURN 0;
+ END IF;
+
+ IF (v_Quantity > 0) THEN
+ -- Get Rounding Precision for Product
+ SELECT COALESCE(MAX(u.StdPrecision), 0)
+ INTO v_StdPrecision
+ FROM C_UOM u, M_PRODUCT p
+ WHERE u.C_UOM_ID=p.C_UOM_ID AND p.M_Product_ID=p_Product_ID;
+ --
+ RETURN ROUND (v_Quantity, v_StdPrecision);
+ END IF;
+ RETURN 0;
+END;
+$BODY$
+LANGUAGE 'plpgsql' STABLE
+;
+
diff --git a/migration/iD11/postgresql/202412021125_IDEMPIERE-6327.sql b/migration/iD11/postgresql/202412021125_IDEMPIERE-6327.sql
new file mode 100644
index 0000000000..e53b64ae66
--- /dev/null
+++ b/migration/iD11/postgresql/202412021125_IDEMPIERE-6327.sql
@@ -0,0 +1,11 @@
+-- IDEMPIERE-6327 Interest Area Window: Subscription tab should not enable "Insert Record"
+SELECT register_migration_script('202412021125_IDEMPIERE-6327.sql') FROM dual;
+
+-- Dec 2, 2024, 11:25:16 AM MYT
+UPDATE AD_Tab SET IsInsertRecord='N',Updated=TO_TIMESTAMP('2024-12-02 11:25:16','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Tab_ID=536
+;
+
+-- Dec 2, 2024, 11:25:32 AM MYT
+UPDATE AD_Field SET IsReadOnly='Y',Updated=TO_TIMESTAMP('2024-12-02 11:25:32','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=7625
+;
+
diff --git a/migration/iD11/postgresql/202412041755_IDEMPIERE-6334.sql b/migration/iD11/postgresql/202412041755_IDEMPIERE-6334.sql
new file mode 100644
index 0000000000..73d98efa10
--- /dev/null
+++ b/migration/iD11/postgresql/202412041755_IDEMPIERE-6334.sql
@@ -0,0 +1,7 @@
+--
+SELECT register_migration_script('202412041755_IDEMPIERE-6334.sql') FROM dual;
+
+-- Dec 4, 2024, 5:55:11 PM CET
+UPDATE AD_Tab SET IsAdvancedTab='Y',Updated=TO_TIMESTAMP('2024-12-04 17:55:11','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Tab_ID=200329
+;
+
diff --git a/migration/iD11/postgresql/202412091327_IDEMPIERE-6329.sql b/migration/iD11/postgresql/202412091327_IDEMPIERE-6329.sql
new file mode 100644
index 0000000000..d989554c41
--- /dev/null
+++ b/migration/iD11/postgresql/202412091327_IDEMPIERE-6329.sql
@@ -0,0 +1,548 @@
+-- IDEMPIERE-6329 Bug in BOM* SQL functions not getting the correct BOM children
+SELECT register_migration_script('202412091327_IDEMPIERE-6329.sql') FROM dual;
+
+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 IsActive='Y' AND M_PriceList_Version_ID=PriceList_Version_ID AND M_Product_ID=Product_ID;
+
+ -- No Price - Check if BOM
+ IF (v_Price = 0) THEN
+ FOR bom IN
+ SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM
+ FROM M_Product_BOM b, M_Product p
+ WHERE b.M_ProductBOM_ID=p.M_Product_ID
+ AND b.M_Product_ID=Product_ID
+ AND b.M_ProductBOM_ID != Product_ID
+ AND (p.IsBOM='N' OR p.IsVerified='Y')
+ AND b.IsActive='Y'
+ LOOP
+ v_ProductPrice := bomPriceLimit (bom.M_ProductBOM_ID, PriceList_Version_ID);
+ v_Price := v_Price + (bom.BOMQty * v_ProductPrice);
+ END LOOP;
+ END IF;
+ --
+ RETURN v_Price;
+
+END;
+$BODY$
+LANGUAGE 'plpgsql' STABLE
+;
+
+CREATE OR REPLACE FUNCTION bompricelist (in product_id numeric, in pricelist_version_id numeric) RETURNS numeric AS
+$BODY$
+DECLARE
+ v_Price NUMERIC;
+ v_ProductPrice NUMERIC;
+ bom RECORD;
+
+BEGIN
+ -- Try to get price from pricelist directly
+ SELECT COALESCE (SUM(PriceList), 0)
+ INTO v_Price
+ FROM M_ProductPrice
+ WHERE IsActive='Y' AND M_PriceList_Version_ID=PriceList_Version_ID AND M_Product_ID=Product_ID;
+
+ -- No Price - Check if BOM
+ IF (v_Price = 0) THEN
+ FOR bom IN
+ SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM
+ FROM M_Product_BOM b, M_Product p
+ WHERE b.M_ProductBOM_ID=p.M_Product_ID
+ AND b.M_Product_ID=Product_ID
+ AND b.M_ProductBOM_ID != Product_ID
+ AND (p.IsBOM='N' OR p.IsVerified='Y')
+ AND b.IsActive='Y'
+ LOOP
+ v_ProductPrice := bomPriceList (bom.M_ProductBOM_ID, PriceList_Version_ID);
+ v_Price := v_Price + (bom.BOMQty * v_ProductPrice);
+ END LOOP;
+ END IF;
+ --
+ RETURN v_Price;
+
+END;
+$BODY$
+LANGUAGE 'plpgsql' STABLE
+;
+
+CREATE OR REPLACE FUNCTION bompricestd (in product_id numeric, in pricelist_version_id numeric) RETURNS numeric AS
+$BODY$
+DECLARE
+ v_Price NUMERIC;
+ v_ProductPrice NUMERIC;
+ bom RECORD;
+
+BEGIN
+ -- Try to get price from PriceList directly
+ SELECT COALESCE(SUM(PriceStd), 0)
+ INTO v_Price
+ FROM M_ProductPrice
+ WHERE IsActive='Y' AND M_PriceList_Version_ID=PriceList_Version_ID AND M_Product_ID=Product_ID;
+
+ -- No Price - Check if BOM
+ IF (v_Price = 0) THEN
+ FOR bom IN
+ SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM
+ FROM M_Product_BOM b, M_Product p
+ WHERE b.M_ProductBOM_ID=p.M_Product_ID
+ AND b.M_Product_ID=Product_ID
+ AND b.M_ProductBOM_ID != Product_ID
+ AND (p.IsBOM='N' OR p.IsVerified='Y')
+ AND b.IsActive='Y'
+ LOOP
+ v_ProductPrice := bomPriceStd (bom.M_ProductBOM_ID, PriceList_Version_ID);
+ v_Price := v_Price + (bom.BOMQty * v_ProductPrice);
+ END LOOP;
+ END IF;
+ --
+ RETURN v_Price;
+
+END;
+$BODY$
+LANGUAGE 'plpgsql' STABLE
+;
+
+CREATE OR REPLACE FUNCTION BOMQtyOnHandForReservation (in product_id numeric, in warehouse_id numeric, in locator_id numeric) RETURNS numeric AS
+$BODY$
+DECLARE
+ myWarehouse_ID numeric;
+ v_Quantity numeric := 99999; -- unlimited
+ v_IsBOM CHAR(1);
+ v_IsStocked CHAR(1);
+ v_ProductType CHAR(1);
+ v_ProductQty numeric;
+ v_StdPrecision int;
+ bom record;
+
+BEGIN
+ -- Check Parameters
+ myWarehouse_ID := Warehouse_ID;
+ IF (myWarehouse_ID IS NULL) THEN
+ IF (Locator_ID IS NULL) THEN
+ RETURN 0;
+ ELSE
+ SELECT SUM(M_Warehouse_ID) INTO myWarehouse_ID
+ FROM M_LOCATOR
+ WHERE M_Locator_ID=Locator_ID;
+ END IF;
+ END IF;
+ IF (myWarehouse_ID IS NULL) THEN
+ RETURN 0;
+ END IF;
+
+ -- Check, if product exists and if it is stocked
+ BEGIN
+ SELECT IsBOM, ProductType, IsStocked
+ INTO v_IsBOM, v_ProductType, v_IsStocked
+ FROM M_PRODUCT
+ WHERE M_Product_ID=Product_ID;
+ --
+ EXCEPTION -- not found
+ WHEN OTHERS THEN
+ RETURN 0;
+ END;
+ -- Unlimited 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_Storageonhand s
+ JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)
+ LEFT JOIN M_LocatorType lt ON (l.M_LocatorType_ID=lt.M_LocatorType_ID)
+ WHERE s.M_Product_ID=Product_ID AND l.M_Warehouse_ID=myWarehouse_ID
+ AND COALESCE(lt.IsAvailableForReservation,'Y')='Y';
+ --
+ RETURN v_ProductQty;
+ END IF;
+
+ -- Go through BOM
+ FOR bom IN -- Get BOM Product info
+ SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM, p.IsStocked, p.ProductType, p.IsVerified
+ FROM M_PRODUCT_BOM b, M_PRODUCT p
+ WHERE b.M_ProductBOM_ID=p.M_Product_ID
+ AND b.M_Product_ID=product_ID
+ AND b.M_ProductBOM_ID != Product_ID
+ AND b.IsActive='Y'
+ LOOP
+ -- Stocked Items "leaf node"
+ IF (bom.ProductType = 'I' AND bom.IsStocked = 'Y') THEN
+ -- Get v_ProductQty
+ SELECT COALESCE(SUM(QtyOnHand), 0)
+ INTO v_ProductQty
+ FROM M_Storageonhand s
+ JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)
+ LEFT JOIN M_LocatorType lt ON (l.M_LocatorType_ID=lt.M_LocatorType_ID)
+ WHERE s.M_Product_ID=bom.M_ProductBOM_ID AND l.M_Warehouse_ID=myWarehouse_ID
+ AND COALESCE(lt.IsAvailableForReservation,'Y')='Y';
+ -- How much can we make with this product
+ v_ProductQty := v_ProductQty/bom.BOMQty;
+ -- How much can we make overall
+ IF (v_ProductQty < v_Quantity) THEN
+ v_Quantity := v_ProductQty;
+ END IF;
+ -- Another BOM
+ ELSIF (bom.IsBOM = 'Y' AND bom.IsVerified = 'Y') THEN
+ v_ProductQty := BOMQtyOnHandForReservation (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 TRUNC(v_Quantity, v_StdPrecision); -- RoundDown
+ END IF;
+ RETURN 0;
+END;
+$BODY$
+LANGUAGE 'plpgsql' STABLE
+;
+
+CREATE OR REPLACE FUNCTION BOMQtyOnHand (in product_id numeric, in warehouse_id numeric, in locator_id numeric) RETURNS numeric AS
+$BODY$
+DECLARE
+ myWarehouse_ID numeric;
+ v_Quantity numeric := 99999; -- unlimited
+ v_IsBOM CHAR(1);
+ v_IsStocked CHAR(1);
+ v_ProductType CHAR(1);
+ v_ProductQty numeric;
+ v_StdPrecision int;
+ bom record;
+
+BEGIN
+ -- Check Parameters
+ myWarehouse_ID := Warehouse_ID;
+ IF (myWarehouse_ID IS NULL) THEN
+ IF (Locator_ID IS NULL) THEN
+ RETURN 0;
+ ELSE
+ SELECT SUM(M_Warehouse_ID) INTO myWarehouse_ID
+ FROM M_LOCATOR
+ WHERE M_Locator_ID=Locator_ID;
+ END IF;
+ END IF;
+ IF (myWarehouse_ID IS NULL) THEN
+ RETURN 0;
+ END IF;
+
+ -- Check, if product exists and if it is stocked
+ BEGIN
+ SELECT IsBOM, ProductType, IsStocked
+ INTO v_IsBOM, v_ProductType, v_IsStocked
+ FROM M_PRODUCT
+ WHERE M_Product_ID=Product_ID;
+ --
+ EXCEPTION -- not found
+ WHEN OTHERS THEN
+ RETURN 0;
+ END;
+ -- Unlimited 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_Storageonhand s
+ JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)
+ WHERE s.M_Product_ID=Product_ID AND l.M_Warehouse_ID=myWarehouse_ID;
+ --
+ RETURN v_ProductQty;
+ END IF;
+
+ -- Go through BOM
+ FOR bom IN -- Get BOM Product info
+ SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM, p.IsStocked, p.ProductType, p.IsVerified
+ FROM M_PRODUCT_BOM b, M_PRODUCT p
+ WHERE b.M_ProductBOM_ID=p.M_Product_ID
+ AND b.M_Product_ID=product_ID
+ AND b.M_ProductBOM_ID != Product_ID
+ AND b.IsActive='Y'
+ LOOP
+ -- Stocked Items "leaf node"
+ IF (bom.ProductType = 'I' AND bom.IsStocked = 'Y') THEN
+ -- Get v_ProductQty
+ SELECT COALESCE(SUM(QtyOnHand), 0)
+ INTO v_ProductQty
+ FROM M_Storageonhand s
+ JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)
+ WHERE s.M_Product_ID=bom.M_ProductBOM_ID AND l.M_Warehouse_ID=myWarehouse_ID;
+ -- How much can we make with this product
+ v_ProductQty := v_ProductQty/bom.BOMQty;
+ -- How much can we make overall
+ IF (v_ProductQty < v_Quantity) THEN
+ v_Quantity := v_ProductQty;
+ END IF;
+ -- Another BOM
+ ELSIF (bom.IsBOM = 'Y' AND BOM.IsVerified = '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 TRUNC(v_Quantity, v_StdPrecision); -- RoundDown
+ END IF;
+ RETURN 0;
+END;
+$BODY$
+LANGUAGE 'plpgsql' STABLE
+;
+
+CREATE OR REPLACE FUNCTION bomqtyordered (in p_product_id numeric, in p_warehouse_id numeric, in p_locator_id numeric) RETURNS numeric AS
+$BODY$
+DECLARE
+ v_Warehouse_ID numeric;
+ v_Quantity numeric := 99999; -- unlimited
+ v_IsBOM CHAR(1);
+ v_IsStocked CHAR(1);
+ v_ProductType CHAR(1);
+ v_ProductQty numeric;
+ v_StdPrecision int;
+ bom record;
+BEGIN
+ -- Check Parameters
+ v_Warehouse_ID := p_Warehouse_ID;
+ IF (v_Warehouse_ID IS NULL) THEN
+ IF (p_Locator_ID IS NULL) THEN
+ RETURN 0;
+ ELSE
+ SELECT MAX(M_Warehouse_ID) INTO v_Warehouse_ID
+ FROM M_LOCATOR
+ WHERE M_Locator_ID=p_Locator_ID;
+ END IF;
+ END IF;
+ IF (v_Warehouse_ID IS NULL) THEN
+ RETURN 0;
+ END IF;
+
+ -- Check, if product exists and if it is stocked
+ BEGIN
+ SELECT IsBOM, ProductType, IsStocked
+ INTO v_IsBOM, v_ProductType, v_IsStocked
+ FROM M_PRODUCT
+ WHERE M_Product_ID=p_Product_ID;
+ --
+ EXCEPTION -- not found
+ WHEN OTHERS THEN
+ RETURN 0;
+ END;
+
+ -- No reservation for non-stocked
+ IF (v_IsBOM='N' AND (v_ProductType<>'I' OR v_IsStocked='N')) THEN
+ RETURN 0;
+ -- Stocked item
+ ELSIF (v_IsStocked='Y') THEN
+ -- Get ProductQty
+ SELECT COALESCE(SUM(Qty), 0)
+ INTO v_ProductQty
+ FROM M_StorageReservation
+ WHERE M_Product_ID=p_Product_ID
+ AND M_Warehouse_ID=v_Warehouse_ID
+ AND IsSOTrx='N'
+ AND IsActive='Y';
+ --
+ RETURN v_ProductQty;
+ END IF;
+
+ -- Go though BOM
+ FOR bom IN
+ -- Get BOM Product info
+ SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM, p.IsStocked, p.ProductType, p.IsVerified
+ FROM M_PRODUCT_BOM b, M_PRODUCT p
+ WHERE b.M_ProductBOM_ID=p.M_Product_ID
+ AND b.M_Product_ID=p_Product_ID
+ AND b.M_ProductBOM_ID != p_Product_ID
+ AND b.IsActive='Y'
+ LOOP
+ -- Stocked Items "leaf node"
+ IF (bom.ProductType = 'I' AND bom.IsStocked = 'Y') THEN
+ -- Get ProductQty
+ SELECT COALESCE(SUM(Qty), 0)
+ INTO v_ProductQty
+ FROM M_StorageReservation
+ WHERE M_Product_ID=p_Product_ID
+ AND M_Warehouse_ID=v_Warehouse_ID
+ AND IsSOTrx='N'
+ AND IsActive='Y';
+ -- How much can we make with this product
+ v_ProductQty := v_ProductQty/bom.BOMQty;
+
+ -- How much can we make overall
+ IF (v_ProductQty < v_Quantity) THEN
+ v_Quantity := v_ProductQty;
+ END IF;
+ -- Another BOM
+ ELSIF (bom.IsBOM = 'Y' AND BOM.IsVerified = '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 TRUNC(v_Quantity, v_StdPrecision); -- RoundDown
+ END IF;
+ --
+ RETURN 0;
+END;
+$BODY$
+LANGUAGE 'plpgsql' STABLE
+;
+
+CREATE OR REPLACE FUNCTION bomqtyreserved (in p_product_id numeric, in p_warehouse_id numeric, in p_locator_id numeric) RETURNS numeric AS
+$BODY$
+DECLARE
+ v_Warehouse_ID numeric;
+ v_Quantity numeric := 99999; -- unlimited
+ v_IsBOM CHAR(1);
+ v_IsStocked CHAR(1);
+ v_ProductType CHAR(1);
+ v_ProductQty numeric;
+ v_StdPrecision int;
+ bom record;
+BEGIN
+ -- Check Parameters
+ v_Warehouse_ID := p_Warehouse_ID;
+ IF (v_Warehouse_ID IS NULL) THEN
+ IF (p_Locator_ID IS NULL) THEN
+ RETURN 0;
+ ELSE
+ SELECT MAX(M_Warehouse_ID) INTO v_Warehouse_ID
+ FROM M_LOCATOR
+ WHERE M_Locator_ID=p_Locator_ID;
+ END IF;
+ END IF;
+ IF (v_Warehouse_ID IS NULL) THEN
+ RETURN 0;
+ END IF;
+
+ -- Check, if product exists and if it is stocked
+ BEGIN
+ SELECT IsBOM, ProductType, IsStocked
+ INTO v_IsBOM, v_ProductType, v_IsStocked
+ FROM M_PRODUCT
+ WHERE M_Product_ID=p_Product_ID;
+ --
+ EXCEPTION -- not found
+ WHEN OTHERS THEN
+ RETURN 0;
+ END;
+
+ -- No reservation for non-stocked
+ IF (v_IsBOM='N' AND (v_ProductType<>'I' OR v_IsStocked='N')) THEN
+ RETURN 0;
+ -- Stocked item
+ ELSIF (v_IsStocked='Y') THEN
+ -- Get ProductQty
+ SELECT COALESCE(SUM(Qty), 0)
+ INTO v_ProductQty
+ FROM M_StorageReservation
+ WHERE M_Product_ID=p_Product_ID
+ AND M_Warehouse_ID=v_Warehouse_ID
+ AND IsSOTrx='Y'
+ AND IsActive='Y';
+ --
+ RETURN v_ProductQty;
+ END IF;
+
+ -- Go though BOM
+ FOR bom IN
+ -- Get BOM Product info
+ SELECT b.M_ProductBOM_ID, b.BOMQty, p.IsBOM, p.IsStocked, p.ProductType, p.IsVerified
+ FROM M_PRODUCT_BOM b, M_PRODUCT p
+ WHERE b.M_ProductBOM_ID=p.M_Product_ID
+ AND b.M_Product_ID=p_Product_ID
+ AND b.M_ProductBOM_ID != p_Product_ID
+ AND b.IsActive='Y'
+ LOOP
+ -- Stocked Items "leaf node"
+ IF (bom.ProductType = 'I' AND bom.IsStocked = 'Y') THEN
+ -- Get ProductQty
+ SELECT COALESCE(SUM(Qty), 0)
+ INTO v_ProductQty
+ FROM M_StorageReservation
+ WHERE M_Product_ID=bom.M_ProductBOM_ID
+ AND M_Warehouse_ID =v_Warehouse_ID
+ AND IsSOTrx='Y'
+ AND IsActive='Y';
+ -- How much can we make with this product
+ v_ProductQty := v_ProductQty/bom.BOMQty;
+ -- How much can we make overall
+ IF (v_ProductQty < v_Quantity) THEN
+ v_Quantity := v_ProductQty;
+ END IF;
+ -- Another BOM
+ ELSIF (bom.IsBOM = 'Y' AND BOM.IsVerified = '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 TRUNC(v_Quantity, v_StdPrecision); -- RoundDown
+ END IF;
+ RETURN 0;
+END;
+$BODY$
+LANGUAGE 'plpgsql' STABLE
+;
+
diff --git a/migration/iD11/postgresql/202412101102_IDEMPIERE-6335.sql b/migration/iD11/postgresql/202412101102_IDEMPIERE-6335.sql
new file mode 100644
index 0000000000..2fb5a6ed5b
--- /dev/null
+++ b/migration/iD11/postgresql/202412101102_IDEMPIERE-6335.sql
@@ -0,0 +1,7 @@
+-- IDEMPIERE-6335
+SELECT register_migration_script('202412101102_IDEMPIERE-6335.sql') FROM dual;
+
+-- Dec 10, 2024, 11:06:45 AM BRT
+INSERT INTO AD_Process_Para (AD_Process_Para_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,Name,Description,Help,AD_Process_ID,SeqNo,AD_Reference_ID,IsRange,AD_Val_Rule_ID,FieldLength,IsMandatory,ColumnName,IsCentrallyMaintained,EntityType,AD_Element_ID,AD_Process_Para_UU,IsEncrypted,IsAutocomplete,DateRangeOption,IsShowNegateButton) VALUES (200484,0,0,'Y',TO_TIMESTAMP('2024-12-10 11:06:44','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2024-12-10 11:06:44','YYYY-MM-DD HH24:MI:SS'),100,'Document Type','Document type or rules','The Document Type determines document sequence and processing rules',142,20,19,'N',52054,0,'N','C_DocType_ID','Y','D',196,'2b9ac743-0893-474a-aa96-c1eafe8fba86','N','N','D','N')
+;
+
diff --git a/migration/processes_post_migration/oracle/01_add_missing_Translations.sql b/migration/processes_post_migration/oracle/01_add_missing_Translations.sql
index bfca3cf421..2785ff38c1 100644
--- a/migration/processes_post_migration/oracle/01_add_missing_Translations.sql
+++ b/migration/processes_post_migration/oracle/01_add_missing_Translations.sql
@@ -32,12 +32,14 @@ BEGIN
ins :=
'INSERT INTO '
|| t.tablename
- || '_TRL ('
+ || '_Trl ('
|| 'ad_language,ad_client_id,ad_org_id,created,createdby,updated,updatedby,isactive,istranslated,'
|| t.tablename
+ || '_Trl_UU,'
+ || t.tablename
|| suffix;
sel :=
- 'SELECT l.ad_language,t.ad_client_id,t.ad_org_id,t.created,t.createdby,t.updated,t.updatedby,t.isactive,''N'' as istranslated,'
+ 'SELECT l.ad_language,t.ad_client_id,t.ad_org_id,SYSDATE,10,SYSDATE,10,t.isactive,''N'' as istranslated,generate_uuid(),'
|| t.tablename
|| suffix;
@@ -65,7 +67,7 @@ BEGIN
|| t.tablename
|| ' t, ad_language l WHERE l.issystemlanguage=''Y'' AND NOT EXISTS (SELECT 1 FROM '
|| t.tablename
- || '_TRL b WHERE b.'
+ || '_Trl b WHERE b.'
|| t.tablename
|| suffix || '=t.'
|| t.tablename
diff --git a/migration/processes_post_migration/postgresql/01_add_missing_translations.sql b/migration/processes_post_migration/postgresql/01_add_missing_translations.sql
index 1fb245d2f5..98344bfba4 100644
--- a/migration/processes_post_migration/postgresql/01_add_missing_translations.sql
+++ b/migration/processes_post_migration/postgresql/01_add_missing_translations.sql
@@ -28,12 +28,14 @@ BEGIN
ins :=
'INSERT INTO '
|| t.tablename
- || '_TRL ('
+ || '_Trl ('
|| 'ad_language,ad_client_id,ad_org_id,created,createdby,updated,updatedby,isactive,istranslated,'
|| t.tablename
+ || '_Trl_UU,'
+ || t.tablename
|| suffix;
sel :=
- 'SELECT l.ad_language,t.ad_client_id,t.ad_org_id,t.created,t.createdby,t.updated,t.updatedby,t.isactive,''N'' as istranslated,'
+ 'SELECT l.ad_language,t.ad_client_id,t.ad_org_id,statement_timestamp(),10,statement_timestamp(),10,t.isactive,''N'' as istranslated,generate_uuid(),'
|| t.tablename
|| suffix;
diff --git a/org.adempiere.base-feature/feature.xml b/org.adempiere.base-feature/feature.xml
index 839e7dd653..ad1ffdab44 100644
--- a/org.adempiere.base-feature/feature.xml
+++ b/org.adempiere.base-feature/feature.xml
@@ -273,6 +273,10 @@
id="org.apache.commons.commons-compress"
version="0.0.0"/>
+
+
diff --git a/org.adempiere.base-feature/model.generator.launch b/org.adempiere.base-feature/model.generator.launch
index 560b048128..b6b342a05e 100644
--- a/org.adempiere.base-feature/model.generator.launch
+++ b/org.adempiere.base-feature/model.generator.launch
@@ -62,6 +62,7 @@
+
diff --git a/org.adempiere.base-feature/packinfolder.app.launch b/org.adempiere.base-feature/packinfolder.app.launch
index 3a3c509882..e04ab163ee 100644
--- a/org.adempiere.base-feature/packinfolder.app.launch
+++ b/org.adempiere.base-feature/packinfolder.app.launch
@@ -68,6 +68,7 @@
+
diff --git a/org.adempiere.base-feature/sign.database.build.launch b/org.adempiere.base-feature/sign.database.build.launch
index e84c656caf..b3efd9a7a6 100644
--- a/org.adempiere.base-feature/sign.database.build.launch
+++ b/org.adempiere.base-feature/sign.database.build.launch
@@ -69,6 +69,7 @@
+
diff --git a/org.adempiere.base-feature/synchronize-terminology.app.launch b/org.adempiere.base-feature/synchronize-terminology.app.launch
index bf30922042..d907243845 100644
--- a/org.adempiere.base-feature/synchronize-terminology.app.launch
+++ b/org.adempiere.base-feature/synchronize-terminology.app.launch
@@ -69,6 +69,7 @@
+
diff --git a/org.adempiere.base-feature/translation.app.launch b/org.adempiere.base-feature/translation.app.launch
index e7d3cc10d8..e42b430b8b 100644
--- a/org.adempiere.base-feature/translation.app.launch
+++ b/org.adempiere.base-feature/translation.app.launch
@@ -69,6 +69,7 @@
+
diff --git a/org.adempiere.base.callout/src/org/adempiere/base/callout/PackageExpDetail.java b/org.adempiere.base.callout/src/org/adempiere/base/callout/PackageExpDetail.java
new file mode 100644
index 0000000000..b80fef8493
--- /dev/null
+++ b/org.adempiere.base.callout/src/org/adempiere/base/callout/PackageExpDetail.java
@@ -0,0 +1,59 @@
+/***********************************************************************
+ * This file is part of iDempiere ERP Open Source *
+ * http://www.idempiere.org *
+ * *
+ * Copyright (C) Contributors *
+ * *
+ * 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. *
+ * *
+ * Contributors: *
+ * - Nicolas Micoud (TGI) *
+ **********************************************************************/
+package org.adempiere.base.callout;
+
+import java.util.Properties;
+
+import org.adempiere.base.IColumnCallout;
+import org.adempiere.base.annotation.Callout;
+import org.compiere.model.GridField;
+import org.compiere.model.GridTab;
+import org.compiere.model.MTable;
+
+/**
+ *
+ * @author Nicolas Micoud (TGI)
+ *
+ */
+@Callout(tableName = "AD_Package_Exp_Detail", columnName = {"SQLStatement"})
+public class PackageExpDetail implements IColumnCallout {
+ @Override
+ public String start(Properties ctx, int WindowNo, GridTab mTab, GridField mField, Object value, Object oldValue) {
+
+ if (value != null && mTab.getValue("AD_Table_ID") == null) {
+ String sql = value.toString();
+ if (sql.startsWith("SELECT ") && sql.contains(" FROM ")) {
+ int start = sql.indexOf(" FROM ") + 6;
+ int end = sql.indexOf(" ", start + 1);
+
+ String tablename = sql.substring(start, end);
+
+ if (MTable.get(ctx, tablename) != null)
+ mTab.setValue("AD_Table_ID", MTable.get(ctx, tablename).getAD_Table_ID());
+ }
+ }
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/org.adempiere.base.callout/src/org/adempiere/model/CalloutRMA.java b/org.adempiere.base.callout/src/org/adempiere/model/CalloutRMA.java
index d71138b047..338762ac5a 100644
--- a/org.adempiere.base.callout/src/org/adempiere/model/CalloutRMA.java
+++ b/org.adempiere.base.callout/src/org/adempiere/model/CalloutRMA.java
@@ -156,6 +156,11 @@ public class CalloutRMA extends CalloutEngine {
MInvoice invoice = rma.getOriginalInvoice();
if (invoice != null)
{
+ int dropshipLocationId = -1;
+ MOrder order = invoice.getOriginalOrder();
+ if (order != null)
+ dropshipLocationId = order.getDropShip_Location_ID();
+
pp.setM_PriceList_ID(invoice.getM_PriceList_ID());
pp.setPriceDate(invoice.getDateInvoiced());
@@ -168,7 +173,7 @@ public class CalloutRMA extends CalloutEngine {
invoice.getDateInvoiced(), invoice.getDateInvoiced(),
AD_Org_ID, rma.getShipment().getM_Warehouse_ID(),
invoice.getC_BPartner_Location_ID(), // should be bill to
- invoice.getC_BPartner_Location_ID(), rma.isSOTrx(), deliveryViaRule, null);
+ invoice.getC_BPartner_Location_ID(), dropshipLocationId, rma.isSOTrx(), deliveryViaRule, null);
}
else
{
@@ -183,7 +188,7 @@ public class CalloutRMA extends CalloutEngine {
order.getDateOrdered(), order.getDateOrdered(),
AD_Org_ID, order.getM_Warehouse_ID(),
order.getC_BPartner_Location_ID(), // should be bill to
- order.getC_BPartner_Location_ID(), rma.isSOTrx(), order.getDeliveryViaRule(), null);
+ order.getC_BPartner_Location_ID(), order.getDropShip_Location_ID(), rma.isSOTrx(), order.getDeliveryViaRule(), null);
}
else
return "No Invoice/Order found the Shipment/Receipt associated";
diff --git a/org.adempiere.base.callout/src/org/compiere/model/CalloutInOut.java b/org.adempiere.base.callout/src/org/compiere/model/CalloutInOut.java
index 412e02f231..8ca716ff72 100644
--- a/org.adempiere.base.callout/src/org/compiere/model/CalloutInOut.java
+++ b/org.adempiere.base.callout/src/org/compiere/model/CalloutInOut.java
@@ -525,7 +525,7 @@ public class CalloutInOut extends CalloutEngine
mTab.setValue("M_Locator_ID", Integer.valueOf(M_Locator_ID));
}
else
- mTab.setValue("M_AttributeSetInstance_ID", null);
+ mTab.setValue("M_AttributeSetInstance_ID", 0);
//
int M_Warehouse_ID = Env.getContextAsInt(ctx, WindowNo, "M_Warehouse_ID");
boolean IsSOTrx = "Y".equals(Env.getContext(ctx, WindowNo, "IsSOTrx"));
diff --git a/org.adempiere.base.callout/src/org/compiere/model/CalloutInventory.java b/org.adempiere.base.callout/src/org/compiere/model/CalloutInventory.java
index 9e6d034539..0d9b8b2a9e 100644
--- a/org.adempiere.base.callout/src/org/compiere/model/CalloutInventory.java
+++ b/org.adempiere.base.callout/src/org/compiere/model/CalloutInventory.java
@@ -60,7 +60,7 @@ public class CalloutInventory extends CalloutEngine
if ("M_Product_ID".equals(mField.getColumnName())) {
// product changed - remove old ASI
- mTab.setValue("M_AttributeSetInstance_ID", null);
+ mTab.setValue("M_AttributeSetInstance_ID", 0);
}
// Get Book Value
@@ -96,7 +96,7 @@ public class CalloutInventory extends CalloutEngine
if (M_AttributeSetInstance_ID != 0)
mTab.setValue(MInventoryLine.COLUMNNAME_M_AttributeSetInstance_ID, M_AttributeSetInstance_ID);
else
- mTab.setValue(MInventoryLine.COLUMNNAME_M_AttributeSetInstance_ID, null);
+ mTab.setValue(MInventoryLine.COLUMNNAME_M_AttributeSetInstance_ID, 0);
}
// Set QtyBook from first storage location
diff --git a/org.adempiere.base.callout/src/org/compiere/model/CalloutInvoice.java b/org.adempiere.base.callout/src/org/compiere/model/CalloutInvoice.java
index b22f690162..f4b8ae324d 100644
--- a/org.adempiere.base.callout/src/org/compiere/model/CalloutInvoice.java
+++ b/org.adempiere.base.callout/src/org/compiere/model/CalloutInvoice.java
@@ -325,7 +325,7 @@ public class CalloutInvoice extends CalloutEngine
&& Env.getContextAsInt(ctx, WindowNo, Env.TAB_INFO, "M_AttributeSetInstance_ID") != 0)
mTab.setValue("M_AttributeSetInstance_ID", Env.getContextAsInt(ctx, WindowNo, Env.TAB_INFO, "M_AttributeSetInstance_ID"));
else
- mTab.setValue("M_AttributeSetInstance_ID", null);
+ mTab.setValue("M_AttributeSetInstance_ID", 0);
/***** Price Calculation see also qty ****/
boolean IsSOTrx = Env.getContext(ctx, WindowNo, "IsSOTrx").equals("Y");
@@ -488,8 +488,9 @@ public class CalloutInvoice extends CalloutEngine
//
String deliveryViaRule = getLineDeliveryViaRule(ctx, WindowNo, mTab);
+ int dropshipLocationId = getDropShipLocationId(ctx, WindowNo, mTab);
int C_Tax_ID = Core.getTaxLookup().get(ctx, M_Product_ID, C_Charge_ID, billDate, shipDate,
- AD_Org_ID, M_Warehouse_ID, billC_BPartner_Location_ID, shipC_BPartner_Location_ID,
+ AD_Org_ID, M_Warehouse_ID, billC_BPartner_Location_ID, shipC_BPartner_Location_ID, dropshipLocationId,
Env.getContext(ctx, WindowNo, "IsSOTrx").equals("Y"), deliveryViaRule, null);
if (log.isLoggable(Level.INFO)) log.info("Tax ID=" + C_Tax_ID);
//
@@ -501,6 +502,13 @@ public class CalloutInvoice extends CalloutEngine
return amt (ctx, WindowNo, mTab, mField, value);
} // tax
+ /**
+ * Get the delivery via rule from the related order
+ * @param ctx
+ * @param windowNo
+ * @param mTab
+ * @return
+ */
private String getLineDeliveryViaRule(Properties ctx, int windowNo, GridTab mTab) {
if (mTab.getValue("C_OrderLine_ID") != null) {
int C_OrderLine_ID = (Integer) mTab.getValue("C_OrderLine_ID");
@@ -523,7 +531,30 @@ public class CalloutInvoice extends CalloutEngine
}
return null;
}
-
+
+ /**
+ * Get the drop shipment location ID from the related order
+ * @param ctx
+ * @param windowNo
+ * @param mTab
+ * @return
+ */
+ private int getDropShipLocationId(Properties ctx, int windowNo, GridTab mTab) {
+ if (mTab.getValue("C_OrderLine_ID") != null) {
+ int C_OrderLine_ID = (Integer) mTab.getValue("C_OrderLine_ID");
+ if (C_OrderLine_ID > 0) {
+ MOrderLine orderLine = new MOrderLine(ctx, C_OrderLine_ID, null);
+ return orderLine.getParent().getDropShip_Location_ID();
+ }
+ }
+ int C_Order_ID = Env.getContextAsInt(ctx, windowNo, "C_Order_ID", true);
+ if (C_Order_ID > 0) {
+ MOrder order = new MOrder(ctx, C_Order_ID, null);
+ return order.getDropShip_Location_ID();
+ }
+ return -1;
+ }
+
/**
* Invoice - Amount.
* - called from QtyInvoiced, PriceActual
diff --git a/org.adempiere.base.callout/src/org/compiere/model/CalloutInvoiceBatch.java b/org.adempiere.base.callout/src/org/compiere/model/CalloutInvoiceBatch.java
index cee2fe3d5c..52e367199e 100644
--- a/org.adempiere.base.callout/src/org/compiere/model/CalloutInvoiceBatch.java
+++ b/org.adempiere.base.callout/src/org/compiere/model/CalloutInvoiceBatch.java
@@ -319,8 +319,9 @@ public class CalloutInvoiceBatch extends CalloutEngine
//
String deliveryViaRule = getLineDeliveryViaRule(ctx, WindowNo, mTab);
+ int dropshipLocationId = getDropShipLocationId(ctx, WindowNo, mTab);
int C_Tax_ID = Core.getTaxLookup().get(ctx, 0, C_Charge_ID, billDate, shipDate,
- AD_Org_ID, M_Warehouse_ID, C_BPartner_Location_ID, C_BPartner_Location_ID,
+ AD_Org_ID, M_Warehouse_ID, C_BPartner_Location_ID, C_BPartner_Location_ID, dropshipLocationId,
Env.getContext(ctx, WindowNo, "IsSOTrx").equals("Y"), deliveryViaRule, null);
if (log.isLoggable(Level.INFO)) log.info("Tax ID=" + C_Tax_ID);
//
@@ -332,6 +333,13 @@ public class CalloutInvoiceBatch extends CalloutEngine
return amt (ctx, WindowNo, mTab, mField, value);
} // tax
+ /**
+ * Get the drop shipment location ID from the related order
+ * @param ctx
+ * @param windowNo
+ * @param mTab
+ * @return
+ */
private String getLineDeliveryViaRule(Properties ctx, int windowNo, GridTab mTab) {
if (mTab.getValue("C_InvoiceLine_ID") != null) {
int C_InvoiceLine_ID = (Integer) mTab.getValue("C_InvoiceLine_ID");
@@ -361,7 +369,39 @@ public class CalloutInvoiceBatch extends CalloutEngine
}
return null;
}
-
+
+ /**
+ * Get the drop shipment location ID from the related order
+ * @param ctx
+ * @param windowNo
+ * @param mTab
+ * @return
+ */
+ private int getDropShipLocationId(Properties ctx, int windowNo, GridTab mTab) {
+ if (mTab.getValue("C_InvoiceLine_ID") != null) {
+ int C_InvoiceLine_ID = (Integer) mTab.getValue("C_InvoiceLine_ID");
+ if (C_InvoiceLine_ID > 0) {
+ MInvoiceLine invoiceLine = new MInvoiceLine(ctx, C_InvoiceLine_ID, null);
+ int C_OrderLine_ID = invoiceLine.getC_OrderLine_ID();
+ if (C_OrderLine_ID > 0) {
+ MOrderLine orderLine = new MOrderLine(ctx, C_OrderLine_ID, null);
+ return orderLine.getParent().getDropShip_Location_ID();
+ }
+ }
+ }
+ if (mTab.getValue("C_Invoice_ID") != null) {
+ int C_Invoice_ID = (Integer) mTab.getValue("C_Invoice_ID");
+ if (C_Invoice_ID > 0) {
+ MInvoice invoice = new MInvoice(ctx, C_Invoice_ID, null);
+ I_C_Order order = invoice.getC_Order();
+ if (order != null) {
+ return order.getDropShip_Location_ID();
+ }
+ }
+ }
+ return -1;
+ }
+
/**
* Invoice - Amount.
* - called from QtyEntered, PriceEntered
diff --git a/org.adempiere.base.callout/src/org/compiere/model/CalloutMovement.java b/org.adempiere.base.callout/src/org/compiere/model/CalloutMovement.java
index cc2f6466bc..78f8712f23 100644
--- a/org.adempiere.base.callout/src/org/compiere/model/CalloutMovement.java
+++ b/org.adempiere.base.callout/src/org/compiere/model/CalloutMovement.java
@@ -54,7 +54,7 @@ public class CalloutMovement extends CalloutEngine
&& Env.getContextAsInt(ctx, WindowNo, Env.TAB_INFO, "M_AttributeSetInstance_ID") != 0)
mTab.setValue("M_AttributeSetInstance_ID", Env.getContextAsInt(ctx, WindowNo, Env.TAB_INFO, "M_AttributeSetInstance_ID"));
else
- mTab.setValue("M_AttributeSetInstance_ID", null);
+ mTab.setValue("M_AttributeSetInstance_ID", 0);
checkQtyAvailable(ctx, mTab, WindowNo, M_Product_ID, null);
return "";
diff --git a/org.adempiere.base.callout/src/org/compiere/model/CalloutOrder.java b/org.adempiere.base.callout/src/org/compiere/model/CalloutOrder.java
index 2684da6eba..2df87e1ee1 100644
--- a/org.adempiere.base.callout/src/org/compiere/model/CalloutOrder.java
+++ b/org.adempiere.base.callout/src/org/compiere/model/CalloutOrder.java
@@ -759,7 +759,7 @@ public class CalloutOrder extends CalloutEngine
&& Env.getContextAsInt(ctx, WindowNo, Env.TAB_INFO, "M_AttributeSetInstance_ID") != 0)
mTab.setValue("M_AttributeSetInstance_ID", Env.getContextAsInt(ctx, WindowNo, Env.TAB_INFO, "M_AttributeSetInstance_ID"));
else
- mTab.setValue("M_AttributeSetInstance_ID", null);
+ mTab.setValue("M_AttributeSetInstance_ID", 0);
/***** Price Calculation see also qty ****/
int C_BPartner_ID = Env.getContextAsInt(ctx, WindowNo, "C_BPartner_ID");
@@ -972,8 +972,9 @@ public class CalloutOrder extends CalloutEngine
//
String deliveryViaRule = Env.getContext(ctx, WindowNo, I_C_Order.COLUMNNAME_DeliveryViaRule, true);
+ int dropshipLocationId = Env.getContextAsInt(ctx, WindowNo, I_C_Order.COLUMNNAME_DropShip_Location_ID, true);
int C_Tax_ID = Core.getTaxLookup().get(ctx, M_Product_ID, C_Charge_ID, billDate, shipDate,
- AD_Org_ID, M_Warehouse_ID, billC_BPartner_Location_ID, shipC_BPartner_Location_ID,
+ AD_Org_ID, M_Warehouse_ID, billC_BPartner_Location_ID, shipC_BPartner_Location_ID, dropshipLocationId,
"Y".equals(Env.getContext(ctx, WindowNo, "IsSOTrx")), deliveryViaRule, null);
if (log.isLoggable(Level.INFO)) log.info("Tax ID=" + C_Tax_ID);
//
diff --git a/org.adempiere.base.callout/src/org/compiere/model/CalloutProduction.java b/org.adempiere.base.callout/src/org/compiere/model/CalloutProduction.java
index 8d686f27e6..f4d727d817 100644
--- a/org.adempiere.base.callout/src/org/compiere/model/CalloutProduction.java
+++ b/org.adempiere.base.callout/src/org/compiere/model/CalloutProduction.java
@@ -55,7 +55,7 @@ public class CalloutProduction extends CalloutEngine
}
else
{
- mTab.setValue("M_AttributeSetInstance_ID", null);
+ mTab.setValue("M_AttributeSetInstance_ID", 0);
}
MProduct product = MProduct.get(ctx, M_Product_ID);
diff --git a/org.adempiere.base.process/src/org/compiere/process/InvoiceCreateInOut.java b/org.adempiere.base.process/src/org/compiere/process/InvoiceCreateInOut.java
index 78eea6d754..1f1a3ee1b7 100644
--- a/org.adempiere.base.process/src/org/compiere/process/InvoiceCreateInOut.java
+++ b/org.adempiere.base.process/src/org/compiere/process/InvoiceCreateInOut.java
@@ -42,6 +42,7 @@ import org.compiere.util.Env;
public class InvoiceCreateInOut extends SvrProcess
{
public static final String PARAM_M_Warehouse_ID = MInOut.COLUMNNAME_M_Warehouse_ID;
+ public static final String PARAM_C_DocType_ID = MInOut.COLUMNNAME_C_DocType_ID;
/** Warehouse */
private int p_M_Warehouse_ID = 0;
@@ -49,7 +50,9 @@ public class InvoiceCreateInOut extends SvrProcess
private int p_C_Invoice_ID = 0;
/** Receipt */
private MInOut m_inout = null;
-
+ /** Document Type */
+ private int p_C_DocType_ID = 0;
+
/**
* Prepare - e.g., get Parameters.
*/
@@ -62,6 +65,8 @@ public class InvoiceCreateInOut extends SvrProcess
;
else if (name.equals(PARAM_M_Warehouse_ID))
p_M_Warehouse_ID = para.getParameterAsInt();
+ else if (name.equals(PARAM_C_DocType_ID))
+ p_C_DocType_ID = para.getParameterAsInt();
else
MProcessPara.validateUnknownParameter(getProcessInfo().getAD_Process_ID(), para);
}
@@ -110,6 +115,8 @@ public class InvoiceCreateInOut extends SvrProcess
if (m_inout != null)
return m_inout;
m_inout = new MInOut (invoice, 0, null, p_M_Warehouse_ID);
+ if (p_C_DocType_ID != 0)
+ m_inout.setC_DocType_ID(p_C_DocType_ID);
m_inout.saveEx();
return m_inout;
}
diff --git a/org.adempiere.base.process/src/org/compiere/process/InvoiceGenerate.java b/org.adempiere.base.process/src/org/compiere/process/InvoiceGenerate.java
index 677c4fd9b2..dfa1e35ad6 100644
--- a/org.adempiere.base.process/src/org/compiere/process/InvoiceGenerate.java
+++ b/org.adempiere.base.process/src/org/compiere/process/InvoiceGenerate.java
@@ -476,7 +476,24 @@ public class InvoiceGenerate extends SvrProcess
MInvoiceLine line = new MInvoiceLine (m_invoice);
line.setShipLine(sLine);
if (sLine.sameOrderLineUOM())
- line.setQtyEntered(sLine.getQtyEntered());
+ {
+ MDocType docType = MDocType.get(ship.getC_DocType_ID());
+ if (docType.isShipConfirm()
+ && (order.getInvoiceRule().equals(MOrder.INVOICERULE_AfterDelivery)
+ || order.getInvoiceRule().equals(MOrder.INVOICERULE_AfterOrderDelivered)
+ || order.getInvoiceRule().equals(MOrder.INVOICERULE_CustomerScheduleAfterDelivery))
+ && sLine.getTargetQty() != null && sLine.getTargetQty().signum() != 0)
+ {
+ if (sLine.getQtyEntered().compareTo(sLine.getTargetQty()) == 0)
+ line.setQtyEntered(sLine.getMovementQty());
+ else
+ line.setQtyEntered(sLine.getMovementQty()
+ .multiply(sLine.getQtyEntered())
+ .divide(sLine.getTargetQty(), 12, RoundingMode.HALF_UP));
+ }
+ else
+ line.setQtyEntered(sLine.getQtyEntered());
+ }
else
line.setQtyEntered(sLine.getMovementQty());
line.setQtyInvoiced(sLine.getMovementQty());
diff --git a/org.adempiere.base.process/src/org/compiere/process/OrderCreateProduction.java b/org.adempiere.base.process/src/org/compiere/process/OrderCreateProduction.java
index f2d548c0e3..7dd1768a97 100644
--- a/org.adempiere.base.process/src/org/compiere/process/OrderCreateProduction.java
+++ b/org.adempiere.base.process/src/org/compiere/process/OrderCreateProduction.java
@@ -37,6 +37,7 @@ import org.compiere.model.MWarehouse;
import org.compiere.model.Query;
import org.compiere.util.Env;
import org.compiere.util.Msg;
+import org.eevolution.model.MPPProductBOM;
/**
*
@@ -90,11 +91,13 @@ public class OrderCreateProduction extends SvrProcess {
MProduction production = new MProduction(line);
MProduct product = new MProduct(getCtx(), line.getM_Product_ID(), get_TrxName());
+ MPPProductBOM productBOM = MPPProductBOM.getDefault(product, get_TrxName());
production.setM_Product_ID(line.getM_Product_ID());
production.setProductionQty(line.getQtyOrdered().subtract(line.getQtyDelivered()));
production.setDatePromised(line.getDatePromised());
production.setC_OrderLine_ID(line.getC_OrderLine_ID());
+ production.setPP_Product_BOM_ID(productBOM.getPP_Product_BOM_ID());
int locator = product.getM_Locator_ID();
if (locator == 0)
diff --git a/org.adempiere.base.process/src/org/compiere/process/OrderLineCreateProduction.java b/org.adempiere.base.process/src/org/compiere/process/OrderLineCreateProduction.java
index 9f821365dc..ba8514ae05 100644
--- a/org.adempiere.base.process/src/org/compiere/process/OrderLineCreateProduction.java
+++ b/org.adempiere.base.process/src/org/compiere/process/OrderLineCreateProduction.java
@@ -27,6 +27,7 @@ import org.compiere.model.MWarehouse;
import org.compiere.util.DB;
import org.compiere.util.Env;
import org.compiere.util.Msg;
+import org.eevolution.model.MPPProductBOM;
/**
* Create (Generate) Production from OrderLine
@@ -86,11 +87,13 @@ public class OrderLineCreateProduction extends SvrProcess
MProduction production = new MProduction( line );
MProduct product = new MProduct (getCtx(), line.getM_Product_ID(), get_TrxName());
+ MPPProductBOM productBOM = MPPProductBOM.getDefault(product, get_TrxName());
production.setM_Product_ID(line.getM_Product_ID());
production.setProductionQty(line.getQtyOrdered().subtract(line.getQtyDelivered()));
production.setDatePromised(line.getDatePromised());
production.setC_OrderLine_ID(p_C_OrderLine_ID);
+ production.setPP_Product_BOM_ID(productBOM.getPP_Product_BOM_ID());
int locator = product.getM_Locator_ID();
if ( locator == 0 )
diff --git a/org.adempiere.base.process/src/org/compiere/process/RequisitionPOCreate.java b/org.adempiere.base.process/src/org/compiere/process/RequisitionPOCreate.java
index 9d2c758c24..33e0926730 100644
--- a/org.adempiere.base.process/src/org/compiere/process/RequisitionPOCreate.java
+++ b/org.adempiere.base.process/src/org/compiere/process/RequisitionPOCreate.java
@@ -304,6 +304,7 @@ public class RequisitionPOCreate extends SvrProcess
|| rLine.getM_AttributeSetInstance_ID() != m_M_AttributeSetInstance_ID
|| rLine.getC_Charge_ID() != 0 // single line per charge
|| m_order == null
+ || (rLine.getC_BPartner_ID() > 0 && m_order.getC_BPartner_ID() != rLine.getC_BPartner_ID())
|| m_order.getDatePromised().compareTo(rLine.getDateRequired()) != 0
)
{
diff --git a/org.adempiere.base.process/src/org/compiere/process/TabCopy.java b/org.adempiere.base.process/src/org/compiere/process/TabCopy.java
index c730a0187a..402566bfe6 100644
--- a/org.adempiere.base.process/src/org/compiere/process/TabCopy.java
+++ b/org.adempiere.base.process/src/org/compiere/process/TabCopy.java
@@ -21,6 +21,7 @@ import java.util.logging.Level;
import org.compiere.model.MField;
import org.compiere.model.MProcessPara;
import org.compiere.model.MTab;
+import org.compiere.model.Query;
import org.compiere.util.AdempiereUserError;
import org.compiere.util.DB;
@@ -113,6 +114,11 @@ public class TabCopy extends SvrProcess
int count = 0;
for (MField oldField : from.getFields(false, get_TrxName()))
{
+ // ignore it when newField already exists
+ if (new Query(getCtx(), MField.Table_Name, "AD_Tab_ID=? AND AD_Column_ID=?", get_TrxName())
+ .setParameters(to.getAD_Tab_ID(), oldField.getAD_Column_ID())
+ .match())
+ continue;
MField newField = new MField (to, oldField);
if (! oldField.isActive())
newField.setIsActive(false);
diff --git a/org.adempiere.base.process/src/org/compiere/process/TabCreateFields.java b/org.adempiere.base.process/src/org/compiere/process/TabCreateFields.java
index f3d7559dbd..3b2b4fe042 100644
--- a/org.adempiere.base.process/src/org/compiere/process/TabCreateFields.java
+++ b/org.adempiere.base.process/src/org/compiere/process/TabCreateFields.java
@@ -100,11 +100,11 @@ public class TabCreateFields extends SvrProcess
+ " AND IsActive='Y' ";
if(!Util.isEmpty(p_EntityType))
- sql += " AND c.entitytype = ?";
+ sql += " AND c.entitytype = ? ";
if(p_CreatedSince != null)
sql += " AND c.created >= ? ";
- sql += "ORDER BY CASE "
+ sql += " ORDER BY CASE "
+ " WHEN c.ColumnName = 'AD_Client_ID' THEN -100 "
+ " WHEN c.ColumnName = 'AD_Org_ID' THEN -90 "
+ " WHEN c.IsParent = 'Y' THEN -85 "
@@ -167,7 +167,7 @@ public class TabCreateFields extends SvrProcess
}
if (column.getAD_Reference_ID() == DisplayType.Text) {
field.setNumLines(3);
- } else if (column.getAD_Reference_ID() == DisplayType.TextLong) {
+ } else if (column.getAD_Reference_ID() == DisplayType.TextLong || column.getAD_Reference_ID() == DisplayType.JSON) {
field.setNumLines(5);
} else if (column.getAD_Reference_ID() == DisplayType.Memo) {
field.setNumLines(8);
diff --git a/org.adempiere.base/src/org/adempiere/base/BaseActivator.java b/org.adempiere.base/src/org/adempiere/base/BaseActivator.java
index d042c7c473..c10fc3534a 100644
--- a/org.adempiere.base/src/org/adempiere/base/BaseActivator.java
+++ b/org.adempiere.base/src/org/adempiere/base/BaseActivator.java
@@ -28,6 +28,14 @@ import java.util.Map;
import java.util.Properties;
import org.adempiere.base.equinox.StackTraceCommand;
+import org.apache.poi.extractor.ExtractorFactory;
+import org.apache.poi.extractor.MainExtractorFactory;
+import org.apache.poi.hssf.usermodel.HSSFWorkbookFactory;
+import org.apache.poi.ooxml.extractor.POIXMLExtractorFactory;
+import org.apache.poi.sl.usermodel.SlideShowFactory;
+import org.apache.poi.ss.usermodel.WorkbookFactory;
+import org.apache.poi.xssf.usermodel.XSSFWorkbookFactory;
+import org.apache.poi.xslf.usermodel.XSLFSlideShowFactory;
import org.compiere.Adempiere;
import org.eclipse.osgi.framework.console.CommandProvider;
import org.osgi.framework.BundleActivator;
@@ -61,6 +69,13 @@ public class BaseActivator implements BundleActivator {
context.registerService(CommandProvider.class.getName(), new StackTraceCommand(), null);
blacklistService = new ComponentBlackListService(context);
+
+ //setup poi factory
+ WorkbookFactory.addProvider(new HSSFWorkbookFactory());
+ WorkbookFactory.addProvider(new XSSFWorkbookFactory());
+ SlideShowFactory.addProvider(new XSLFSlideShowFactory());
+ ExtractorFactory.addProvider(new POIXMLExtractorFactory());
+ ExtractorFactory.addProvider(new MainExtractorFactory());
}
/*
@@ -120,6 +135,14 @@ public class BaseActivator implements BundleActivator {
blacklistService.stop(context);
blacklistService = null;
}
+
+ //remove poi factory
+ WorkbookFactory.removeProvider(HSSFWorkbookFactory.class);
+ WorkbookFactory.removeProvider(XSSFWorkbookFactory.class);
+ SlideShowFactory.removeProvider(XSLFSlideShowFactory.class);
+ ExtractorFactory.removeProvider(POIXMLExtractorFactory.class);
+ ExtractorFactory.removeProvider(MainExtractorFactory.class);
+
Adempiere.stop();
}
diff --git a/org.adempiere.base/src/org/adempiere/base/DefaultTaxLookup.java b/org.adempiere.base/src/org/adempiere/base/DefaultTaxLookup.java
index 997083ae95..93b38eb052 100644
--- a/org.adempiere.base/src/org/adempiere/base/DefaultTaxLookup.java
+++ b/org.adempiere.base/src/org/adempiere/base/DefaultTaxLookup.java
@@ -51,10 +51,19 @@ public class DefaultTaxLookup implements ITaxLookup {
public int get(Properties ctx, int M_Product_ID, int C_Charge_ID, Timestamp billDate, Timestamp shipDate,
int AD_Org_ID, int M_Warehouse_ID, int billC_BPartner_Location_ID, int shipC_BPartner_Location_ID,
boolean IsSOTrx, String deliveryViaRule, String trxName) {
- return Tax.get(ctx, M_Product_ID, C_Charge_ID, billDate, shipDate, AD_Org_ID, M_Warehouse_ID, billC_BPartner_Location_ID, shipC_BPartner_Location_ID,
+ return Tax.get(ctx, M_Product_ID, C_Charge_ID, billDate, shipDate, AD_Org_ID, M_Warehouse_ID, billC_BPartner_Location_ID, shipC_BPartner_Location_ID,
IsSOTrx, deliveryViaRule, trxName);
}
+ @Override
+ public int get(Properties ctx, int M_Product_ID, int C_Charge_ID, Timestamp billDate, Timestamp shipDate,
+ int AD_Org_ID, int M_Warehouse_ID, int billC_BPartner_Location_ID, int shipC_BPartner_Location_ID,
+ int dropshipC_BPartner_Location_ID,
+ boolean IsSOTrx, String deliveryViaRule, String trxName) {
+ return Tax.get(ctx, M_Product_ID, C_Charge_ID, billDate, shipDate, AD_Org_ID, M_Warehouse_ID, billC_BPartner_Location_ID, shipC_BPartner_Location_ID,
+ dropshipC_BPartner_Location_ID, IsSOTrx, deliveryViaRule, trxName);
+ }
+
@Override
public int get(Properties ctx, int C_TaxCategory_ID, boolean IsSOTrx, Timestamp shipDate, int shipFromC_Location_ID,
int shipToC_Location_ID, Timestamp billDate, int billFromC_Location_ID, int billToC_Location_ID,
@@ -62,4 +71,11 @@ public class DefaultTaxLookup implements ITaxLookup {
return Tax.get(ctx, C_TaxCategory_ID, IsSOTrx, shipDate, shipFromC_Location_ID, shipToC_Location_ID, billDate, billFromC_Location_ID, billToC_Location_ID, trxName);
}
+ @Override
+ public int get(Properties ctx, int C_TaxCategory_ID, boolean IsSOTrx, Timestamp shipDate, int shipFromC_Location_ID,
+ int shipToC_Location_ID, int dropshipC_Location_ID, Timestamp billDate, int billFromC_Location_ID, int billToC_Location_ID,
+ String trxName) {
+ return Tax.get(ctx, C_TaxCategory_ID, IsSOTrx, shipDate, shipFromC_Location_ID, shipToC_Location_ID, dropshipC_Location_ID, billDate, billFromC_Location_ID, billToC_Location_ID, trxName);
+ }
+
}
diff --git a/org.adempiere.base/src/org/adempiere/base/ITaxLookup.java b/org.adempiere.base/src/org/adempiere/base/ITaxLookup.java
index 7861059f86..6d014aa019 100644
--- a/org.adempiere.base/src/org/adempiere/base/ITaxLookup.java
+++ b/org.adempiere.base/src/org/adempiere/base/ITaxLookup.java
@@ -55,6 +55,37 @@ public interface ITaxLookup {
int billC_BPartner_Location_ID, int shipC_BPartner_Location_ID,
boolean IsSOTrx, String deliveryViaRule, String trxName);
+ /**
+ * Find C_Tax_ID by Product/Charge + Warehouse Location + BPartner Location + DeliveryViaRule
+ * @param ctx
+ * @param M_Product_ID
+ * @param C_Charge_ID
+ * @param billDate Billing Date
+ * @param shipDate Shipment Date
+ * @param AD_Org_ID
+ * @param M_Warehouse_ID
+ * @param billC_BPartner_Location_ID Bill to location
+ * @param shipC_BPartner_Location_ID Ship to location
+ * @param dropshipC_BPartner_Location_ID Drop Ship to location (ignored if not implemented)
+ * @param IsSOTrx
+ * @param deliveryViaRule Order/Invoice's Delivery Via Rule
+ * @param trxName
+ * @return C_Tax_ID
+ */
+ public default int get (Properties ctx, int M_Product_ID, int C_Charge_ID,
+ Timestamp billDate, Timestamp shipDate,
+ int AD_Org_ID, int M_Warehouse_ID,
+ int billC_BPartner_Location_ID, int shipC_BPartner_Location_ID,
+ int dropshipC_BPartner_Location_ID,
+ boolean IsSOTrx, String deliveryViaRule, String trxName) {
+ // fallback to default method without dropshipC_BPartner_Location_ID if not implemented
+ return get(ctx, M_Product_ID, C_Charge_ID,
+ billDate, shipDate,
+ AD_Org_ID, M_Warehouse_ID,
+ billC_BPartner_Location_ID, shipC_BPartner_Location_ID,
+ IsSOTrx, deliveryViaRule, trxName);
+ }
+
/**
* Find C_Tax_ID
* @param ctx
@@ -73,4 +104,33 @@ public interface ITaxLookup {
int C_TaxCategory_ID, boolean IsSOTrx,
Timestamp shipDate, int shipFromC_Location_ID, int shipToC_Location_ID,
Timestamp billDate, int billFromC_Location_ID, int billToC_Location_ID, String trxName);
+
+ /**
+ * Find C_Tax_ID
+ * @param ctx
+ * @param C_TaxCategory_ID
+ * @param IsSOTrx
+ * @param shipDate Shipment Date
+ * @param shipFromC_Location_ID Shipping from (not use in default lookup implementation)
+ * @param shipToC_Location_ID Shipping to (not use in default lookup implementation)
+ * @param dropshipC_Location_ID Drop Ship location
+ * @param billDate Billing Date
+ * @param billFromC_Location_ID Billing from (Tax Location from)
+ * @param billToC_Location_ID Billing to (Tax Location to)
+ * @param deliveryRule Order/Invoice's Delivery Via Rule
+ * @param trxName
+ * @return C_Tax_ID
+ */
+ public default int get (Properties ctx,
+ int C_TaxCategory_ID, boolean IsSOTrx,
+ Timestamp shipDate, int shipFromC_Location_ID, int shipToC_Location_ID,
+ int dropshipC_Location_ID,
+ Timestamp billDate, int billFromC_Location_ID, int billToC_Location_ID, String trxName) {
+ // fallback to default method without dropshipC_BPartner_Location_ID if not implemented
+ return get(ctx,
+ C_TaxCategory_ID, IsSOTrx,
+ shipDate, shipFromC_Location_ID, shipToC_Location_ID,
+ billDate, billFromC_Location_ID, billToC_Location_ID, trxName);
+ }
+
}
diff --git a/org.adempiere.base/src/org/adempiere/base/event/EventManager.java b/org.adempiere.base/src/org/adempiere/base/event/EventManager.java
index e6d9838162..61a6a28a59 100644
--- a/org.adempiere.base/src/org/adempiere/base/event/EventManager.java
+++ b/org.adempiere.base/src/org/adempiere/base/event/EventManager.java
@@ -31,6 +31,7 @@ import java.util.stream.Collectors;
import org.adempiere.base.BaseActivator;
import org.adempiere.base.event.annotations.BaseEventHandler;
+import org.compiere.model.PO;
import org.compiere.util.CLogger;
import org.compiere.util.Env;
import org.compiere.util.Ini;
@@ -332,8 +333,11 @@ public class EventManager implements IEventManager {
} else {
Map map = new HashMap(3);
map.put(EventConstants.EVENT_TOPIC, topic);
- if (data != null)
+ if (data != null) {
map.put(EVENT_DATA, data);
+ if (data instanceof PO po)
+ map.put(TABLE_NAME_PROPERTY, po.get_TableName());
+ }
map.put(EVENT_ERROR_MESSAGES, new ArrayList());
if (copySessionContext)
map.put(EVENT_CONTEXT, getCurrentSessionContext());
diff --git a/org.adempiere.base/src/org/adempiere/base/sso/ISSOPrincipalService.java b/org.adempiere.base/src/org/adempiere/base/sso/ISSOPrincipalService.java
index 908a822ffd..0df7488b1e 100644
--- a/org.adempiere.base/src/org/adempiere/base/sso/ISSOPrincipalService.java
+++ b/org.adempiere.base/src/org/adempiere/base/sso/ISSOPrincipalService.java
@@ -96,4 +96,12 @@ public interface ISSOPrincipalService
* @throws ParseException
*/
public Language getLanguage(Object token) throws ParseException;
+
+ /**
+ * Get logout url
+ * @return logout url or null
+ */
+ default String getLogoutURL() {
+ return null;
+ }
}
diff --git a/org.adempiere.base/src/org/adempiere/exceptions/ProductNotOnPriceListException.java b/org.adempiere.base/src/org/adempiere/exceptions/ProductNotOnPriceListException.java
index 982a9b6ba1..eea06a63fc 100644
--- a/org.adempiere.base/src/org/adempiere/exceptions/ProductNotOnPriceListException.java
+++ b/org.adempiere.base/src/org/adempiere/exceptions/ProductNotOnPriceListException.java
@@ -64,24 +64,24 @@ public class ProductNotOnPriceListException extends AdempiereException
MProduct p = MProduct.get(Env.getCtx(), pp.getM_Product_ID());
if (sb.length() > 0)
sb.append(", ");
- sb.append("@M_Product_ID@:").append(p == null ? "?" : p.get_Translation(MProduct.COLUMNNAME_Name));
+ sb.append("@M_Product_ID@: ").append(p == null ? "?" : (p.getValue() + " - " + p.get_Translation(MProduct.COLUMNNAME_Name)));
}
if (pp.getM_PriceList_ID() > 0)
{
MPriceList pl = MPriceList.get(Env.getCtx(), pp.getM_PriceList_ID(), null);
if (sb.length() > 0)
sb.append(", ");
- sb.append("@M_PriceList_ID@:").append(pl == null ? "?" : pl.get_Translation(MPriceList.COLUMNNAME_Name));
+ sb.append("@M_PriceList_ID@: ").append(pl == null ? "?" : pl.get_Translation(MPriceList.COLUMNNAME_Name));
}
if (pp.getPriceDate() != null)
{
DateFormat df = DisplayType.getDateFormat(DisplayType.Date);
if (sb.length() > 0)
sb.append(", ");
- sb.append("@Date@:").append(df.format(pp.getPriceDate()));
+ sb.append("@Date@: ").append(df.format(pp.getPriceDate()));
}
//
- sb.insert(0, "@"+AD_Message+"@ - ");
+ sb.insert(0, "@"+AD_Message+"@ -> ");
return sb.toString();
}
}
diff --git a/org.adempiere.base/src/org/adempiere/impexp/GridTabCSVExporter.java b/org.adempiere.base/src/org/adempiere/impexp/GridTabCSVExporter.java
index 87a874a8aa..ea5d7c4a70 100644
--- a/org.adempiere.base/src/org/adempiere/impexp/GridTabCSVExporter.java
+++ b/org.adempiere.base/src/org/adempiere/impexp/GridTabCSVExporter.java
@@ -106,6 +106,8 @@ public class GridTabCSVExporter implements IGridTabExporter
specialHDispayType = DisplayType.Location;
continue;
} else if (! (field.isDisplayed() || field.isDisplayedGrid())) {
+ continue;
+ } else if (DisplayType.Binary == field.getDisplayType()) {
continue;
}
String headName = resolveColumnName(table, column);
@@ -438,15 +440,16 @@ public class GridTabCSVExporter implements IGridTabExporter
String ref = (String) idO;
value = MRefList.getListName(Env.getCtx(), column.getAD_Reference_Value_ID(), ref);
} else {
- int id = (Integer) idO;
+ MTable forTab = MTable.get(Env.getCtx(), foreignTable);
+ String foreignKeyCol = forTab.getKeyColumns()[0];
int start = headName.indexOf("[")+1;
int end = headName.length()-1;
String foreignColumn = headName.substring(start, end);
StringBuilder select = new StringBuilder("SELECT ")
.append(foreignColumn).append(" FROM ")
.append(foreignTable).append(" WHERE ")
- .append(foreignTable).append("_ID=?");
- value = DB.getSQLValueStringEx(null, select.toString(), id);
+ .append(foreignKeyCol).append("=?");
+ value = DB.getSQLValueStringEx(null, select.toString(), idO);
}
}
} else {
@@ -478,7 +481,7 @@ public class GridTabCSVExporter implements IGridTabExporter
*/
private String resolveColumnName(MTable table, MColumn column) {
StringBuilder name = new StringBuilder(column.getColumnName());
- if (DisplayType.isLookup(column.getAD_Reference_ID())) {
+ if (DisplayType.isLookup(column.getAD_Reference_ID()) && !DisplayType.isMultiID(column.getAD_Reference_ID())) {
// resolve to identifier - search for value first, if not search for name - if not use the ID
String foreignTable = column.getReferenceTableName();
if ("AD_EntityType".equals(foreignTable) && I_AD_EntityType.COLUMNNAME_AD_EntityType_ID.equals(column.getColumnName())){
@@ -579,7 +582,8 @@ public class GridTabCSVExporter implements IGridTabExporter
|| gridField.isEncryptedColumn()
|| !(gridField.isDisplayed() || gridField.isDisplayedGrid())
|| gridField.isReadOnly()
- || (DisplayType.Button == MColumn.get(Env.getCtx(),gridField.getAD_Column_ID()).getAD_Reference_ID())
+ || DisplayType.Button == gridField.getDisplayType()
+ || DisplayType.Binary == gridField.getDisplayType()
)
continue;
@@ -598,7 +602,8 @@ public class GridTabCSVExporter implements IGridTabExporter
{
if ("AD_Client_ID".equals(field.getColumnName()))
continue;
- if (DisplayType.Button == MColumn.get(Env.getCtx(),field.getAD_Column_ID()).getAD_Reference_ID())
+ if ( DisplayType.Button == field.getDisplayType()
+ || DisplayType.Binary == field.getDisplayType())
continue;
if ( field.isVirtualColumn()
|| field.isEncrypted()
diff --git a/org.adempiere.base/src/org/adempiere/impexp/GridTabCSVImporter.java b/org.adempiere.base/src/org/adempiere/impexp/GridTabCSVImporter.java
index df5cd9deac..ff2c026629 100644
--- a/org.adempiere.base/src/org/adempiere/impexp/GridTabCSVImporter.java
+++ b/org.adempiere.base/src/org/adempiere/impexp/GridTabCSVImporter.java
@@ -1022,17 +1022,16 @@ public class GridTabCSVImporter implements IGridTabImporter
if (isForeing && value != null && !"(null)".equals(value)){
String foreignTable = column.getReferenceTableName();
- String idS = null;
- int id = -1;
+ Object idS = null;
if("AD_Ref_List".equals(foreignTable))
- idS= resolveForeignList(column,foreignColumn,value,null);
+ idS = resolveForeignList(column,foreignColumn,value,null);
else
- id = resolveForeign(foreignTable,foreignColumn,value,field,null);
+ idS = resolveForeign(foreignTable,foreignColumn,value,field,null);
- if(idS == null && id < 0){
+ if(idS == null){
//it could be that record still doesn't exist if import mode is inserting or merging
if(isUpdateMode())
- return new StringBuilder(Msg.getMsg(Env.getCtx(),id==-2?"ForeignMultipleResolved":"ForeignNotResolved",new Object[]{header.get(i),value}));
+ return new StringBuilder(Msg.getMsg(Env.getCtx(),(idS instanceof Integer && (int)idS==-2)?"ForeignMultipleResolved":"ForeignNotResolved",new Object[]{header.get(i),value}));
}
} else {
// TODO: we could validate length of string or min/max
@@ -1068,6 +1067,8 @@ public class GridTabCSVImporter implements IGridTabImporter
//without Country any address would be invalid
boolean thereIsCountry = false ;
boolean isEmptyRow = true;
+ Object countryId = null;
+ int regionIndex = -1;
for(int j= i;j< header.size();j++){
if(!header.get(j).contains(MTable.getTableName(Env.getCtx(),MLocation.Table_ID)))
break;
@@ -1080,30 +1081,59 @@ public class GridTabCSVImporter implements IGridTabImporter
String columnName = header.get(j);
Object value = tmpRow.get(j);
if(value!=null){
- if(columnName.contains("C_Country_ID"))
+ if(columnName.contains("C_Country_ID")) {
thereIsCountry= true;
+ countryId = value;
+ }
}else
continue;
boolean isKeyColumn = columnName.indexOf("/") > 0;
- boolean isForeing = columnName.indexOf("[") > 0 && columnName.indexOf("]")>0;
+ boolean isForeign = columnName.indexOf("[") > 0 && columnName.indexOf("]")>0;
boolean isDetail = columnName.indexOf(">") > 0;
String foreignColumn = null;
- columnName = getColumnName(isKeyColumn,isForeing,isDetail,columnName);
- if(isForeing)
+ columnName = getColumnName(isKeyColumn,isForeign,isDetail,columnName);
+ if(isForeign)
foreignColumn = header.get(j).substring(header.get(j).indexOf("[")+1, header.get(j).indexOf("]"));
- if(isForeing && !"(null)".equals(value)){
+ if(isForeign && !"(null)".equals(value)){
String foreignTable = columnName.substring(0,columnName.length()-3);
- int id = resolveForeign(foreignTable,foreignColumn,value,field,null);
- if (id < 0)
- return new StringBuilder(Msg.getMsg(Env.getCtx(), id==-2?"ForeignMultipleResolved":"ForeignNotResolved" ,new Object[]{header.get(j),value}));
+ if ("C_Region".equals(foreignTable)) {
+ regionIndex = j;
+ } else {
+ Object id = resolveForeign(foreignTable,foreignColumn,value,field,null);
+ if (id == null || (id instanceof Integer && (int)id < 0))
+ return new StringBuilder(Msg.getMsg(Env.getCtx(),(id instanceof Integer && (int)id==-2)?"ForeignMultipleResolved":"ForeignNotResolved" ,new Object[]{header.get(j),value}));
+ if (columnName.contains("C_Country_ID")) {
+ countryId = id;
+ }
+ }
}
isEmptyRow=false;
}
MColumn column = MColumn.get(Env.getCtx(), field.getAD_Column_ID());
if((field.isMandatory(true) || column.isMandatory()) && !isEmptyRow && !thereIsCountry)
return new StringBuilder(Msg.getMsg(Env.getCtx(), "FillMandatory")+" "+field.getColumnName()+"["+"C_Country_ID]");
+
+ if (countryId != null && regionIndex != -1) {
+ String columnName = header.get(regionIndex);
+ Object value = tmpRow.get(regionIndex);
+
+ boolean isKeyColumn = columnName.indexOf("/") > 0;
+ boolean isForeign = columnName.indexOf("[") > 0 && columnName.indexOf("]") > 0;
+ boolean isDetail = columnName.indexOf(">") > 0;
+ String foreignColumn = null;
+ columnName = getColumnName(isKeyColumn, isForeign, isDetail, columnName);
+ if (isForeign)
+ foreignColumn = header.get(regionIndex).substring(header.get(regionIndex).indexOf("[") + 1, header.get(regionIndex).indexOf("]"));
+
+ if (isForeign && !"(null)".equals(value)) {
+ int id = resolveForeignRegionByCountry(foreignColumn, value, field, null, (Integer) countryId);
+ if (id < 0)
+ return new StringBuilder(Msg.getMsg(Env.getCtx(), id == -2 ? "ForeignMultipleResolved" : "ForeignNotResolved",new Object[] { header.get(regionIndex), value }));
+ }
+ }
+
}
return null;
}
@@ -1125,6 +1155,7 @@ public class GridTabCSVImporter implements IGridTabImporter
boolean isThereRow = false;
MLocation address = null;
List parentColumns = new ArrayList();
+ int regionIndex = -1;
for(int i = startindx ; i < endindx + 1 ; i++){
String columnName = header.get(i);
Object value = map.get(header.get(i));
@@ -1136,15 +1167,15 @@ public class GridTabCSVImporter implements IGridTabImporter
continue;
boolean isKeyColumn= columnName.indexOf("/") > 0;
- boolean isForeing = columnName.indexOf("[") > 0 && columnName.indexOf("]")>0;
+ boolean isForeign = columnName.indexOf("[") > 0 && columnName.indexOf("]")>0;
isDetail = columnName.indexOf(">") > 0;
- columnName = getColumnName(isKeyColumn,isForeing,isDetail,columnName);
+ columnName = getColumnName(isKeyColumn,isForeign,isDetail,columnName);
String foreignColumn = null;
Object setValue = null;
- if(isForeing)
+ if(isForeign)
foreignColumn = header.get(i).substring(header.get(i).indexOf("[")+1,header.get(i).indexOf("]"));
- if(!isForeing && !isKeyColumn && ("AD_Language".equals(columnName) || "EntityType".equals(columnName))) {
+ if(!isForeign && !isKeyColumn && ("AD_Language".equals(columnName) || "EntityType".equals(columnName))) {
setValue = value;
GridField field = gridTab.getField(columnName);
logMsg = gridTab.setValue(field,setValue);
@@ -1163,9 +1194,12 @@ public class GridTabCSVImporter implements IGridTabImporter
}
GridField field = gridTab.getField(columnName);
if(!"(null)".equals(value.toString().trim())){
- if(isForeing) {
+ if(isForeign) {
String foreignTable = columnName.substring(0,columnName.length()-3);
- setValue = resolveForeign(foreignTable,foreignColumn,value,field,trx);
+ if("C_Region".equals(foreignTable))
+ regionIndex = i;
+ else
+ setValue = resolveForeign(foreignTable,foreignColumn,value,field,trx);
if("C_City".equals(foreignTable))
address.setCity(value.toString());
}else
@@ -1184,7 +1218,7 @@ public class GridTabCSVImporter implements IGridTabImporter
break;
}
- if(isForeing && masterRecord!=null){
+ if(isForeign && masterRecord!=null){
if (masterRecord.get_Value(foreignColumn).toString().equals(value)){
logMsg = gridTab.setValue(field,masterRecord.get_ID());
if(logMsg.equals(""))
@@ -1196,7 +1230,7 @@ public class GridTabCSVImporter implements IGridTabImporter
break;
}
}
- }else if(isForeing && masterRecord==null && gridTab.getTabLevel()>0){
+ }else if(isForeign && masterRecord==null && gridTab.getTabLevel()>0){
Object master =gridTab.getParentTab().getValue(foreignColumn);
if (master!=null && value!=null && !master.toString().equals(value)){
logMsg = header.get(i)+" - "+Msg.getMsg(Env.getCtx(),"DiffParentValue", new Object[] {master.toString(),value});
@@ -1205,19 +1239,18 @@ public class GridTabCSVImporter implements IGridTabImporter
}else if (masterRecord==null && isDetail){
MColumn column = MColumn.get(Env.getCtx(),field.getAD_Column_ID());
String foreignTable = column.getReferenceTableName();
- String idS = null;
- int id = -1;
+ Object idS = null;
if ("AD_Ref_List".equals(foreignTable))
- idS= resolveForeignList(column, foreignColumn, value,trx);
+ idS = resolveForeignList(column, foreignColumn, value,trx);
else
- id = resolveForeign(foreignTable,foreignColumn,value, field, trx);
+ idS = resolveForeign(foreignTable,foreignColumn,value, field, trx);
- if(idS == null && id < 0)
- return Msg.getMsg(Env.getCtx(),id==-2?"ForeignMultipleResolved":"ForeignNotResolved",new Object[]{header.get(i),value});
+ if (idS == null || (idS instanceof Integer && (int)idS < 0))
+ return Msg.getMsg(Env.getCtx(),(idS instanceof Integer && (int)idS==-2)?"ForeignMultipleResolved":"ForeignNotResolved",new Object[]{header.get(i),value});
- if(id >= 0)
- logMsg = gridTab.setValue(field,id);
+ if (idS instanceof Integer && (int)idS >= 0)
+ logMsg = gridTab.setValue(field,idS);
else if (idS != null)
logMsg = gridTab.setValue(field,idS);
@@ -1240,7 +1273,7 @@ public class GridTabCSVImporter implements IGridTabImporter
}else{
MColumn column = MColumn.get(Env.getCtx(),field.getAD_Column_ID());
- if (isForeing){
+ if (isForeign){
String foreignTable = column.getReferenceTableName();
if ("AD_Ref_List".equals(foreignTable)) {
String idS = resolveForeignList(column, foreignColumn, value,trx);
@@ -1251,14 +1284,14 @@ public class GridTabCSVImporter implements IGridTabImporter
isThereRow =true;
} else {
- int id = resolveForeign(foreignTable, foreignColumn, value,field,trx);
- if(id < 0)
- return Msg.getMsg(Env.getCtx(),id==-2?"ForeignMultipleResolved":"ForeignNotResolved",new Object[]{header.get(i),value});
+ Object id = resolveForeign(foreignTable, foreignColumn, value,field,trx);
+ if (id == null || (id instanceof Integer && (int)id < 0))
+ return Msg.getMsg(Env.getCtx(),(id instanceof Integer && (int)id==-2)?"ForeignMultipleResolved":"ForeignNotResolved",new Object[]{header.get(i),value});
setValue = id;
if (field.isParentValue()) {
- int actualId = (Integer) field.getValue();
- if (actualId != id) {
+ Object actualId = field.getValue();
+ if (actualId != null && ! actualId.equals(id)) {
logMsg = Msg.getMsg(Env.getCtx(), "ParentCannotChange",new Object[]{header.get(i)});
break;
}
@@ -1334,6 +1367,29 @@ public class GridTabCSVImporter implements IGridTabImporter
}
if(address!=null){
+ if (regionIndex != -1 && address.getC_Country_ID() > 0) {
+ String columnName = header.get(regionIndex);
+ Object value = map.get(header.get(regionIndex));
+ boolean isDetail = false;
+ boolean isKeyColumn= columnName.indexOf("/") > 0;
+ boolean isForeign = columnName.indexOf("[") > 0 && columnName.indexOf("]")>0;
+ isDetail = columnName.indexOf(">") > 0;
+ columnName = getColumnName(isKeyColumn,isForeign,isDetail,columnName);
+ String foreignColumn = null;
+ Object setValue = null;
+
+ if(isForeign)
+ foreignColumn = header.get(regionIndex).substring(header.get(regionIndex).indexOf("[")+1,header.get(regionIndex).indexOf("]"));
+
+ GridField field = gridTab.getField(columnName);
+ if(!"(null)".equals(value.toString().trim())){
+ if(isForeign) {
+ setValue = resolveForeignRegionByCountry(foreignColumn, value, field, trx, address.getC_Country_ID());
+ }else
+ setValue = value;
+ }
+ address.set_ValueOfColumn(columnName,setValue);
+ }
if (!address.save()){
logMsg = CLogger.retrieveError()+" Address : "+address;
}else {
@@ -1380,9 +1436,9 @@ public class GridTabCSVImporter implements IGridTabImporter
setValue = idS;
} else {
- int id = resolveForeign(foreignTable, foreignColumn, setValue, field, trx);
- if (id < 0)
- return Msg.getMsg(Env.getCtx(),id==-2?"ForeignMultipleResolved":"ForeignNotResolved",new Object[]{columnName,setValue});
+ Object id = resolveForeign(foreignTable, foreignColumn, setValue, field, trx);
+ if (id == null || (id instanceof Integer && (int)id < 0))
+ return Msg.getMsg(Env.getCtx(),(id instanceof Integer && (int)id==-2)?"ForeignMultipleResolved":"ForeignNotResolved",new Object[]{columnName,setValue});
setValue = id;
}
@@ -1423,7 +1479,7 @@ public class GridTabCSVImporter implements IGridTabImporter
return (new Optional(new ParseBigDecimal(new DecimalFormatSymbols(Language.getLoginLanguage().getLocale()))));
} else if (DisplayType.YesNo == field.getDisplayType()) {
return (new Optional(new ParseBool("y", "n")));
- } else if (DisplayType.TextLong == field.getDisplayType()) {
+ } else if (DisplayType.TextLong == field.getDisplayType() || DisplayType.JSON == field.getDisplayType()) {
return (new Optional(new StrMinMax(1, Long.MAX_VALUE)));
} else if (DisplayType.isText(field.getDisplayType())) {
return (new Optional(new StrMinMax(1, field.getFieldLength())));
@@ -1478,7 +1534,7 @@ public class GridTabCSVImporter implements IGridTabImporter
String idS = resolveForeignList(column, foreignColumn, tmpValue,trx);
setValue = idS;
}else {
- int id = resolveForeign(foreignTable, foreignColumn, tmpValue,field,trx);
+ Object id = resolveForeign(foreignTable, foreignColumn, tmpValue,field,trx);
setValue = id;
}
}else{
@@ -1519,8 +1575,7 @@ public class GridTabCSVImporter implements IGridTabImporter
String idS = resolveForeignList(column,foreignColumn,value,trx);
value = idS;
}else {
- int id = resolveForeign(foreignTable,foreignColumn,value,field,trx);
- value = id;
+ value = resolveForeign(foreignTable,foreignColumn,value,field,trx);
}
}
}else{ //mandatory key not found
@@ -1587,7 +1642,7 @@ public class GridTabCSVImporter implements IGridTabImporter
* @param trx
* @return -3 for not found, -2 for more than 1 match and > 0 for foreign id
*/
- private int resolveForeign(String foreignTable, String foreignColumn, Object value, GridField field, Trx trx) {
+ private Object resolveForeign(String foreignTable, String foreignColumn, Object value, GridField field, Trx trx) {
boolean systemAccess = false;
if (!"AD_Client".equals(foreignTable)) {
MTable ft = MTable.get(Env.getCtx(), foreignTable);
@@ -1632,10 +1687,14 @@ public class GridTabCSVImporter implements IGridTabImporter
}
}
StringBuilder selectCount = new StringBuilder("SELECT COUNT(*)").append(postSelect);
- StringBuilder selectId = new StringBuilder("SELECT ").append(foreignTable).append("_ID").append(postSelect);
+ MTable forTab = MTable.get(Env.getCtx(), foreignTable);
+ StringBuilder selectId = new StringBuilder("SELECT ").append(forTab.getKeyColumns()[0]).append(postSelect);
int count = DB.getSQLValueEx(trxName, selectCount.toString(), value, thisClientId);
if (count == 1) { // single value found, OK
- return DB.getSQLValueEx(trxName, selectId.toString(), value, thisClientId);
+ if (forTab.isUUIDKeyTable())
+ return DB.getSQLValueStringEx(trxName, selectId.toString(), value, thisClientId);
+ else
+ return DB.getSQLValueEx(trxName, selectId.toString(), value, thisClientId);
} else if (count > 1) { // multiple values found, error ForeignMultipleResolved
return -2;
} else if (count == 0) { // no values found, error ForeignNotResolved
@@ -1643,7 +1702,10 @@ public class GridTabCSVImporter implements IGridTabImporter
// not found in client, try with System
count = DB.getSQLValueEx(trxName, selectCount.toString(), value, 0 /* System */);
if (count == 1) { // single value found, OK
- return DB.getSQLValueEx(trxName, selectId.toString(), value, 0 /* System */);
+ if (forTab.isUUIDKeyTable())
+ return DB.getSQLValueStringEx(trxName, selectId.toString(), value, 0 /* System */);
+ else
+ return DB.getSQLValueEx(trxName, selectId.toString(), value, 0 /* System */);
} else if (count > 1) { // multiple values found, error ForeignMultipleResolved
return -2;
}
@@ -1652,6 +1714,36 @@ public class GridTabCSVImporter implements IGridTabImporter
return -3; // no values found, error ForeignNotResolved
}
+ private int resolveForeignRegionByCountry(String foreignColumn, Object regionValue, GridField field, Trx trx, int countryId) {
+ String foreignTable = "C_Region";
+ boolean systemAccess = true;
+ int thisClientId = Env.getAD_Client_ID(Env.getCtx());
+
+ StringBuilder postSelect = new StringBuilder(" FROM ")
+ .append(foreignTable).append(" WHERE ")
+ .append(foreignColumn).append("=? AND C_Country_ID=? AND IsActive='Y' AND AD_Client_ID=?");
+
+ StringBuilder selectCount = new StringBuilder("SELECT COUNT(*)").append(postSelect);
+ StringBuilder selectId = new StringBuilder("SELECT ").append(foreignTable).append("_ID").append(postSelect);
+ int count = DB.getSQLValueEx(trxName, selectCount.toString(), regionValue, countryId, thisClientId);
+ if (count == 1) { // single value found, OK
+ return DB.getSQLValueEx(trxName, selectId.toString(), regionValue, countryId, thisClientId);
+ } else if (count > 1) { // multiple values found, error ForeignMultipleResolved
+ return -2;
+ } else if (count == 0) { // no values found, error ForeignNotResolved
+ if (systemAccess && thisClientId != 0) {
+ // not found in client, try with System
+ count = DB.getSQLValueEx(trxName, selectCount.toString(), regionValue, countryId, 0 /* System */);
+ if (count == 1) { // single value found, OK
+ return DB.getSQLValueEx(trxName, selectId.toString(), regionValue, countryId, 0 /* System */);
+ } else if (count > 1) { // multiple values found, error ForeignMultipleResolved
+ return -2;
+ }
+ }
+ }
+ return -3; // no values found, error ForeignNotResolved
+ }
+
//Copy from GridTable
@SuppressWarnings("unchecked")
private boolean isValueChanged(Object oldValue, Object value)
diff --git a/org.adempiere.base/src/org/adempiere/model/GenericPO.java b/org.adempiere.base/src/org/adempiere/model/GenericPO.java
index 23656aa137..50463421ea 100644
--- a/org.adempiere.base/src/org/adempiere/model/GenericPO.java
+++ b/org.adempiere.base/src/org/adempiere/model/GenericPO.java
@@ -26,6 +26,7 @@ import java.math.BigDecimal;
import java.sql.ResultSet;
import java.util.Properties;
+import org.adempiere.util.ServerContextPropertiesWrapper;
import org.compiere.model.MTable;
import org.compiere.model.PO;
import org.compiere.model.POInfo;
@@ -346,7 +347,7 @@ public class GenericPO extends PO implements DocAction {
* @author Low Heng Sin
*
*/
-class PropertiesWrapper extends Properties {
+class PropertiesWrapper extends ServerContextPropertiesWrapper {
/**
* generated serial id
*/
diff --git a/org.adempiere.base/src/org/adempiere/util/ModelClassGenerator.java b/org.adempiere.base/src/org/adempiere/util/ModelClassGenerator.java
index 87a71cb5f6..3ec85f8da1 100644
--- a/org.adempiere.base/src/org/adempiere/util/ModelClassGenerator.java
+++ b/org.adempiere.base/src/org/adempiere/util/ModelClassGenerator.java
@@ -19,8 +19,6 @@
*****************************************************************************/
package org.adempiere.util;
-import static org.compiere.model.SystemIDs.REFERENCE_PAYMENTRULE;
-
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
@@ -31,7 +29,6 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Collection;
-import java.util.StringTokenizer;
import java.util.TreeSet;
import java.util.logging.Level;
@@ -319,6 +316,10 @@ public class ModelClassGenerator
+ " AND c.IsActive='Y' AND (c.ColumnSQL IS NULL OR c.ColumnSQL NOT LIKE '@SQL%') "
+ (!Util.isEmpty(entityTypeFilter) ? " AND c." + entityTypeFilter : "")
+ " ORDER BY c.ColumnName";
+ if (DB.isOracle())
+ sql += " COLLATE \"BINARY\"";
+ else if (DB.isPostgreSQL())
+ sql += " COLLATE \"C\"";
boolean isKeyNamePairCreated = false; // true if the method "getKeyNamePair" is already generated
PreparedStatement pstmt = null;
ResultSet rs = null;
@@ -472,13 +473,6 @@ public class ModelClassGenerator
String staticVar = addListValidation (sb, AD_Reference_ID, columnName);
sb.insert(0, staticVar);
}
-
- // Payment Validation
- if (displayType == DisplayType.Payment)
- {
- String staticVar = addListValidation (sb, REFERENCE_PAYMENTRULE, columnName);
- sb.insert(0, staticVar);
- }
// setValue ("ColumnName", xx);
if (virtualColumn)
@@ -670,6 +664,10 @@ public class ModelClassGenerator
StringBuilder statement = new StringBuilder();
//
String sql = "SELECT Value, Name FROM AD_Ref_List WHERE AD_Reference_ID=? ORDER BY Value"; // even inactive, see IDEMPIERE-4979
+ if (DB.isOracle())
+ sql += " COLLATE \"BINARY\"";
+ else if (DB.isPostgreSQL())
+ sql += " COLLATE \"C\"";
PreparedStatement pstmt = null;
ResultSet rs = null;
try
@@ -914,121 +912,6 @@ public class ModelClassGenerator
*/
public static void generateSource(String sourceFolder, String packageName, String entityType, String tableName, String columnEntityType)
{
- if (sourceFolder == null || sourceFolder.trim().length() == 0)
- throw new IllegalArgumentException("Must specify source folder");
-
- File file = new File(sourceFolder);
- if (!file.exists())
- throw new IllegalArgumentException("Source folder doesn't exists. sourceFolder="+sourceFolder);
-
- if (packageName == null || packageName.trim().length() == 0)
- throw new IllegalArgumentException("Must specify package name");
-
- if (tableName == null || tableName.trim().length() == 0)
- throw new IllegalArgumentException("Must specify table name");
-
- StringBuilder tableLike = new StringBuilder().append(tableName.trim());
- if (!tableLike.toString().startsWith("'") || !tableLike.toString().endsWith("'"))
- tableLike = new StringBuilder("'").append(tableLike).append("'");
-
- StringBuilder entityTypeFilter = new StringBuilder();
- if (entityType != null && entityType.trim().length() > 0)
- {
- entityTypeFilter.append("EntityType IN (");
- StringTokenizer tokenizer = new StringTokenizer(entityType, ",");
- int i = 0;
- while(tokenizer.hasMoreTokens()) {
- StringBuilder token = new StringBuilder().append(tokenizer.nextToken().trim());
- if (!token.toString().startsWith("'") || !token.toString().endsWith("'"))
- token = new StringBuilder("'").append(token).append("'");
- if (i > 0)
- entityTypeFilter.append(",");
- entityTypeFilter.append(token);
- i++;
- }
- entityTypeFilter.append(")");
- }
- else
- {
- entityTypeFilter.append("EntityType IN ('U','A')");
- }
-
- StringBuilder directory = new StringBuilder().append(sourceFolder.trim());
- String packagePath = packageName.replace(".", File.separator);
- if (!(directory.toString().endsWith("/") || directory.toString().endsWith("\\")))
- {
- directory.append(File.separator);
- }
- if (File.separator.equals("/"))
- directory = new StringBuilder(directory.toString().replaceAll("[\\\\]", File.separator));
- else
- directory = new StringBuilder(directory.toString().replaceAll("[/]", File.separator));
- directory.append(packagePath);
- file = new File(directory.toString());
- if (!file.exists())
- file.mkdirs();
-
- // complete sql
- String filterViews = null;
- if (tableLike.toString().contains("%")) {
- filterViews = " AND (TableName IN ('RV_WarehousePrice','RV_BPartner') OR IsView='N')"; // special views
- }
- if (tableLike.toString().equals("'%'")) {
- filterViews += " AND TableName NOT LIKE 'W|_%' ESCAPE '|'"; // exclude webstore from general model generator
- }
- StringBuilder sql = new StringBuilder();
- sql.append("SELECT AD_Table_ID ")
- .append("FROM AD_Table ")
- .append("WHERE IsActive = 'Y' AND TableName NOT LIKE '%_Trl' ");
- // Autodetect if we need to use IN or LIKE clause - teo_sarca [ 3020640 ]
- if (tableLike.indexOf(",") == -1)
- sql.append(" AND TableName LIKE ").append(tableLike);
- else
- sql.append(" AND TableName IN (").append(tableLike).append(")"); // only specific tables
- sql.append(" AND ").append(entityTypeFilter.toString());
- if (filterViews != null) {
- sql.append(filterViews);
- }
- sql.append(" ORDER BY TableName");
- //
- StringBuilder columnFilterBuilder = new StringBuilder();
- if (!Util.isEmpty(columnEntityType, true))
- {
- columnFilterBuilder.append("EntityType IN (");
- StringTokenizer tokenizer = new StringTokenizer(columnEntityType, ",");
- int i = 0;
- while(tokenizer.hasMoreTokens()) {
- StringBuilder token = new StringBuilder().append(tokenizer.nextToken().trim());
- if (!token.toString().startsWith("'") || !token.toString().endsWith("'"))
- token = new StringBuilder("'").append(token).append("'");
- if (i > 0)
- columnFilterBuilder.append(",");
- columnFilterBuilder.append(token);
- i++;
- }
- columnFilterBuilder.append(")");
- }
- String columnFilter = columnFilterBuilder.length() > 0 ? columnFilterBuilder.toString() : null;
-
- PreparedStatement pstmt = null;
- ResultSet rs = null;
- try
- {
- pstmt = DB.prepareStatement(sql.toString(), null);
- rs = pstmt.executeQuery();
- while (rs.next())
- {
- new ModelClassGenerator(rs.getInt(1), directory.toString(), packageName, columnFilter);
- }
- }
- catch (SQLException e)
- {
- throw new DBException(e, sql.toString());
- }
- finally
- {
- DB.close(rs, pstmt);
- rs = null; pstmt = null;
- }
+ ModelInterfaceGenerator.generateSource(ModelInterfaceGenerator.GEN_SOURCE_CLASS, sourceFolder, packageName, entityType, tableName, columnEntityType);
}
}
diff --git a/org.adempiere.base/src/org/adempiere/util/ModelInterfaceGenerator.java b/org.adempiere.base/src/org/adempiere/util/ModelInterfaceGenerator.java
index 832ffdc4d9..20951e585d 100644
--- a/org.adempiere.base/src/org/adempiere/util/ModelInterfaceGenerator.java
+++ b/org.adempiere.base/src/org/adempiere/util/ModelInterfaceGenerator.java
@@ -106,6 +106,9 @@ public class ModelInterfaceGenerator
/** Logger */
private static final CLogger log = CLogger.getCLogger(ModelInterfaceGenerator.class);
+ public final static String GEN_SOURCE_INTERFACE = "I";
+ public final static String GEN_SOURCE_CLASS = "C";
+
/**
* @param AD_Table_ID
* @param directory
@@ -249,6 +252,10 @@ public class ModelInterfaceGenerator
+ " AND c.IsActive='Y' AND (c.ColumnSQL IS NULL OR c.ColumnSQL NOT LIKE '@SQL%') "
+ (!Util.isEmpty(entityTypeFilter) ? " AND c." + entityTypeFilter : "")
+ " ORDER BY c.ColumnName";
+ if (DB.isOracle())
+ sql += " COLLATE \"BINARY\"";
+ else if (DB.isPostgreSQL())
+ sql += " COLLATE \"C\"";
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
@@ -778,6 +785,18 @@ public class ModelInterfaceGenerator
* @param columnEntityType
*/
public static void generateSource(String sourceFolder, String packageName, String entityType, String tableName, String columnEntityType)
+ {
+ generateSource(GEN_SOURCE_INTERFACE, sourceFolder, packageName, entityType, tableName, columnEntityType);
+ }
+
+ /**
+ * @param sourceFolder
+ * @param packageName
+ * @param entityType
+ * @param tableName table Like
+ * @param columnEntityType
+ */
+ public static void generateSource(String type, String sourceFolder, String packageName, String entityType, String tableName, String columnEntityType)
{
if (sourceFolder == null || sourceFolder.trim().length() == 0)
throw new IllegalArgumentException("Must specify source folder");
@@ -792,9 +811,7 @@ public class ModelInterfaceGenerator
if (tableName == null || tableName.trim().length() == 0)
throw new IllegalArgumentException("Must specify table name");
- StringBuilder tableLike = new StringBuilder().append(tableName.trim());
- if (!tableLike.toString().startsWith("'") || !tableLike.toString().endsWith("'"))
- tableLike = new StringBuilder("'").append(tableLike).append("'");
+ StringBuilder tableLike = new StringBuilder().append(tableName.trim().toUpperCase().replaceAll("'", ""));
StringBuilder entityTypeFilter = new StringBuilder();
if (entityType != null && entityType.trim().length() > 0)
@@ -803,7 +820,7 @@ public class ModelInterfaceGenerator
StringTokenizer tokenizer = new StringTokenizer(entityType, ",");
int i = 0;
while(tokenizer.hasMoreTokens()) {
- StringBuilder token = new StringBuilder(tokenizer.nextToken().trim());
+ StringBuilder token = new StringBuilder().append(tokenizer.nextToken().trim());
if (!token.toString().startsWith("'") || !token.toString().endsWith("'"))
token = new StringBuilder("'").append(token).append("'");
if (i > 0)
@@ -828,7 +845,7 @@ public class ModelInterfaceGenerator
directory = new StringBuilder(directory.toString().replaceAll("[\\\\]", File.separator));
else
directory = new StringBuilder(directory.toString().replaceAll("[/]", File.separator));
- directory = new StringBuilder(directory).append(packagePath);
+ directory.append(packagePath);
file = new File(directory.toString());
if (!file.exists())
file.mkdirs();
@@ -847,9 +864,19 @@ public class ModelInterfaceGenerator
.append("WHERE IsActive = 'Y' AND TableName NOT LIKE '%_Trl' ");
// Autodetect if we need to use IN or LIKE clause - teo_sarca [ 3020640 ]
if (tableLike.indexOf(",") == -1)
- sql.append(" AND TableName LIKE ").append(tableLike);
- else
- sql.append(" AND TableName IN (").append(tableLike).append(")"); // only specific tables
+ sql.append(" AND UPPER(TableName) LIKE ").append(DB.TO_STRING(tableLike.toString()));
+ else { // only specific tables
+ StringBuilder finalTableLike = new StringBuilder("");
+ for (String table : tableLike.toString().split(",")) {
+ if (finalTableLike.length() > 0)
+ finalTableLike.append(", ");
+
+ finalTableLike.append(DB.TO_STRING(table.replaceAll("'", "").trim()));
+ }
+
+ sql.append(" AND UPPER(TableName) IN (").append(finalTableLike).append(")");
+ }
+
sql.append(" AND ").append(entityTypeFilter.toString());
if (filterViews != null) {
sql.append(filterViews);
@@ -882,10 +909,19 @@ public class ModelInterfaceGenerator
{
pstmt = DB.prepareStatement(sql.toString(), null);
rs = pstmt.executeQuery();
+
+ boolean isEmpty = true;
while (rs.next())
{
- new ModelInterfaceGenerator(rs.getInt(1), directory.toString(), packageName, columnFilter);
+ isEmpty = false;
+ if (type.equals(GEN_SOURCE_INTERFACE))
+ new ModelInterfaceGenerator(rs.getInt(1), directory.toString(), packageName, columnFilter);
+ else if (type.equals(GEN_SOURCE_CLASS))
+ new ModelClassGenerator(rs.getInt(1), directory.toString(), packageName, columnFilter);
}
+
+ if (isEmpty)
+ System.out.println("No data found for the table with name " + tableName);
}
catch (SQLException e)
{
diff --git a/org.adempiere.base/src/org/compiere/acct/Doc.java b/org.adempiere.base/src/org/compiere/acct/Doc.java
index 2bc7b926ce..ebd72a137e 100644
--- a/org.adempiere.base/src/org/compiere/acct/Doc.java
+++ b/org.adempiere.base/src/org/compiere/acct/Doc.java
@@ -30,6 +30,7 @@ import java.util.Iterator;
import java.util.Properties;
import java.util.logging.Level;
+import org.adempiere.exceptions.AdempiereException;
import org.adempiere.exceptions.DBException;
import org.compiere.model.I_C_AllocationHdr;
import org.compiere.model.I_C_Cash;
@@ -665,7 +666,7 @@ public abstract class Doc
String AD_MessageValue = "PostingError-" + p_Status;
int AD_User_ID = p_po.getUpdatedBy();
MNote note = new MNote (getCtx(), AD_MessageValue, AD_User_ID,
- getAD_Client_ID(), getAD_Org_ID(), p_po.get_TrxName());
+ getAD_Client_ID(), getAD_Org_ID(), null);
note.setRecord(p_po.get_Table_ID(), p_po.get_ID());
// Reference
note.setReference(toString()); // Document
@@ -684,7 +685,15 @@ public abstract class Doc
.append(" - " + Msg.getElement(Env.getCtx(),"IsBalanced") + "=").append( Msg.getMsg(Env.getCtx(), String.valueOf(isBalanced())))
.append(" - " + Msg.getElement(Env.getCtx(),"C_AcctSchema_ID") + "=").append(m_as.getName());
note.setTextMsg(Text.toString());
- note.saveEx();
+ try {
+ note.saveEx();
+ } catch (AdempiereException e) {
+ if (e.getMessage() != null && e.getMessage().startsWith("Foreign ID " + p_po.get_ID() + " not found in ")) {
+ ; //ignore, in unit test
+ } else {
+ throw e;
+ }
+ }
p_Error = Text.toString();
}
diff --git a/org.adempiere.base/src/org/compiere/acct/Doc_AllocationHdr.java b/org.adempiere.base/src/org/compiere/acct/Doc_AllocationHdr.java
index e4a5ba0c70..74322274e6 100644
--- a/org.adempiere.base/src/org/compiere/acct/Doc_AllocationHdr.java
+++ b/org.adempiere.base/src/org/compiere/acct/Doc_AllocationHdr.java
@@ -195,6 +195,24 @@ public class Doc_AllocationHdr extends Doc
invGainLossFactLines = new ArrayList();
payGainLossFactLines = new ArrayList();
+ // Do not create fact lines for reversal of invoice
+ if (p_lines.length == 2)
+ {
+ DocLine_Allocation line1 = (DocLine_Allocation)p_lines[0];
+ DocLine_Allocation line2 = (DocLine_Allocation)p_lines[1];
+ if (line1.getC_Payment_ID() == 0 && line1.getC_Order_ID() == 0 && line1.getC_CashLine_ID() == 0 && line1.getC_Invoice_ID() > 0
+ && line2.getC_Payment_ID() == 0 && line2.getC_Order_ID() == 0 && line2.getC_CashLine_ID() == 0 && line2.getC_Invoice_ID() > 0)
+ {
+ MInvoice invoice1 = new MInvoice(Env.getCtx(), line1.getC_Invoice_ID(), getTrxName());
+ MInvoice invoice2 = new MInvoice(Env.getCtx(), line2.getC_Invoice_ID(), getTrxName());
+ if (invoice1.getGrandTotal().equals(invoice2.getGrandTotal().negate())
+ && invoice2.getReversal_ID() == invoice1.getC_Invoice_ID())
+ {
+ return m_facts;
+ }
+ }
+ }
+
// create Fact Header
Fact fact = new Fact(this, as, Fact.POST_Actual);
Fact factForRGL = new Fact(this, as, Fact.POST_Actual); // dummy fact (not posted) to calculate Realized Gain & Loss
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 1948462568..a16f32653c 100644
--- a/org.adempiere.base/src/org/compiere/acct/Doc_Invoice.java
+++ b/org.adempiere.base/src/org/compiere/acct/Doc_Invoice.java
@@ -25,6 +25,7 @@ import java.sql.Savepoint;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.logging.Level;
@@ -34,14 +35,18 @@ import org.compiere.model.MAccount;
import org.compiere.model.MAcctSchema;
import org.compiere.model.MClientInfo;
import org.compiere.model.MConversionRate;
+import org.compiere.model.MCost;
import org.compiere.model.MCostDetail;
+import org.compiere.model.MCostElement;
import org.compiere.model.MCurrency;
+import org.compiere.model.MFactAcct;
import org.compiere.model.MInvoice;
import org.compiere.model.MInvoiceLine;
import org.compiere.model.MLandedCostAllocation;
import org.compiere.model.MOrderLandedCostAllocation;
import org.compiere.model.MTax;
import org.compiere.model.ProductCost;
+import org.compiere.model.Query;
import org.compiere.model.X_M_Cost;
import org.compiere.util.DB;
import org.compiere.util.Env;
@@ -422,7 +427,7 @@ public class Doc_Invoice extends Doc
// ** ARI, ARF
if (getDocumentType().equals(DOCTYPE_ARInvoice)
|| getDocumentType().equals(DOCTYPE_ARProForma))
- {
+ {
BigDecimal grossAmt = getAmount(Doc.AMTTYPE_Gross);
BigDecimal serviceAmt = Env.ZERO;
@@ -440,7 +445,7 @@ public class Doc_Invoice extends Doc
FactLine tl = fact.createLine(null, m_taxes[i].getAccount(DocTax.ACCTTYPE_TaxDue, as),
getC_Currency_ID(), null, amt);
if (tl != null)
- tl.setC_Tax_ID(m_taxes[i].getC_Tax_ID());
+ tl.setC_Tax_ID(m_taxes[i].getC_Tax_ID());
}
}
// Revenue CR
@@ -588,6 +593,12 @@ public class Doc_Invoice extends Doc
// ** API
else if (getDocumentType().equals(DOCTYPE_APInvoice))
{
+ MInvoice invoice = (MInvoice)getPO();
+ MInvoice originalInvoice = null;
+ if (invoice.getReversal_ID() > 0 && invoice.getReversal_ID() < invoice.getC_Invoice_ID())
+ {
+ originalInvoice = new MInvoice(Env.getCtx(), invoice.getReversal_ID(), invoice.get_TrxName());
+ }
BigDecimal grossAmt = getAmount(Doc.AMTTYPE_Gross);
BigDecimal serviceAmt = Env.ZERO;
@@ -601,6 +612,10 @@ public class Doc_Invoice extends Doc
getC_Currency_ID(), m_taxes[i].getAmount(), null);
if (tl != null)
tl.setC_Tax_ID(m_taxes[i].getC_Tax_ID());
+ if (tl != null && invoice.getReversal_ID() > 0 && invoice.getReversal_ID() < invoice.getC_Invoice_ID())
+ {
+ tl.updateReverseLine(MInvoice.Table_ID, invoice.getReversal_ID(), 0, BigDecimal.ONE);
+ }
}
// Expense DR
for (int i = 0; i < p_lines.length; i++)
@@ -620,6 +635,17 @@ public class Doc_Invoice extends Doc
else
desc += " 100%";
fl.setDescription(desc);
+ if (invoice.getReversal_ID() > 0 && invoice.getReversal_ID() < invoice.getC_Invoice_ID())
+ {
+ int lineId = 0;
+ if (originalInvoice != null)
+ {
+ MInvoiceLine[] lines = originalInvoice.getLines();
+ if (lines.length > i)
+ lineId = lines[i].getC_InvoiceLine_ID();
+ }
+ fl.updateReverseLine(MInvoice.Table_ID, invoice.getReversal_ID(), lineId, BigDecimal.ONE);
+ }
}
if (!landedCost)
{
@@ -636,12 +662,34 @@ public class Doc_Invoice extends Doc
amt = amt.add(discount);
dAmt = discount;
MAccount tradeDiscountReceived = line.getAccount(ProductCost.ACCTTYPE_P_TDiscountRec, as);
- fact.createLine (line, tradeDiscountReceived,
+ FactLine fl = fact.createLine (line, tradeDiscountReceived,
getC_Currency_ID(), null, dAmt);
+ if (fl != null && invoice.getReversal_ID() > 0 && invoice.getReversal_ID() < invoice.getC_Invoice_ID())
+ {
+ int lineId = 0;
+ if (originalInvoice != null)
+ {
+ MInvoiceLine[] lines = originalInvoice.getLines();
+ if (lines.length > i)
+ lineId = lines[i].getC_InvoiceLine_ID();
+ }
+ fl.updateReverseLine(MInvoice.Table_ID, invoice.getReversal_ID(), lineId, BigDecimal.ONE);
+ }
}
}
- fact.createLine (line, expense,
+ FactLine fl = fact.createLine (line, expense,
getC_Currency_ID(), amt, null);
+ if (fl != null && invoice.getReversal_ID() > 0 && invoice.getReversal_ID() < invoice.getC_Invoice_ID())
+ {
+ int lineId = 0;
+ if (originalInvoice != null)
+ {
+ MInvoiceLine[] lines = originalInvoice.getLines();
+ if (lines.length > i)
+ lineId = lines[i].getC_InvoiceLine_ID();
+ }
+ fl.updateReverseLine(MInvoice.Table_ID, invoice.getReversal_ID(), lineId, BigDecimal.ONE);
+ }
if (!line.isItem())
{
grossAmt = grossAmt.subtract(amt);
@@ -672,13 +720,23 @@ public class Doc_Invoice extends Doc
serviceAmt = getAmount(Doc.AMTTYPE_Gross);
grossAmt = Env.ZERO;
}
- if (grossAmt.signum() != 0)
- fact.createLine(null, MAccount.get(getCtx(), payables_ID),
+ FactLine fl = null;
+ if (grossAmt.signum() > 0)
+ fl = fact.createLine(null, MAccount.get(getCtx(), payables_ID),
getC_Currency_ID(), null, grossAmt);
- if (serviceAmt.signum() != 0)
- fact.createLine(null, MAccount.get(getCtx(), payablesServices_ID),
+ else if (grossAmt.signum() < 0)
+ fl = fact.createLine(null, MAccount.get(getCtx(), payables_ID),
+ getC_Currency_ID(), grossAmt.negate(), null);
+ if (serviceAmt.signum() > 0)
+ fl = fact.createLine(null, MAccount.get(getCtx(), payablesServices_ID),
getC_Currency_ID(), null, serviceAmt);
-
+ else if (serviceAmt.signum() < 0)
+ fl = fact.createLine(null, MAccount.get(getCtx(), payablesServices_ID),
+ getC_Currency_ID(), serviceAmt.negate(), null);
+ if (fl != null && invoice.getReversal_ID() > 0 && invoice.getReversal_ID() < invoice.getC_Invoice_ID())
+ {
+ fl.updateReverseLine(MInvoice.Table_ID, invoice.getReversal_ID(), 0, BigDecimal.ONE);
+ }
// Set Locations
FactLine[] fLines = fact.getLines();
for (int i = 0; i < fLines.length; i++)
@@ -934,7 +992,8 @@ public class Doc_Invoice extends Doc
for (int i = 0; i < lcas.length; i++)
totalBase += lcas[i].getBase().doubleValue();
- Map costDetailAmtMap = new HashMap();
+ Map costDetailAmtMap = new HashMap<>();
+ Map mcostQtyMap = new HashMap<>();
// Create New
MInvoiceLine il = new MInvoiceLine (getCtx(), C_InvoiceLine_ID, getTrxName());
@@ -962,95 +1021,159 @@ public class Doc_Invoice extends Doc
if (X_M_Cost.COSTINGMETHOD_AverageInvoice.equals(costingMethod) || X_M_Cost.COSTINGMETHOD_AveragePO.equals(costingMethod))
{
- BigDecimal allocationAmt = lca.getAmt();
+ BigDecimal allocationAmt = lca.getAmt();
+ boolean reversal = false;
+ if (allocationAmt.signum() < 0) //reversal
+ {
+ allocationAmt = allocationAmt.negate();
+ reversal = true;
+ }
+
BigDecimal estimatedAmt = BigDecimal.ZERO;
- int oCurrencyId = 0;
+ BigDecimal costAdjustmentAmt = BigDecimal.ZERO;
boolean usesSchemaCurrency = false;
- Timestamp oDateAcct = getDateAcct();
- if (lca.getM_InOutLine_ID() > 0)
+ MInvoiceLine reversalLine = null;
+ if (reversal)
{
- I_M_InOutLine iol = lca.getM_InOutLine();
- if (iol.getC_OrderLine_ID() > 0)
+ MInvoice invoice = (MInvoice)getPO();
+ MInvoice reversalInvoice = new MInvoice(getCtx(), invoice.getReversal_ID(), getTrxName());
+ MInvoiceLine[] lines = invoice.getLines();
+ MInvoiceLine[] reversalLines = reversalInvoice.getLines();
+ for(int j = 0; j < lines.length; j++) {
+ if (lines[j].get_ID() == il.get_ID()) {
+ reversalLine = reversalLines[j];
+ break;
+ }
+ }
+ }
+ else
+ {
+ int oCurrencyId = 0;
+ Timestamp oDateAcct = getDateAcct();
+ if (lca.getM_InOutLine_ID() > 0)
{
- oCurrencyId = iol.getC_OrderLine().getC_Currency_ID();
- oDateAcct = iol.getC_OrderLine().getC_Order().getDateAcct();
- MOrderLandedCostAllocation[] allocations = MOrderLandedCostAllocation.getOfOrderLine(iol.getC_OrderLine_ID(), getTrxName());
- for(MOrderLandedCostAllocation allocation : allocations)
+ I_M_InOutLine iol = lca.getM_InOutLine();
+ if (iol.getC_OrderLine_ID() > 0)
{
- if (allocation.getC_OrderLandedCost().getM_CostElement_ID() != lca.getM_CostElement_ID())
- continue;
-
- BigDecimal amt = allocation.getAmt();
- BigDecimal qty = allocation.getQty();
- if (qty.compareTo(iol.getMovementQty()) != 0)
+ oCurrencyId = iol.getC_OrderLine().getC_Currency_ID();
+ oDateAcct = iol.getC_OrderLine().getC_Order().getDateAcct();
+ MOrderLandedCostAllocation[] allocations = MOrderLandedCostAllocation.getOfOrderLine(iol.getC_OrderLine_ID(), getTrxName());
+ for(MOrderLandedCostAllocation allocation : allocations)
{
- amt = amt.multiply(iol.getMovementQty()).divide(qty, 12, RoundingMode.HALF_UP);
+ if (allocation.getC_OrderLandedCost().getM_CostElement_ID() != lca.getM_CostElement_ID())
+ continue;
+
+ BigDecimal amt = allocation.getAmt();
+ BigDecimal qty = allocation.getQty();
+ if (qty.compareTo(iol.getMovementQty()) != 0)
+ {
+ amt = amt.multiply(iol.getMovementQty()).divide(qty, 12, RoundingMode.HALF_UP);
+ }
+ estimatedAmt = estimatedAmt.add(amt);
}
- estimatedAmt = estimatedAmt.add(amt);
}
}
- }
-
- if (estimatedAmt.scale() > as.getCostingPrecision())
- {
- estimatedAmt = estimatedAmt.setScale(as.getCostingPrecision(), RoundingMode.HALF_UP);
- }
- BigDecimal costAdjustmentAmt = allocationAmt;
- if (estimatedAmt.signum() > 0)
- {
- //get other allocation amt
- StringBuilder sql = new StringBuilder("SELECT Sum(Amt) FROM C_LandedCostAllocation WHERE M_InOutLine_ID=? ")
- .append("AND C_LandedCostAllocation_ID<>? ")
- .append("AND M_CostElement_ID=? ")
- .append("AND AD_Client_ID=? ");
- BigDecimal otherAmt = DB.getSQLValueBD(getTrxName(), sql.toString(), lca.getM_InOutLine_ID(), lca.getC_LandedCostAllocation_ID(),
- lca.getM_CostElement_ID(), lca.getAD_Client_ID());
- if (otherAmt != null)
+
+ if (estimatedAmt.scale() > as.getCostingPrecision())
{
- estimatedAmt = estimatedAmt.subtract(otherAmt);
- if (allocationAmt.signum() < 0)
- {
- //add back since the sum above would include the original trx
- estimatedAmt = estimatedAmt.add(allocationAmt.negate());
- }
- }
- //added for IDEMPIERE-3014
- //convert to accounting schema currency
- if (estimatedAmt.signum() > 0 && oCurrencyId != getC_Currency_ID())
- {
- estimatedAmt = MConversionRate.convert(getCtx(), estimatedAmt,
- oCurrencyId, as.getC_Currency_ID(),
- oDateAcct, getC_ConversionType_ID(),
- getAD_Client_ID(), getAD_Org_ID());
-
- allocationAmt = MConversionRate.convert(getCtx(), allocationAmt,
- getC_Currency_ID(), as.getC_Currency_ID(),
- getDateAcct(), getC_ConversionType_ID(),
- getAD_Client_ID(), getAD_Org_ID());
- setC_Currency_ID(as.getC_Currency_ID());
- usesSchemaCurrency = true;
+ estimatedAmt = estimatedAmt.setScale(as.getCostingPrecision(), RoundingMode.HALF_UP);
}
-
+ costAdjustmentAmt = allocationAmt;
if (estimatedAmt.signum() > 0)
- {
- if (allocationAmt.signum() > 0)
- costAdjustmentAmt = allocationAmt.subtract(estimatedAmt);
- else if (allocationAmt.signum() < 0)
- costAdjustmentAmt = allocationAmt.add(estimatedAmt);
- }
- }
-
- if (!dr)
- costAdjustmentAmt = costAdjustmentAmt.negate();
+ {
+ //get other allocation amt
+ StringBuilder sql = new StringBuilder("SELECT Sum(Amt) FROM C_LandedCostAllocation WHERE M_InOutLine_ID=? ")
+ .append("AND C_LandedCostAllocation_ID<>? ")
+ .append("AND M_CostElement_ID=? ")
+ .append("AND AD_Client_ID=? ");
+ BigDecimal otherAmt = DB.getSQLValueBD(getTrxName(), sql.toString(), lca.getM_InOutLine_ID(), lca.getC_LandedCostAllocation_ID(),
+ lca.getM_CostElement_ID(), lca.getAD_Client_ID());
+ if (otherAmt != null)
+ {
+ estimatedAmt = estimatedAmt.subtract(otherAmt);
+ }
+ //added for IDEMPIERE-3014
+ //convert to accounting schema currency
+ if (estimatedAmt.signum() > 0 && oCurrencyId != getC_Currency_ID())
+ {
+ estimatedAmt = MConversionRate.convert(getCtx(), estimatedAmt,
+ oCurrencyId, as.getC_Currency_ID(),
+ oDateAcct, getC_ConversionType_ID(),
+ getAD_Client_ID(), getAD_Org_ID());
- boolean zeroQty = false;
- if (costAdjustmentAmt.signum() != 0)
+ allocationAmt = MConversionRate.convert(getCtx(), allocationAmt,
+ getC_Currency_ID(), as.getC_Currency_ID(),
+ getDateAcct(), getC_ConversionType_ID(),
+ getAD_Client_ID(), getAD_Org_ID());
+ setC_Currency_ID(as.getC_Currency_ID());
+ usesSchemaCurrency = true;
+ }
+
+ if (estimatedAmt.signum() > 0)
+ {
+ costAdjustmentAmt = allocationAmt.subtract(estimatedAmt);
+ }
+ }
+
+ if (!dr)
+ costAdjustmentAmt = costAdjustmentAmt.negate();
+ }
+
+ BigDecimal amtAsset = Env.ZERO;
+ BigDecimal amtVariance = Env.ZERO;
+ BigDecimal costDetailQty = lca.getQty();
+ if (costAdjustmentAmt.signum() != 0 && !reversal)
{
Trx trx = Trx.get(getTrxName(), false);
Savepoint savepoint = null;
try {
savepoint = trx.setSavepoint(null);
- BigDecimal costDetailAmt = costAdjustmentAmt;
+
+ amtVariance = Env.ZERO;
+ amtAsset = costAdjustmentAmt;
+
+ if(X_M_Cost.COSTINGMETHOD_AveragePO.equals(costingMethod))
+ {
+ int AD_Org_ID = lca.getAD_Org_ID();
+ int M_AttributeSetInstance_ID = lca.getM_AttributeSetInstance_ID();
+
+ if (MAcctSchema.COSTINGLEVEL_Client.equals(as.getCostingLevel()))
+ {
+ AD_Org_ID = 0;
+ M_AttributeSetInstance_ID = 0;
+ }
+ else if (MAcctSchema.COSTINGLEVEL_Organization.equals(as.getCostingLevel()))
+ M_AttributeSetInstance_ID = 0;
+ else if (MAcctSchema.COSTINGLEVEL_BatchLot.equals(as.getCostingLevel()))
+ AD_Org_ID = 0;
+
+ MCostElement ce = MCostElement.getMaterialCostElement(getCtx(), as.getCostingMethod(),
+ AD_Org_ID);
+ MCost c = MCost.get(getCtx(), getAD_Client_ID(), AD_Org_ID, lca.getM_Product_ID(),
+ as.getM_CostType_ID(), as.getC_AcctSchema_ID(), ce.getM_CostElement_ID(),
+ M_AttributeSetInstance_ID, getTrxName());
+ if (c != null)
+ {
+ BigDecimal mcostQty = c.getCurrentQty();
+ if (mcostQtyMap.containsKey(c.get_UUID())) {
+ mcostQty = mcostQty.subtract(mcostQtyMap.get(c.get_UUID()));
+ if (mcostQty.signum() < 0)
+ mcostQty = new BigDecimal("0.00");
+ }
+ if (mcostQty.compareTo(lca.getQty()) < 0) {
+ amtAsset = mcostQty.multiply(costAdjustmentAmt.divide(lca.getQty(), as.getCostingPrecision(), RoundingMode.HALF_UP));
+ amtVariance = costAdjustmentAmt.subtract(amtAsset);
+ costDetailQty = mcostQty;
+ }
+ if (mcostQtyMap.containsKey(c.get_UUID())) {
+ mcostQtyMap.put(c.get_UUID(), mcostQtyMap.get(c.get_UUID()).add(costDetailQty));
+ } else {
+ mcostQtyMap.put(c.get_UUID(), costDetailQty);
+ }
+ }
+ }
+
+ BigDecimal costDetailAmt = amtAsset;
//convert to accounting schema currency
if (getC_Currency_ID() != as.getC_Currency_ID())
costDetailAmt = MConversionRate.convert(getCtx(), costDetailAmt,
@@ -1066,18 +1189,20 @@ public class Doc_Invoice extends Doc
costDetailAmt = costDetailAmt.add(prevAmt);
}
costDetailAmtMap.put(key, costDetailAmt);
- if (!MCostDetail.createInvoice(as, lca.getAD_Org_ID(),
+ if (costDetailAmt.signum() != 0 &&
+ !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(),
+ costDetailAmt, costDetailQty,
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 {
+ try {
+ amtAsset = BigDecimal.ZERO;
+ amtVariance = costAdjustmentAmt;
trx.rollback(savepoint);
savepoint = null;
} catch (SQLException e1) {
@@ -1090,16 +1215,113 @@ public class Doc_Invoice extends Doc
} catch (SQLException e) {}
}
}
+ } else if (reversal) {
+ costDetailQty = BigDecimal.ZERO;
+ int AD_Org_ID = lca.getAD_Org_ID();
+ int M_AttributeSetInstance_ID = lca.getM_AttributeSetInstance_ID();
+
+ if (MAcctSchema.COSTINGLEVEL_Client.equals(as.getCostingLevel()))
+ {
+ AD_Org_ID = 0;
+ M_AttributeSetInstance_ID = 0;
+ }
+ else if (MAcctSchema.COSTINGLEVEL_Organization.equals(as.getCostingLevel()))
+ M_AttributeSetInstance_ID = 0;
+ else if (MAcctSchema.COSTINGLEVEL_BatchLot.equals(as.getCostingLevel()))
+ AD_Org_ID = 0;
+ String key = lca.getM_Product_ID()+"_"+M_AttributeSetInstance_ID;
+ if (!costDetailAmtMap.containsKey(key)) {
+ costDetailAmtMap.put(key, BigDecimal.ZERO);
+ amtAsset = BigDecimal.ZERO;
+ amtVariance = BigDecimal.ZERO;
+ MAccount varianceAccount = pc.getAccount(ProductCost.ACCTTYPE_P_AverageCostVariance, as);
+ MAccount assetAccount = pc.getAccount(ProductCost.ACCTTYPE_P_Asset, as);
+ Query query = MFactAcct.createRecordIdQuery(MInvoice.Table_ID, reversalLine.getC_Invoice_ID(), as.getC_AcctSchema_ID(), getTrxName());
+ List factAccts = query.list();
+ for(MFactAcct factAcct : factAccts) {
+ if (factAcct.getM_Product_ID() != lca.getM_Product_ID())
+ continue;
+ if (factAcct.getLine_ID() != reversalLine.get_ID())
+ continue;
+ if (factAcct.getAccount_ID() == assetAccount.getAccount_ID()) {
+ if (factAcct.getAmtAcctDr().signum() != 0)
+ amtAsset = amtAsset.add(factAcct.getAmtAcctDr());
+ else if (factAcct.getAmtAcctCr().signum() != 0)
+ amtAsset = amtAsset.subtract(factAcct.getAmtAcctCr());
+ } else if (factAcct.getAccount_ID() == varianceAccount.getAccount_ID()) {
+ if (factAcct.getAmtAcctDr().signum() != 0)
+ amtVariance = amtVariance.add(factAcct.getAmtAcctDr());
+ else if (factAcct.getAmtAcctCr().signum() != 0)
+ amtVariance = amtVariance.subtract(factAcct.getAmtAcctCr());
+ }
+ }
+ if (lca.getM_AttributeSetInstance_ID() > 0 && M_AttributeSetInstance_ID == 0) {
+ String sql =
+ """
+ SELECT SUM(Qty)
+ FROM M_CostDetail
+ WHERE C_InvoiceLine_ID=? AND Coalesce(M_CostElement_ID,0)=?
+ AND M_Product_ID=? AND C_AcctSchema_ID=?
+ """;
+ costDetailQty = DB.getSQLValueBDEx(getTrxName(), sql, reversalLine.get_ID(), lca.getM_CostElement_ID(), lca.getM_Product_ID(), as.getC_AcctSchema_ID());
+ if (costDetailQty == null)
+ costDetailQty = BigDecimal.ZERO;
+ } else if (lca.getM_AttributeSetInstance_ID() > 0 && M_AttributeSetInstance_ID > 0) {
+ MCostDetail cd = MCostDetail.get (as.getCtx(), "C_InvoiceLine_ID=? AND Coalesce(M_CostElement_ID,0)="+lca.getM_CostElement_ID()+" AND M_Product_ID="+lca.getM_Product_ID(),
+ reversalLine.get_ID(), lca.getM_AttributeSetInstance_ID(), as.getC_AcctSchema_ID(), getTrxName());
+ costDetailQty = cd != null ? cd.getQty() : BigDecimal.ZERO;
+ if (cd != null) {
+ amtAsset = cd.getAmt();
+ }
+ if (i > 0) {
+ for(int j = 0; j < i; j++) {
+ if (lcas[j].getM_Product_ID() == lca.getM_Product_ID()) {
+ //variance have been posted by product
+ amtVariance = BigDecimal.ZERO;
+ }
+ }
+ }
+ } else {
+ MCostDetail cd = MCostDetail.get (as.getCtx(), "C_InvoiceLine_ID=? AND Coalesce(M_CostElement_ID,0)="+lca.getM_CostElement_ID()+" AND M_Product_ID="+lca.getM_Product_ID(),
+ reversalLine.get_ID(), lca.getM_AttributeSetInstance_ID(), as.getC_AcctSchema_ID(), getTrxName());
+ costDetailQty = cd != null ? cd.getQty() : BigDecimal.ZERO;
+ }
+ if (costDetailQty.signum() != 0)
+ {
+ MCostElement ce = MCostElement.getMaterialCostElement(getCtx(), as.getCostingMethod(),
+ AD_Org_ID);
+ MCost c = MCost.get(getCtx(), getAD_Client_ID(), AD_Org_ID, lca.getM_Product_ID(),
+ as.getM_CostType_ID(), as.getC_AcctSchema_ID(), ce.getM_CostElement_ID(),
+ M_AttributeSetInstance_ID, getTrxName());
+ if (c != null) {
+ if (c.getCurrentQty().signum() == 0) {
+ amtVariance = amtVariance.add(amtAsset);
+ amtAsset = BigDecimal.ZERO;
+ } else if (c.getCurrentQty().compareTo(costDetailQty) < 0) {
+ BigDecimal currentAmtAsset = amtAsset;
+ amtAsset = amtAsset.divide(costDetailQty, RoundingMode.HALF_UP).multiply(c.getCurrentQty());
+ amtVariance = amtVariance.add(currentAmtAsset.subtract(amtAsset));
+ costDetailQty = c.getCurrentQty();
+ }
+ }
+ }
+ if (amtAsset.signum() != 0) {
+ if (!MCostDetail.createInvoice(as, lca.getAD_Org_ID(),
+ lca.getM_Product_ID(), lca.getM_AttributeSetInstance_ID(),
+ C_InvoiceLine_ID, lca.getM_CostElement_ID(),
+ amtAsset.negate(), costDetailQty,
+ desc, getTrxName())) {
+ throw new RuntimeException("Failed to create cost detail record.");
+ }
+ }
+ if (getC_Currency_ID() != as.getC_Currency_ID()) {
+ usesSchemaCurrency = true;
+ setC_Currency_ID(as.getC_Currency_ID());
+ }
+ }
}
- boolean reversal = false;
- if (allocationAmt.signum() < 0) //reversal
- {
- allocationAmt = allocationAmt.negate();
- reversal = true;
- }
-
- if (allocationAmt.signum() > 0)
+ if (allocationAmt.signum() > 0 && !reversal)
{
if (allocationAmt.scale() > as.getStdPrecision())
{
@@ -1109,46 +1331,53 @@ public class Doc_Invoice extends Doc
{
estimatedAmt = estimatedAmt.setScale(as.getStdPrecision(), RoundingMode.HALF_UP);
}
- int compare = allocationAmt.compareTo(estimatedAmt);
- if (compare > 0)
+ if (allocationAmt.compareTo(estimatedAmt)!=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());
+ if (estimatedAmt.signum() != 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 overAmt = allocationAmt.subtract(estimatedAmt);
- drAmt = dr ? (reversal ? null : overAmt) : (reversal ? overAmt : null);
- crAmt = dr ? (reversal ? overAmt : null) : (reversal ? null : overAmt);
- 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());
+ if (amtVariance.signum() != 0) {
+ if (amtVariance.signum() > 0) {
+ drAmt = dr ? amtVariance : null;
+ crAmt = dr ? null : amtVariance;
+ } else {
+ BigDecimal underAmt = amtVariance.negate();
+ drAmt = dr ? null : underAmt;
+ crAmt = dr ? underAmt : null;
+ }
+
+ account = pc.getAccount(ProductCost.ACCTTYPE_P_AverageCostVariance, 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());
+ }
+
+ if (amtAsset.signum() != 0) {
+ if (amtAsset.signum() > 0) {
+ drAmt = dr ? amtAsset : null;
+ crAmt = dr ? null : amtAsset;
+ } else {
+ BigDecimal underAmt = amtAsset.negate();
+ drAmt = dr ? null : underAmt;
+ crAmt = dr ? underAmt : null;
+ }
+ account = pc.getAccount(ProductCost.ACCTTYPE_P_Asset, 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());
+ }
}
- 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
+ else if (allocationAmt.signum() != 0)
{
drAmt = dr ? (reversal ? null : allocationAmt) : (reversal ? allocationAmt : null);
crAmt = dr ? (reversal ? allocationAmt : null) : (reversal ? null : allocationAmt);
@@ -1158,7 +1387,46 @@ public class Doc_Invoice extends Doc
fl.setM_Product_ID(lca.getM_Product_ID());
fl.setQty(line.getQty());
}
- }
+ } else if (reversal) {
+ account = pc.getAccount(ProductCost.ACCTTYPE_P_LandedCostClearing, as);
+ FactLine fl = fact.createLine (line, account, getC_Currency_ID(), BigDecimal.ZERO, BigDecimal.ZERO);
+ fl.updateReverseLine(MInvoice.Table_ID, reversalLine.getC_Invoice_ID(), reversalLine.get_ID(), BigDecimal.ONE);
+ if (fl.getAmtAcctCr().signum() == 0 && fl.getAmtAcctDr().signum() == 0)
+ fact.remove(fl);
+
+ if (amtVariance.signum() != 0) {
+ if (amtVariance.signum() > 0) {
+ drAmt = dr ? null : amtVariance;
+ crAmt = dr ? amtVariance : null;
+ } else {
+ BigDecimal underAmt = amtVariance.negate();
+ drAmt = dr ? underAmt : null;
+ crAmt = dr ? null : underAmt;
+ }
+
+ account = pc.getAccount(ProductCost.ACCTTYPE_P_AverageCostVariance, 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());
+ }
+
+ if (amtAsset.signum() != 0) {
+ if (amtAsset.signum() > 0) {
+ drAmt = dr ? null : amtAsset;
+ crAmt = dr ? amtAsset : null;
+ } else {
+ BigDecimal underAmt = amtAsset.negate();
+ drAmt = dr ? underAmt : null;
+ crAmt = dr ? null : underAmt;
+ }
+ account = 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());
+ }
+ }
if (usesSchemaCurrency)
setC_Currency_ID(line.getC_Currency_ID());
}
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 4628d49620..a07ee1bafd 100644
--- a/org.adempiere.base/src/org/compiere/acct/Doc_MatchInv.java
+++ b/org.adempiere.base/src/org/compiere/acct/Doc_MatchInv.java
@@ -36,7 +36,9 @@ import org.compiere.model.MAccount;
import org.compiere.model.MAcctSchema;
import org.compiere.model.MAcctSchemaElement;
import org.compiere.model.MConversionRate;
+import org.compiere.model.MCost;
import org.compiere.model.MCostDetail;
+import org.compiere.model.MCostElement;
import org.compiere.model.MCurrency;
import org.compiere.model.MFactAcct;
import org.compiere.model.MInOut;
@@ -389,7 +391,8 @@ public class Doc_MatchInv extends Doc
// Invoice Price Variance difference
BigDecimal ipv = cr.getAcctBalance().add(dr.getAcctBalance()).negate();
- processInvoicePriceVariance(as, fact, ipv);
+ BigDecimal ipvSource = dr.getAmtSourceDr().subtract(cr.getAmtSourceCr()).negate();
+ processInvoicePriceVariance(as, fact, ipv, ipvSource);
if (log.isLoggable(Level.FINE)) log.fine("IPV=" + ipv + "; Balance=" + fact.getSourceBalance());
String error = createMatchInvCostDetail(as);
@@ -421,15 +424,70 @@ public class Doc_MatchInv extends Doc
* @param ipv
*/
protected void processInvoicePriceVariance(MAcctSchema as, Fact fact,
- BigDecimal ipv) {
+ BigDecimal ipv, BigDecimal ipvSource) {
if (ipv.signum() == 0) return;
- FactLine pv = fact.createLine(null,
- m_pc.getAccount(ProductCost.ACCTTYPE_P_IPV, as),
- as.getC_Currency_ID(), ipv);
- updateFactLine(pv);
-
MMatchInv matchInv = (MMatchInv)getPO();
+ String costingMethod = m_pc.getProduct().getCostingMethod(as);
+ BigDecimal amtVariance = Env.ZERO;
+ BigDecimal amtAsset = Env.ZERO;
+ BigDecimal qtyMatched = matchInv.getQty();
+ BigDecimal qtyCost = null;
+ Boolean isStockCoverage = false;
+
+ boolean isReversal = matchInv.getReversal_ID() > 0 && matchInv.getReversal_ID() < matchInv.get_ID();
+ if (X_M_Cost.COSTINGMETHOD_AveragePO.equals(costingMethod) && m_invoiceLine.getM_Product_ID() > 0 && !isReversal)
+ {
+ isStockCoverage = true;
+
+ int AD_Org_ID = m_receiptLine.getAD_Org_ID();
+ int M_AttributeSetInstance_ID = matchInv.getM_AttributeSetInstance_ID();
+
+ if (MAcctSchema.COSTINGLEVEL_Client.equals(as.getCostingLevel()))
+ {
+ AD_Org_ID = 0;
+ M_AttributeSetInstance_ID = 0;
+ }
+ else if (MAcctSchema.COSTINGLEVEL_Organization.equals(as.getCostingLevel()))
+ M_AttributeSetInstance_ID = 0;
+ else if (MAcctSchema.COSTINGLEVEL_BatchLot.equals(as.getCostingLevel()))
+ AD_Org_ID = 0;
+
+ MCostElement ce = MCostElement.getMaterialCostElement(getCtx(), costingMethod, AD_Org_ID);
+
+ MCostDetail cd = MCostDetail.get (as.getCtx(), "M_MatchInv_ID=? AND Coalesce(M_CostElement_ID,0)=0",
+ matchInv.getM_MatchInv_ID(), M_AttributeSetInstance_ID, as.getC_AcctSchema_ID(), getTrxName());
+ if(cd!=null){
+ qtyCost = cd.getCurrentQty();
+ }else{
+ MCost c = MCost.get(getCtx(), getAD_Client_ID(), AD_Org_ID, m_invoiceLine.getM_Product_ID(),
+ as.getM_CostType_ID(), as.getC_AcctSchema_ID(), ce.getM_CostElement_ID(),
+ M_AttributeSetInstance_ID, getTrxName());
+ qtyCost = (c!=null? c.getCurrentQty():Env.ZERO);
+ }
+
+ if (qtyCost != null && qtyCost.compareTo(qtyMatched) < 0 )
+ {
+ //If current cost qty < invoice qty
+ amtAsset = qtyCost.multiply(ipv).divide(qtyMatched,as.getCostingPrecision(),RoundingMode.HALF_UP);
+ amtVariance = ipv.subtract(amtAsset);
+
+ }else{
+ //If current qty >= invoice qty
+ amtAsset = ipv;
+ }
+
+ }
+ else if (X_M_Cost.COSTINGMETHOD_AveragePO.equals(costingMethod) && m_invoiceLine.getM_Product_ID() > 0 && isReversal)
+ {
+ isStockCoverage = true;
+ int M_AttributeSetInstance_ID = matchInv.getM_AttributeSetInstance_ID();
+ MCostDetail cd = MCostDetail.get (as.getCtx(), "M_MatchInv_ID=? AND Coalesce(M_CostElement_ID,0)=0",
+ matchInv.getReversal_ID(), M_AttributeSetInstance_ID, as.getC_AcctSchema_ID(), getTrxName());
+ amtAsset = cd != null ? cd.getAmt().negate() : BigDecimal.ZERO;
+ amtVariance = ipv.subtract(amtAsset);
+ }
+
Trx trx = Trx.get(getTrxName(), false);
Savepoint savepoint = null;
boolean zeroQty = false;
@@ -439,7 +497,7 @@ public class Doc_MatchInv extends Doc
if (!MCostDetail.createMatchInvoice(as, m_invoiceLine.getAD_Org_ID(),
m_invoiceLine.getM_Product_ID(), m_invoiceLine.getM_AttributeSetInstance_ID(),
matchInv.getM_MatchInv_ID(), 0,
- ipv, BigDecimal.ZERO, "Invoice Price Variance", getTrxName())) {
+ isStockCoverage ? amtAsset: ipv, BigDecimal.ZERO, "Invoice Price Variance", getTrxName())) {
throw new RuntimeException("Failed to create cost detail record.");
}
} catch (SQLException e) {
@@ -460,36 +518,57 @@ public class Doc_MatchInv extends Doc
}
}
- String costingMethod = m_pc.getProduct().getCostingMethod(as);
MAccount account = m_pc.getAccount(ProductCost.ACCTTYPE_P_Asset, as);
if (m_pc.isService())
account = m_pc.getAccount(ProductCost.ACCTTYPE_P_Expense, as);
if (X_M_Cost.COSTINGMETHOD_AveragePO.equals(costingMethod)) {
- if (zeroQty)
- account = m_pc.getAccount(ProductCost.ACCTTYPE_P_AverageCostVariance, as);
- FactLine line = fact.createLine(null,
- m_pc.getAccount(ProductCost.ACCTTYPE_P_IPV, as),
- as.getC_Currency_ID(), ipv.negate());
- updateFactLine(line);
- line.setQty(getQty().negate());
-
- line = fact.createLine(null, account, as.getC_Currency_ID(), ipv);
- updateFactLine(line);
+ FactLine varianceLine = null;
+ if (amtVariance.compareTo(Env.ZERO) != 0)
+ {
+ varianceLine = fact.createLine(null,
+ m_pc.getAccount(ProductCost.ACCTTYPE_P_AverageCostVariance, as), as.getC_Currency_ID(),
+ amtVariance);
+ updateFactLine(varianceLine);
+
+ if (m_invoiceLine.getParent().getC_Currency_ID() != as.getC_Currency_ID())
+ {
+ updateFactLineAmtSource(varianceLine, ipvSource.multiply(amtVariance).divide(ipv));
+ }
+ }
+ if (amtAsset.compareTo(Env.ZERO) != 0)
+ {
+ FactLine line = fact.createLine(null, account, as.getC_Currency_ID(), amtAsset);
+ updateFactLine(line);
+
+ if (m_invoiceLine.getParent().getC_Currency_ID() != as.getC_Currency_ID())
+ {
+ updateFactLineAmtSource(line, ipvSource.multiply(amtAsset).divide(ipv));
+ }
+ }
} else if (X_M_Cost.COSTINGMETHOD_AverageInvoice.equals(costingMethod) && !zeroQty) {
- FactLine line = fact.createLine(null,
- m_pc.getAccount(ProductCost.ACCTTYPE_P_IPV, as),
- as.getC_Currency_ID(), ipv.negate());
+ //TODO test for avg Invoice costing method as here dropped posting of posting to IPV account
+ FactLine line = fact.createLine(null, account, as.getC_Currency_ID(), ipv);
updateFactLine(line);
- line.setQty(getQty().negate());
- line = fact.createLine(null, account, as.getC_Currency_ID(), ipv);
- updateFactLine(line);
+ if (m_invoiceLine.getParent().getC_Currency_ID() != as.getC_Currency_ID())
+ {
+ updateFactLineAmtSource(line, ipvSource);
+ }
+ }else{
+ //For standard costing post to IPV account
+ FactLine pv = fact.createLine(null,
+ m_pc.getAccount(ProductCost.ACCTTYPE_P_IPV, as),
+ as.getC_Currency_ID(), ipv);
+ updateFactLine(pv);
+ if (m_invoiceLine.getParent().getC_Currency_ID() != as.getC_Currency_ID())
+ {
+ updateFactLineAmtSource(pv, ipvSource);
+ }
}
}
- /**
- * Verify if the posting involves two or more organizations
- * @return true if there are more than one org involved on the posting
+ /** Verify if the posting involves two or more organizations
+ @return true if there are more than one org involved on the posting
*/
private boolean isInterOrg(MAcctSchema as) {
MAcctSchemaElement elementorg = as.getAcctSchemaElement(MAcctSchemaElement.ELEMENTTYPE_Organization);
@@ -907,7 +986,8 @@ public class Doc_MatchInv extends Doc
// Invoice Price Variance difference
BigDecimal ipv = cr.getAcctBalance().add(dr.getAcctBalance()).negate();
- processInvoicePriceVariance(as, fact, ipv);
+ BigDecimal ipvSource = dr.getAmtSourceDr().subtract(cr.getAmtSourceCr()).negate();
+ processInvoicePriceVariance(as, fact, ipv, ipvSource);
if (log.isLoggable(Level.FINE)) log.fine("IPV=" + ipv + "; Balance=" + fact.getSourceBalance());
String error = createMatchInvCostDetail(as);
@@ -1258,6 +1338,29 @@ public class Doc_MatchInv extends Doc
factLine.setQty(getQty());
}
+ /**
+ * Invoice currency & acct schema currency are not same then update AmtSource value
+ * to avoid source not balanced error/ignore suspense balancing.
+ *
+ * @param factLine
+ * @param ipvSource
+ */
+ protected void updateFactLineAmtSource(FactLine factLine, BigDecimal ipvSource)
+ {
+ // When only Rate differ then set Dr & Cr Source amount as zero.
+ factLine.setAmtSourceCr(Env.ZERO);
+ factLine.setAmtSourceDr(Env.ZERO);
+
+ // Price is vary then set Source amount according to source variance
+ if (ipvSource.compareTo(Env.ZERO) != 0)
+ {
+ if (ipvSource.signum() < 0)
+ factLine.setAmtSourceCr(ipvSource);
+ else
+ factLine.setAmtSourceDr(ipvSource);
+ }
+ }
+
/**
* Create Gain/Loss for invoice
* @param as accounting schema
diff --git a/org.adempiere.base/src/org/compiere/db/AdempiereDatabase.java b/org.adempiere.base/src/org/compiere/db/AdempiereDatabase.java
index 9b4e0afc18..6be146daa3 100644
--- a/org.adempiere.base/src/org/compiere/db/AdempiereDatabase.java
+++ b/org.adempiere.base/src/org/compiere/db/AdempiereDatabase.java
@@ -202,7 +202,18 @@ public interface AdempiereDatabase
*/
public String TO_NUMBER (BigDecimal number, int displayType);
+ /**
+ * Return string as JSON object for INSERT statements
+ * @param value
+ * @return value as JSON
+ */
+ public String TO_JSON (String value);
+ /**
+ * @return string with right casting for JSON inserts
+ */
+ public String getJSONCast ();
+
/**
* Get next sequence number in this Sequence
* @param Name Sequence name
@@ -312,7 +323,7 @@ public interface AdempiereDatabase
public boolean isPagingSupported();
/**
- * modify sql to return a subset of the query result
+ * modify sql to return a subset of the query result. use 1 base index for start and end parameter
* @param sql
* @param start
* @param end
@@ -424,6 +435,11 @@ public interface AdempiereDatabase
*/
public String getClobDataType();
+ /**
+ * @return json object data type name
+ */
+ public String getJsonDataType();
+
/**
* @return time stamp data type name
*/
diff --git a/org.adempiere.base/src/org/compiere/db/StatementProxy.java b/org.adempiere.base/src/org/compiere/db/StatementProxy.java
index 9fd61a7d8e..2da803be93 100644
--- a/org.adempiere.base/src/org/compiere/db/StatementProxy.java
+++ b/org.adempiere.base/src/org/compiere/db/StatementProxy.java
@@ -25,6 +25,7 @@ import java.util.logging.Level;
import javax.sql.RowSet;
import org.adempiere.exceptions.DBException;
+import org.compiere.model.SystemProperties;
import org.compiere.util.CCachedRowSet;
import org.compiere.util.CLogger;
import org.compiere.util.CStatementVO;
@@ -133,8 +134,11 @@ public class StatementProxy implements InvocationHandler {
}
}
Method m = p_stmt.getClass().getMethod(name, method.getParameterTypes());
+ String nullTrxName = null;
try
{
+ if (SystemProperties.isTraceNullTrxConnection() && p_vo.getTrxName() == null)
+ nullTrxName = Trx.registerNullTrx();
return m.invoke(p_stmt, args);
}
catch (InvocationTargetException e)
@@ -143,6 +147,8 @@ public class StatementProxy implements InvocationHandler {
}
finally
{
+ if (nullTrxName != null && p_vo.getTrxName() == null)
+ Trx.unregisterNullTrx(nullTrxName);
if (log.isLoggable(Level.FINE) && logSql != null && logOperation != null)
{
log.fine((DisplayType.getDateFormat(DisplayType.DateTime)).format(new Date(System.currentTimeMillis()))+","+logOperation+","+logSql+","+(p_vo.getTrxName() != null ? p_vo.getTrxName() : "")+" (end)");
diff --git a/org.adempiere.base/src/org/compiere/dbPort/Convert.java b/org.adempiere.base/src/org/compiere/dbPort/Convert.java
index 0d8c5896ef..a3d894d0d5 100644
--- a/org.adempiere.base/src/org/compiere/dbPort/Convert.java
+++ b/org.adempiere.base/src/org/compiere/dbPort/Convert.java
@@ -83,10 +83,12 @@ public abstract class Convert
/** Logger */
private static final CLogger log = CLogger.getCLogger (Convert.class);
+ private static File fileOr = null;
private static FileOutputStream fosScriptOr = null;
- private static Writer writerOr;
+ private static Writer writerOr = null;
+ private static File filePg = null;
private static FileOutputStream fosScriptPg = null;
- private static Writer writerPg;
+ private static Writer writerPg = null;
/**
* Set Verbose
@@ -471,7 +473,7 @@ public abstract class Convert
Files.createDirectories(Paths.get(folderPg));
}
if (fosScriptOr == null) {
- File fileOr = new File(folderOr + fileName);
+ fileOr = new File(folderOr + fileName);
fosScriptOr = new FileOutputStream(fileOr, true);
writerOr = new BufferedWriter(new OutputStreamWriter(fosScriptOr, "UTF8"));
writerOr.append("-- ");
@@ -488,7 +490,7 @@ public abstract class Convert
pgStatement = r[0];
}
if (fosScriptPg == null) {
- File filePg = new File(folderPg + fileName);
+ filePg = new File(folderPg + fileName);
fosScriptPg = new FileOutputStream(filePg, true);
writerPg = new BufferedWriter(new OutputStreamWriter(fosScriptPg, "UTF8"));
writerPg.append("-- ");
@@ -688,4 +690,48 @@ public abstract class Convert
w.flush();
}
+ /**
+ * Close the files for migration scripts, used just on Tests
+ */
+ public static void closeLogMigrationScript() {
+ try {
+ if (writerOr != null) {
+ writerOr.flush();
+ writerOr.close();
+ writerOr = null;
+ }
+ if (writerPg != null) {
+ writerPg.flush();
+ writerPg.close();
+ writerPg = null;
+ }
+ if (fosScriptOr != null) {
+ fosScriptOr.flush();
+ fosScriptOr.close();
+ fosScriptOr = null;
+ }
+ if (fosScriptPg != null) {
+ fosScriptPg.flush();
+ fosScriptPg.close();
+ fosScriptPg = null;
+ }
+ fileOr = null;
+ filePg = null;
+ } catch (IOException e) {
+ // ignore
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Get the name of the migration script file
+ * @return
+ */
+ public static String getGeneratedMigrationScriptFileName() {
+ if (filePg != null) {
+ return filePg.getName();
+ }
+ return null;
+ }
+
} // Convert
\ No newline at end of file
diff --git a/org.adempiere.base/src/org/compiere/impexp/OFXBankStatementHandler.java b/org.adempiere.base/src/org/compiere/impexp/OFXBankStatementHandler.java
index 0b8fd4a8f9..fa0c096fbd 100644
--- a/org.adempiere.base/src/org/compiere/impexp/OFXBankStatementHandler.java
+++ b/org.adempiere.base/src/org/compiere/impexp/OFXBankStatementHandler.java
@@ -245,7 +245,8 @@ public abstract class OFXBankStatementHandler extends DefaultHandler
if (isOfx1)
{
- m_reader = new BufferedReader(new InputStreamReader(new OFX1ToXML(reader)));
+ OFX1ToXML in = new OFX1ToXML(reader);
+ m_reader = new BufferedReader(new InputStreamReader(in));
}
else
{
@@ -257,9 +258,12 @@ public abstract class OFXBankStatementHandler extends DefaultHandler
{
m_errorMessage = new StringBuffer("ErrorReadingData");
m_errorDescription = new StringBuffer(e.getMessage());
- closeBufferedReader();
return result;
}
+ finally
+ {
+ closeBufferedReader();
+ }
return result;
} // attachInput
diff --git a/org.adempiere.base/src/org/compiere/impexp/OFXFileBankStatementLoader.java b/org.adempiere.base/src/org/compiere/impexp/OFXFileBankStatementLoader.java
index dc86eeb1f4..7b3daed703 100644
--- a/org.adempiere.base/src/org/compiere/impexp/OFXFileBankStatementLoader.java
+++ b/org.adempiere.base/src/org/compiere/impexp/OFXFileBankStatementLoader.java
@@ -17,6 +17,7 @@
package org.compiere.impexp;
import java.io.FileInputStream;
+import java.io.IOException;
import org.compiere.model.MBankStatementLoader;
import org.xml.sax.SAXException;
@@ -70,6 +71,13 @@ public final class OFXFileBankStatementLoader extends OFXBankStatementHandler im
m_errorMessage = new StringBuffer("ErrorReadingData");
m_errorDescription = new StringBuffer();
}
+ finally
+ {
+ if (m_stream != null)
+ try {
+ m_stream.close();
+ } catch (IOException e) {}
+ }
return result;
} // init
diff --git a/org.adempiere.base/src/org/compiere/model/DataStatusEvent.java b/org.adempiere.base/src/org/compiere/model/DataStatusEvent.java
index 5fbbb1e161..313c44331c 100644
--- a/org.adempiere.base/src/org/compiere/model/DataStatusEvent.java
+++ b/org.adempiere.base/src/org/compiere/model/DataStatusEvent.java
@@ -257,10 +257,9 @@ public final class DataStatusEvent extends EventObject implements Serializable
retValue.append(m_currentRow+1);
// of
retValue.append("/");
- if (m_allLoaded)
- retValue.append(m_totalRows);
- else
- retValue.append(m_loadedRows).append("->").append(m_totalRows);
+ if (! m_allLoaded)
+ retValue.append(m_loadedRows).append("->");
+ retValue.append(m_totalRows);
//
return retValue.toString();
} // getMessage
@@ -358,6 +357,7 @@ public final class DataStatusEvent extends EventObject implements Serializable
e.m_changedColumn == m_changedColumn &&
Util.equals(e.m_columnName, m_columnName) &&
e.m_currentRow == m_currentRow &&
+ e.m_loadedRows == m_loadedRows &&
e.isInitEdit == isInitEdit;
}
diff --git a/org.adempiere.base/src/org/compiere/model/GridField.java b/org.adempiere.base/src/org/compiere/model/GridField.java
index 69ac181f94..0e29105ab1 100644
--- a/org.adempiere.base/src/org/compiere/model/GridField.java
+++ b/org.adempiere.base/src/org/compiere/model/GridField.java
@@ -881,6 +881,7 @@ public class GridField
if (m_vo.DefaultValue != null && !m_vo.DefaultValue.equals("") && !m_vo.DefaultValue.startsWith(MColumn.VIRTUAL_UI_COLUMN_PREFIX))
{
String defStr = ""; // problem is with texts like 'sss;sss'
+ String defStrMultipleSelect = "";
// It is one or more variables/constants
StringTokenizer st = new StringTokenizer(m_vo.DefaultValue, ",;", false);
while (st.hasMoreTokens())
@@ -892,7 +893,16 @@ public class GridField
defStr = Env.parseContext(m_vo.ctx, m_vo.WindowNo, m_vo.TabNo, defStr.trim(), false, false);
else if (defStr.indexOf("'") != -1) // it is a 'String'
defStr = defStr.replace('\'', ' ').trim();
-
+
+ if (DisplayType.isChosenMultipleSelection(m_vo.displayType)) {
+ defStrMultipleSelect += defStr + ",";
+ if (!st.hasMoreTokens()) {
+ defStr = defStrMultipleSelect.substring(0, defStrMultipleSelect.length() - 1);
+ } else {
+ continue;
+ }
+ }
+
if (!defStr.equals(""))
{
if (log.isLoggable(Level.FINE)) log.fine("[DefaultValue] " + m_vo.ColumnName + "=" + defStr);
@@ -2106,6 +2116,7 @@ public class GridField
if (m_vo.displayType == DisplayType.Text
|| m_vo.displayType == DisplayType.Memo
|| m_vo.displayType == DisplayType.TextLong
+ || m_vo.displayType == DisplayType.JSON
|| m_vo.displayType == DisplayType.Binary
|| m_vo.displayType == DisplayType.RowID
|| isEncrypted())
diff --git a/org.adempiere.base/src/org/compiere/model/GridTab.java b/org.adempiere.base/src/org/compiere/model/GridTab.java
index 29297e0e24..30c9a6961f 100644
--- a/org.adempiere.base/src/org/compiere/model/GridTab.java
+++ b/org.adempiere.base/src/org/compiere/model/GridTab.java
@@ -235,6 +235,8 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
public static final String CTX_IsLookupOnlySelection = "_TabInfo_IsLookupOnlySelection";
public static final String CTX_IsAllowAdvancedLookup = "_TabInfo_IsAllowAdvancedLookup";
+ public static final int DEFAULT_GLOBAL_MAX_QUERY_RECORDS = 100000;
+
/**
* Tab loader for Tabs > 0
*/
@@ -2544,20 +2546,23 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
}
// Row Count
int rows = getRowCount();
- if (rows == 0)
+ if (rows == 0 && !m_mTable.isLoading())
{
log.fine("No Rows");
return -1;
}
if (newRow >= rows)
{
- newRow = rows-1;
- if (log.isLoggable(Level.FINE)) log.fine("Set to max Row: " + newRow);
+ if (!m_mTable.isLoading())
+ {
+ newRow = rows-1;
+ if (log.isLoggable(Level.FINE)) log.fine("Set to max Row: " + newRow);
+ }
}
else if (newRow < 0)
{
newRow = 0;
- log.fine("Set to first Row");
+ if (log.isLoggable(Level.FINE)) log.fine("Set to first Row");
}
m_mTable.waitLoadingForRow(newRow);
@@ -2860,7 +2865,8 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
MLookup mLookup = (MLookup)dependentField.getLookup();
// if the lookup is dynamic (i.e. contains this columnName as variable)
if (mLookup.getValidation().indexOf("@"+columnName+"@") != -1
- || mLookup.getValidation().matches(".*[@]"+columnName+"[:].+[@].*$"))
+ || mLookup.getValidation().matches(".*[@]"+getTabNo()+"[|]"+columnName+"([:].+)?[@].*")
+ || mLookup.getValidation().matches(".*[@][~]?"+columnName+"([:].+)?[@].*"))
{
if (log.isLoggable(Level.FINE)) log.fine(columnName + " changed - "
+ dependentField.getColumnName() + " set to null");
@@ -3556,6 +3562,9 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
int tabMaxQueryRecords = m_vo.MaxQueryRecords;
if (roleMaxQueryRecords > 0 && (roleMaxQueryRecords < tabMaxQueryRecords || tabMaxQueryRecords == 0))
tabMaxQueryRecords = roleMaxQueryRecords;
+ if (tabMaxQueryRecords == 0)
+ tabMaxQueryRecords = MSysConfig.getIntValue(MSysConfig.GLOBAL_MAX_QUERY_RECORDS,
+ DEFAULT_GLOBAL_MAX_QUERY_RECORDS, Env.getAD_Client_ID(Env.getCtx()));
return tabMaxQueryRecords;
}
diff --git a/org.adempiere.base/src/org/compiere/model/GridTable.java b/org.adempiere.base/src/org/compiere/model/GridTable.java
index 7893bc8355..ae7af29209 100644
--- a/org.adempiere.base/src/org/compiere/model/GridTable.java
+++ b/org.adempiere.base/src/org/compiere/model/GridTable.java
@@ -46,6 +46,7 @@ import java.util.logging.Level;
import javax.swing.event.TableModelListener;
import javax.swing.table.AbstractTableModel;
+import org.adempiere.exceptions.AdempiereException;
import org.adempiere.exceptions.DBException;
import org.adempiere.util.ServerContext;
import org.compiere.Adempiere;
@@ -95,14 +96,15 @@ public class GridTable extends AbstractTableModel
implements Serializable
{
/**
- * generated serial id
+ *
*/
- private static final long serialVersionUID = -5564364545827057092L;
+ private static final long serialVersionUID = 3948220810042370826L;
protected static final String SORTED_DSE_EVENT = "Sorted";
public static final int DEFAULT_GRIDTABLE_LOAD_TIMEOUT_IN_SECONDS = 30;
-
+ public static final int DEFAULT_GRIDTABLE_COUNT_TIMEOUT_IN_SECONDS = 1;
+
public static final String LOAD_TIMEOUT_ERROR_MESSAGE = "GridTabLoadTimeoutError";
public static final String DATA_REFRESH_MESSAGE = "Refreshed";
@@ -169,6 +171,8 @@ public class GridTable extends AbstractTableModel
/** Rowcount */
private int m_rowCount = 0;
+ private boolean m_rowCountTimeout = false;
+ private boolean m_rowLoadTimeout = false;
/** Has Data changed? */
private boolean m_changed = false;
/** Index of changed row via SetValueAt */
@@ -426,7 +430,8 @@ public class GridTable extends AbstractTableModel
//IDEMPIERE-5193 Add Limit to Query
if(m_maxRows > 0 && DB.getDatabase().isPagingSupported())
{
- m_SQL = DB.getDatabase().addPagingSQL(m_SQL, 1, m_maxRows);
+ // set to maxRows plus one to trigger FindOverMax on overflow
+ m_SQL = DB.getDatabase().addPagingSQL(m_SQL, 1, m_maxRows+1);
}
//
@@ -645,7 +650,8 @@ public class GridTable extends AbstractTableModel
m_buffer = new ArrayList