You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ofbiz.apache.org by ml...@apache.org on 2024/10/04 14:19:59 UTC

(ofbiz-framework) branch trunk updated: Improved: Convert OrderServices.xml mini-lang to groovyDSL checkCreateStockRequirementQoh, checkCreateStockRequirementAtp and checkCreateProductRequirementForFacility (OFBIZ-9984)

This is an automated email from the ASF dual-hosted git repository.

mleila pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ofbiz-framework.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 014056918f Improved: Convert OrderServices.xml mini-lang to groovyDSL checkCreateStockRequirementQoh, checkCreateStockRequirementAtp and checkCreateProductRequirementForFacility (OFBIZ-9984)
014056918f is described below

commit 014056918f000d0d58b3fb48df99bca9042130b9
Author: MLeila <ml...@apache.org>
AuthorDate: Fri Oct 4 16:19:50 2024 +0200

    Improved: Convert OrderServices.xml mini-lang to groovyDSL
    checkCreateStockRequirementQoh, checkCreateStockRequirementAtp and
    checkCreateProductRequirementForFacility (OFBIZ-9984)
    
    Migration of the services and corresponding tests update to ensure
---
 .../order/minilang/order/OrderServices.xml         | 222 ---------------------
 .../order/servicedef/services_requirement.xml      |  12 +-
 .../order/OrderRequirementServicesScript.groovy    | 174 +++++++++++++++-
 .../order/order/test/OrderRequirementTests.groovy  |   9 +-
 applications/order/testdef/data/OrderTestData.xml  |  12 +-
 5 files changed, 193 insertions(+), 236 deletions(-)

diff --git a/applications/order/minilang/order/OrderServices.xml b/applications/order/minilang/order/OrderServices.xml
index f24b297dc9..1147b39bce 100644
--- a/applications/order/minilang/order/OrderServices.xml
+++ b/applications/order/minilang/order/OrderServices.xml
@@ -21,228 +21,6 @@ under the License.
 <simple-methods xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://ofbiz.apache.org/Simple-Method" xsi:schemaLocation="http://ofbiz.apache.org/Simple-Method http://ofbiz.apache.org/dtds/simple-methods.xsd">
 
-    <!-- order requirement methods -->
-    <simple-method method-name="getProductFacilityAndQuantities" short-description="finds ProductFacility and QOH, ATP inventory for an inventoryItem">
-        <!-- Get the ProductFacility for the minimum stock level -->
-        <entity-one entity-name="ProductFacility" value-field="productFacility">
-            <field-map field-name="productId" from-field="inventoryItem.productId"/>
-            <field-map field-name="facilityId" from-field="inventoryItem.facilityId"/>
-        </entity-one>
-
-        <!-- Get the product's total quantityOnHand in the facility -->
-        <set from-field="inventoryItem.productId" field="inputMap.productId"/>
-        <set from-field="inventoryItem.facilityId" field="inputMap.facilityId"/>
-        <call-service service-name="getInventoryAvailableByFacility" in-map-name="inputMap">
-           <result-to-field result-name="quantityOnHandTotal" field="quantityOnHandTotal"/>
-           <result-to-field result-name="availableToPromiseTotal" field="availableToPromiseTotal"/>
-        </call-service>
-        <clear-field field="inputMap"/>
-    </simple-method>
-
-    <simple-method method-name="getProductRequirementMethod" short-description="finds the requirement method for the product">
-        <if-not-empty field="parameters.orderId">
-            <entity-one entity-name="OrderHeader" auto-field-map="true" value-field="order"/>
-        </if-not-empty>
-        <entity-one entity-name="Product" auto-field-map="true" value-field="product"/>
-        <set from-field="product.requirementMethodEnumId" field="requirementMethodId"/>
-        <if-empty field="requirementMethodId">
-            <set field="isMarketingPkg" value="${groovy: org.apache.ofbiz.entity.util.EntityTypeUtil.hasParentType(delegator, 'ProductType', 'productTypeId', product.productTypeId, 'parentTypeId', 'MARKETING_PKG')}" type="Boolean"/>
-            <if>
-                <condition>
-                    <and>
-                        <if-compare field="isMarketingPkg" operator="equals" value="false" type="Boolean"/>
-                        <if-compare field="product.productTypeId" operator="not-equals" value="DIGITAL_GOOD"/>
-                        <not><if-empty field="order"/></not>
-                    </and>
-                </condition>
-                <then>
-                    <entity-one entity-name="ProductStore" value-field="productStore">
-                        <field-map field-name="productStoreId" from-field="order.productStoreId"/>
-                    </entity-one>
-                    <set from-field="productStore.requirementMethodEnumId" field="requirementMethodId"/>
-                </then>
-            </if>
-        </if-empty>
-    </simple-method>
-
-    <simple-method method-name="checkCreateStockRequirementQoh" short-description="Create a Requirement if QOH goes under the minimum stock level">
-        <check-permission permission="ORDERMGR" action="_CREATE">
-            <fail-property resource="OrderErrorUiLabels" property="OrderSecurityErrorToRunCheckCreateStockRequirement"/>
-        </check-permission>
-        <check-errors/>
-
-        <!-- If the service is triggered by the updateItemIssuance service, get the ItemIssuance by the passed itemIssuanceId -->
-        <if-not-empty field="parameters.itemIssuanceId">
-            <entity-one entity-name="ItemIssuance" auto-field-map="true" value-field="itemIssuance"/>
-            <entity-one entity-name="InventoryItem" value-field="inventoryItem">
-                <field-map field-name="inventoryItemId" from-field="itemIssuance.inventoryItemId"/>
-            </entity-one>
-        <else>
-            <entity-one entity-name="InventoryItem" auto-field-map="true" value-field="inventoryItem"/>
-        </else>
-        </if-not-empty>
-
-        <set from-field="inventoryItem.productId" field="parameters.productId"/>
-        <call-simple-method method-name="getProductRequirementMethod"/>
-
-        <if-compare field="requirementMethodId" operator="equals" value="PRODRQM_STOCK">
-            <!-- get QOH, ATP quantities and find ProductFacility which has the minimum stock -->
-            <call-simple-method method-name="getProductFacilityAndQuantities"/>
-
-            <if-not-empty field="productFacility.minimumStock">
-                <!-- No requirements are created if we are already under stock -->
-                <if-compare-field field="quantityOnHandTotal" to-field="productFacility.minimumStock" operator="greater-equals" type="BigDecimal">
-                    <calculate field="newQuantityOnHand">
-                        <calcop operator="subtract" field="quantityOnHandTotal">
-                            <calcop operator="get" field="parameters.quantity"/>
-                        </calcop>
-                    </calculate>
-                    <!-- If this new issuance will cause the quantityOnHandTotal to go below the minimumStock, create a new requirement -->
-                    <if-compare-field field="newQuantityOnHand" to-field="productFacility.minimumStock" operator="less" type="BigDecimal">
-                        <set from-field="inventoryItem.productId" field="inputMap.productId"/>
-                        <set from-field="productFacility.facilityId" field="inputMap.facilityId"/>
-                        <if-not-empty field="productFacility.reorderQuantity">
-                            <set from-field="productFacility.reorderQuantity" field="inputMap.quantity"/>
-                        <else>
-                            <set from-field="parameters.quantity" field="inputMap.quantity"/>
-                        </else>
-                        </if-not-empty>
-                        <set value="PRODUCT_REQUIREMENT" field="inputMap.requirementTypeId"/>
-                        <call-service service-name="createRequirement" in-map-name="inputMap">
-                            <result-to-field result-name="requirementId" field="parameters.requirementId"/>
-                        </call-service>
-                        <field-to-result field="parameters.requirementId" result-name="requirementId"/>
-                    </if-compare-field>
-                </if-compare-field>
-            </if-not-empty>
-        </if-compare>
-    </simple-method>
-
-    <simple-method method-name="checkCreateStockRequirementAtp" short-description="Create a Requirement if ATP goes under the minimum stock level">
-        <check-permission permission="ORDERMGR" action="_CREATE">
-            <fail-property resource="OrderErrorUiLabels" property="OrderSecurityErrorToRunCheckCreateStockRequirement"/>
-        </check-permission>
-        <check-errors/>
-
-        <!-- assumes that inventoryItemId is one of the parameters and get the inventory item of the reservation -->
-        <entity-one entity-name="InventoryItem" value-field="inventoryItem" auto-field-map="true"/>
-
-        <!-- find the requirement method for this product -->
-        <set from-field="inventoryItem.productId" field="parameters.productId"/>
-        <call-simple-method method-name="getProductRequirementMethod"/>
-
-        <if-compare field="requirementMethodId" operator="equals" value="PRODRQM_STOCK_ATP">
-            <!-- get QOH, ATP quantities and find ProductFacility which has the minimum stock -->
-            <call-simple-method method-name="getProductFacilityAndQuantities"/>
-
-            <if-not-empty field="productFacility.minimumStock">
-                <!-- No requirements are created if we are not under stock -->
-                <!-- this service is supposed to be called after inventory is reserved, so inventory should have been updated already -->
-                <if-compare-field field="availableToPromiseTotal" to-field="productFacility.minimumStock" operator="less" type="BigDecimal">
-                    <calculate field="oldAvailableToPromise">
-                        <calcop operator="add" field="availableToPromiseTotal">
-                            <calcop operator="get" field="parameters.quantity"/>
-                        </calcop>
-                    </calculate>
-                    <!-- If before this reservation the availableToPromiseTotal was over minimumStock, create a new requirement -->
-                    <if-compare-field field="oldAvailableToPromise" to-field="productFacility.minimumStock" operator="greater-equals" type="BigDecimal">
-                        <set from-field="inventoryItem.productId" field="inputMap.productId"/>
-                        <set from-field="productFacility.facilityId" field="inputMap.facilityId"/>
-                        <if-not-empty field="productFacility.reorderQuantity">
-                            <set from-field="productFacility.reorderQuantity" field="inputMap.quantity"/>
-                        <else>
-                            <set from-field="parameters.quantity" field="inputMap.quantity"/>
-                        </else>
-                        </if-not-empty>
-                        <set value="PRODUCT_REQUIREMENT" field="inputMap.requirementTypeId"/>
-                        <call-service service-name="createRequirement" in-map-name="inputMap">
-                            <result-to-field result-name="requirementId" field="parameters.requirementId"/>
-                        </call-service>
-                        <field-to-result field="parameters.requirementId" result-name="requirementId"/>
-                    </if-compare-field>
-                </if-compare-field>
-            </if-not-empty>
-        </if-compare>
-    </simple-method>
-
-    <simple-method method-name="checkCreateProductRequirementForFacility" short-description="Create Requirements for all the products in a facility with QOH under the minimum stock level">
-        <check-permission permission="ORDERMGR" action="_CREATE">
-            <fail-property resource="OrderErrorUiLabels" property="OrderSecurityErrorToRunCheckCreateStockRequirement"/>
-        </check-permission>
-        <check-errors/>
-
-        <entity-and entity-name="ProductFacility" list="products">
-            <field-map field-name="facilityId" from-field="parameters.facilityId"/>
-        </entity-and>
-
-        <iterate list="products" entry="productFacility">
-            <set from-field="productFacility.productId" field="parameters.productId"/>
-            <clear-field field="requirementMethodId"/>
-            <call-simple-method method-name="getProductRequirementMethod"/>
-            <if-empty field="requirementMethodId">
-                <set field="requirementMethodId" from-field="parameters.defaultRequirementMethodId"/>
-            </if-empty>
-            <if>
-                <condition>
-                    <or>
-                        <if-compare field="requirementMethodId" operator="equals" value="PRODRQM_STOCK"/>
-                        <if-compare field="requirementMethodId" operator="equals" value="PRODRQM_STOCK_ATP"/>
-                    </or>
-                </condition>
-                <then>
-
-                    <!-- get QOH, ATP and find ProductFacility which has the minimum stock -->
-
-                    <if-not-empty field="productFacility.minimumStock">
-                        <!-- Get the product's total quantityOnHand in the facility -->
-                        <clear-field field="inputMap"/>
-                        <set from-field="productFacility.productId" field="inputMap.productId"/>
-                        <set from-field="productFacility.facilityId" field="inputMap.facilityId"/>
-                        <call-service service-name="getInventoryAvailableByFacility" in-map-name="inputMap">
-                           <result-to-field result-name="quantityOnHandTotal" field="quantityOnHandTotal"/>
-                           <result-to-field result-name="availableToPromiseTotal" field="availableToPromiseTotal"/>
-                        </call-service>
-
-                        <if-compare field="requirementMethodId" operator="equals" value="PRODRQM_STOCK">
-                            <set from-field="quantityOnHandTotal" field="currentQuantity"/>
-                        <else>
-                            <set from-field="availableToPromiseTotal" field="currentQuantity"/>
-                        </else>
-                        </if-compare>
-
-                        <!-- No requirements are created if we are already under stock -->
-                        <if-compare-field field="currentQuantity" to-field="productFacility.minimumStock" operator="less" type="BigDecimal">
-                            <clear-field field="inputMap"/>
-                            <set from-field="productFacility.productId" field="inputMap.productId"/>
-                            <if-not-empty field="productFacility.reorderQuantity">
-                                <set from-field="productFacility.reorderQuantity" field="inputMap.quantity" type="BigDecimal"/>
-                            <else>
-                                <set value="0" field="inputMap.quantity" type="BigDecimal"/>
-                            </else>
-                            </if-not-empty>
-
-                            <calculate field="quantityShortfall">
-                                <calcop operator="subtract" field="productFacility.minimumStock">
-                                    <calcop operator="get" field="currentQuantity"/>
-                                </calcop>
-                            </calculate>
-                            <if-compare-field field="inputMap.quantity" to-field="quantityShortfall" operator="less" type="BigDecimal">
-                                <set from-field="quantityShortfall" field="inputMap.quantity" type="BigDecimal"/>
-                            </if-compare-field>
-
-                            <set value="PRODUCT_REQUIREMENT" field="inputMap.requirementTypeId"/>
-                            <set from-field="parameters.facilityId" field="inputMap.facilityId"/>
-                            <call-service service-name="createRequirement" in-map-name="inputMap">
-                                <result-to-field result-name="requirementId" field="requirementId"/>
-                            </call-service>
-                            <log level="info" message="Requirement creted with id [${requirementId}] for product with id [${productFacility.productId}]."/>
-                        </if-compare-field>
-                    </if-not-empty>
-                </then>
-            </if>
-        </iterate>
-    </simple-method>
-
     <simple-method method-name="orderSequence_enforced" short-description="Enforced Sequence (no gaps, per organization)">
         <log level="info" message="In getNextOrderId sequence enum Enforced"/> 
         <set field="partyAcctgPreference" from-field="parameters.partyAcctgPreference"/>
diff --git a/applications/order/servicedef/services_requirement.xml b/applications/order/servicedef/services_requirement.xml
index 58a100c5a9..e5706842d5 100644
--- a/applications/order/servicedef/services_requirement.xml
+++ b/applications/order/servicedef/services_requirement.xml
@@ -129,8 +129,8 @@ under the License.
         <attribute name="quantity" type="BigDecimal" mode="IN" optional="true"/>
         <attribute name="requirementId" type="String" mode="OUT" optional="true"/>
     </service>
-    <service name="checkCreateStockRequirementQoh" engine="simple" auth="true" default-entity-name="ItemIssuance"
-            location="component://order/minilang/order/OrderServices.xml" invoke="checkCreateStockRequirementQoh">
+    <service name="checkCreateStockRequirementQoh" engine="groovy" auth="true" default-entity-name="ItemIssuance"
+            location="component://order/src/main/groovy/org/apache/ofbiz/order/order/OrderRequirementServicesScript.groovy" invoke="checkCreateStockRequirementQoh">
         <description>Create a Product Requirement based on QOH inventory</description>
         <auto-attributes include="pk" mode="IN" optional="true"/>
         <auto-attributes include="nonpk" mode="IN" optional="true">
@@ -138,8 +138,8 @@ under the License.
         </auto-attributes>
         <attribute name="requirementId" type="String" mode="OUT" optional="true"/>
     </service>
-    <service name="checkCreateStockRequirementAtp" engine="simple" auth="true"
-            location="component://order/minilang/order/OrderServices.xml" invoke="checkCreateStockRequirementAtp">
+    <service name="checkCreateStockRequirementAtp" engine="groovy" auth="true"
+            location="component://order/src/main/groovy/org/apache/ofbiz/order/order/OrderRequirementServicesScript.groovy" invoke="checkCreateStockRequirementAtp">
         <description>Create a Product Requirement based on ATP inventory</description>
         <attribute name="orderId" type="String" mode="IN" optional="false"/>
         <attribute name="shipGroupSeqId" type="String" mode="IN" optional="false"/>
@@ -150,8 +150,8 @@ under the License.
         <attribute name="requirementId" type="String" mode="OUT" optional="true"/>
     </service>
 
-    <service name="checkCreateProductRequirementForFacility" engine="simple" auth="true"
-            location="component://order/minilang/order/OrderServices.xml" invoke="checkCreateProductRequirementForFacility">
+    <service name="checkCreateProductRequirementForFacility" engine="groovy" auth="true"
+            location="component://order/src/main/groovy/org/apache/ofbiz/order/order/OrderRequirementServicesScript.groovy" invoke="checkCreateProductRequirementForFacility">
         <description>Create Requirements for all the products in a facility with QOH under the minimum stock level</description>
         <attribute name="facilityId" type="String" mode="IN" optional="false"/>
         <attribute name="defaultRequirementMethodId" type="String" mode="IN" optional="true"/>
diff --git a/applications/order/src/main/groovy/org/apache/ofbiz/order/order/OrderRequirementServicesScript.groovy b/applications/order/src/main/groovy/org/apache/ofbiz/order/order/OrderRequirementServicesScript.groovy
index 3a74c55ff6..c546595706 100644
--- a/applications/order/src/main/groovy/org/apache/ofbiz/order/order/OrderRequirementServicesScript.groovy
+++ b/applications/order/src/main/groovy/org/apache/ofbiz/order/order/OrderRequirementServicesScript.groovy
@@ -18,7 +18,11 @@
 */
 package org.apache.ofbiz.order.order
 
+import org.apache.ofbiz.base.util.Debug
+import org.apache.ofbiz.base.util.UtilProperties
 import org.apache.ofbiz.entity.GenericValue
+import org.apache.ofbiz.entity.util.EntityTypeUtil
+import org.apache.ofbiz.service.ServiceUtil
 
 /*
  * Create OrderRequirementCommitment and Requirement for items with automatic requirement upon ordering
@@ -29,18 +33,18 @@ Map checkCreateOrderRequirement() {
     if (order.orderTypeId == 'SALES_ORDER' && reqMap.requirementMethodId == 'PRODRQM_AUTO') {
         createRequirementAndCommitment()
     }
-    success()
+    return success()
 }
 
-Map getProductRequirementMethod() {
+Map getProductRequirementMethod(String productId) {
     GenericValue order = from('OrderHeader').where(parameters).queryOne()
-    GenericValue product = from('Product').where(parameters).queryOne()
-    String requirementMethodId = product ? product.requirementMethodId : ''
+    GenericValue product = from('Product').where(productId: productId).queryOne()
+    String requirementMethodId = product ? product.requirementMethodEnumId : ''
     if (!requirementMethodId && product) {
         boolean isMarketingPkg = EntityTypeUtil.hasParentType(delegator, 'ProductType', 'productTypeId',
                                                               product.productTypeId, 'parentTypeId', 'MARKETING_PKG')
         if (!isMarketingPkg && product.productTypeId != 'DIGITAL_GOOD' && order) {
-            productStore = from('ProductStore').where(productStoreId: order.productStoreId).queryOne()
+            GenericValue productStore = from('ProductStore').where(productStoreId: order.productStoreId).queryOne()
             requirementMethodId = productStore ? productStore.requirementMethodEnumId : ''
         }
     }
@@ -69,3 +73,163 @@ Map createRequirementAndCommitment() {
     return returnMap
 }
 
+/*
+Stock Requirement
+*/
+
+Map checkCreateStockRequirementQoh() {
+    checkCreateStockRequirement('PRODRQM_STOCK')
+}
+
+Map checkCreateStockRequirementAtp() {
+    checkCreateStockRequirement('PRODRQM_STOCK_ATP')
+}
+
+Map checkCreateStockRequirement(String methodId) {
+    if (!(security.hasEntityPermission("ORDERMGR", "_CREATE", parameters.userLogin))) {
+        return error(UtilProperties.getMessage("OrderErrorUiLabels", "OrderSecurityErrorToRunCheckCreateStockRequirement", parameters.locale))
+    }
+    Map resultMap = success()
+
+    String requirementMethodId
+    String productId
+    String facilityId
+    GenericValue inventoryItem
+    BigDecimal quantity = parameters.quantity?:0
+
+    // If the service is triggered by the updateItemIssuance service, get the ItemIssuance by the passed itemIssuanceId
+    Map inventoryItemAndRequirementMethodId = getStockRequirementMethod()
+    requirementMethodId = inventoryItemAndRequirementMethodId.requirementMethodId
+    inventoryItem = inventoryItemAndRequirementMethodId.inventoryItem
+    if (inventoryItem) {
+        productId = inventoryItem.productId
+        facilityId = inventoryItem.facilityId
+    }
+
+    if (requirementMethodId && methodId == requirementMethodId && inventoryItem) {
+        Map result = getProductFacilityAndQuantities(productId, facilityId)
+        boolean createRequirement = false
+        if (result.productFacility) {
+            GenericValue productFacility = result.productFacility
+            BigDecimal currentQuantity = 0
+            BigDecimal minimumStock = productFacility.getBigDecimal('minimumStock')
+            switch (methodId) {
+                case 'PRODRQM_STOCK': //qoh
+                    //No requirements are created if we are already under stock
+                    BigDecimal quantityOnHandTotal = result.quantityOnHandTotal
+                    if (minimumStock && quantityOnHandTotal >= minimumStock) {
+                        BigDecimal newQuantityOnHand = quantityOnHandTotal.subtract(quantity)
+                        /*If this new issuance will cause the quantityOnHandTotal to go below the minimumStock,
+                        create a new requirement */
+                        if (newQuantityOnHand < productFacility.minimumStock) {
+                            currentQuantity = quantityOnHandTotal
+                            createRequirement = true
+                        }
+                    }
+                    break
+                case 'PRODRQM_STOCK_ATP': // atp
+                    /* No requirements are created if we are not under stock
+                    this service is supposed to be called after inventory is reserved,
+                    so inventory should have been updated already */
+                    BigDecimal availableToPromiseTotal = result.availableToPromiseTotal
+                    if (minimumStock && availableToPromiseTotal < minimumStock) {
+                        BigDecimal oldAvailableToPromiseTotal = availableToPromiseTotal.add(quantity)
+                        /* If before this reservation the availableToPromiseTotal was over minimumStock,
+                        create a new requirement*/
+                        if (oldAvailableToPromiseTotal >= minimumStock) {
+                            currentQuantity = availableToPromiseTotal
+                            createRequirement = true
+                        }
+                    }
+                    break
+            }
+            if (createRequirement) {
+                BigDecimal reqQuantity = productFacility.reorderQuantity ?:(quantity?:0)
+                Map inputMap = [
+                        productId        : productId,
+                        facilityId       : facilityId,
+                        quantity         : reqQuantity,
+                        requirementTypeId: 'PRODUCT_REQUIREMENT'
+                ]
+                result = run service: 'createRequirement', with: inputMap
+                if (ServiceUtil.isError(result)) {
+                    return result
+                }
+                resultMap.requirementId = result.requirementId
+            }
+        }
+    }
+    return resultMap
+}
+
+
+Map checkCreateProductRequirementForFacility() {
+    if (!(security.hasEntityPermission("ORDERMGR", "_CREATE", parameters.userLogin))) {
+        return error(UtilProperties.getMessage("OrderErrorUiLabels", "OrderSecurityErrorToRunCheckCreateStockRequirement", parameters.locale))
+    }
+    Map resultMap = success()
+
+    List<GenericValue> products = from('ProductFacility').where([facilityId: parameters.facilityId]).queryList()
+    for (GenericValue productFacility : products) {
+        String requirementMethodId = getProductRequirementMethod(productFacility.productId).requirementMethodId
+        if (!requirementMethodId) requirementMethodId = parameters.defaultRequirementMethodId
+        if (requirementMethodId) {
+            Map result = getProductFacilityAndQuantities(productFacility.productId, productFacility.facilityId)
+            boolean createRequirement = false
+            BigDecimal currentQuantity = 'PRODRQM_STOCK' == requirementMethodId?result.quantityOnHandTotal: result.availableToPromiseTotal
+            BigDecimal minimumStock = productFacility.getBigDecimal('minimumStock')
+            if (minimumStock && currentQuantity < minimumStock) {
+                BigDecimal reqQuantity = productFacility.reorderQuantity ?:0
+                BigDecimal quantityShortfall = minimumStock.subtract(currentQuantity)
+                if (reqQuantity < quantityShortfall) {
+                    reqQuantity = quantityShortfall
+                }
+                Map inputMap = [productId        : productFacility.productId,
+                                facilityId       : productFacility.facilityId,
+                                quantity         : reqQuantity,
+                                requirementTypeId: 'PRODUCT_REQUIREMENT'
+                    ]
+                result = run service: 'createRequirement', with: inputMap
+                if (ServiceUtil.isError(result)) {
+                    return result
+                }
+                Debug.logInfo("Requirement creted with id [${result.requirementId}] for product with id [${productFacility.productId}]", 'OrderRequirementServiceScript')
+            }
+        }
+    }
+    return resultMap
+}
+
+private Map getStockRequirementMethod() {
+    GenericValue inventoryItem = null
+    if (parameters.itemIssuanceId) {
+         GenericValue itemIssuance = from('ItemIssuance')
+            .where([itemIssuanceId: parameters.itemIssuanceId])
+            .queryOne()
+        inventoryItem = itemIssuance? itemIssuance.getRelatedOne('InventoryItem', true): null
+    } else {
+        inventoryItem = from('InventoryItem').where(parameters).queryOne()
+    }
+    if (!inventoryItem) return [requirementMethodId: null, inventoryItem: null]
+    String requirementMethodId = getProductRequirementMethod(inventoryItem.productId).requirementMethodId
+    return [requirementMethodId: requirementMethodId, inventoryItem: inventoryItem]
+}
+
+private Map getProductFacilityAndQuantities(String productId, String facilityId) {
+    // Get the ProductFacility for the minimum stock level
+    GenericValue productFacility = from('ProductFacility')
+            .where([productId : productId,
+                    facilityId: facilityId])
+            .queryOne()
+    // Get the product's total quantityOnHand in the facility
+    Map resultMap = run service: 'getInventoryAvailableByFacility', with: [
+            *         : parameters,
+            productId : productId,
+            facilityId: facilityId
+    ]
+    if (ServiceUtil.isError(resultMap)) {
+        return resultMap
+    }
+    resultMap.productFacility = productFacility
+    return resultMap
+}
diff --git a/applications/order/src/main/groovy/org/apache/ofbiz/order/order/test/OrderRequirementTests.groovy b/applications/order/src/main/groovy/org/apache/ofbiz/order/order/test/OrderRequirementTests.groovy
index 1c3c8d7e2e..e359ba0165 100644
--- a/applications/order/src/main/groovy/org/apache/ofbiz/order/order/test/OrderRequirementTests.groovy
+++ b/applications/order/src/main/groovy/org/apache/ofbiz/order/order/test/OrderRequirementTests.groovy
@@ -30,7 +30,7 @@ class OrderRequirementTests extends OFBizTestCase {
     void testCheckCreateProductRequirementForFacility() {
         Map serviceCtx = [
             facilityId: 'WebStoreWarehouse',
-            orderItemSeqId: '00001',
+            defaultRequirementMethodId: 'PRODRQM_STOCK',
             userLogin: userLogin
         ]
         Map serviceResult = dispatcher.runSync('checkCreateProductRequirementForFacility', serviceCtx)
@@ -40,6 +40,9 @@ class OrderRequirementTests extends OFBizTestCase {
         Map serviceCtx = [
             orderId: 'TEST_DEMO10090',
             orderItemSeqId: '00001',
+            shipGroupSeqId: '00001',
+            itemIssuanceId: '9006',
+            quantity: '300',
             userLogin: userLogin
         ]
         Map serviceResult = dispatcher.runSync('checkCreateStockRequirementQoh', serviceCtx)
@@ -47,9 +50,11 @@ class OrderRequirementTests extends OFBizTestCase {
     }
     void testCheckCreateStockRequirementAtp() {
         Map serviceCtx = [
-            orderId: 'TEST_DEMO10090',
+            orderId: 'TEST_DEMO10091',
             orderItemSeqId: '00001',
             shipGroupSeqId: '00001',
+            inventoryItemId: '9028',
+            quantity: '20',
             userLogin: userLogin
         ]
         Map serviceResult = dispatcher.runSync('checkCreateStockRequirementAtp', serviceCtx)
diff --git a/applications/order/testdef/data/OrderTestData.xml b/applications/order/testdef/data/OrderTestData.xml
index 8fe8bb4035..518c13dc01 100644
--- a/applications/order/testdef/data/OrderTestData.xml
+++ b/applications/order/testdef/data/OrderTestData.xml
@@ -26,13 +26,20 @@ under the License.
     <ProductCategory productCategoryId="CATALOG1_BEST_SELL" productCategoryTypeId="BEST_SELL_CATEGORY" categoryName="Demo Best Selling"/>
     <ProdCatalogCategory prodCatalogId="DemoCatalog" productCategoryId="CATALOG1_BEST_SELL" fromDate="2010-01-01 12:00:00.0" sequenceNum="1" prodCatalogCategoryTypeId="PCCT_BEST_SELL"/>
 
-    <ProductStore productStoreId="9000" storeName="OFBiz Ebay Store1-AU" companyName="Apache Ofbiz" title="Test OFBiz store1"/>
+    <ProductStore productStoreId="9000" storeName="OFBiz Ebay Store1-AU" companyName="Apache Ofbiz" title="Test OFBiz store1" requirementMethodEnumId="PRODRQM_STOCK"/>
     <ProductStoreEmailSetting productStoreId="9000" emailType="PRDS_ODR_CONFIRM" bodyScreenLocation="component://ecommerce/widget/EmailOrderScreens.xml#OrderConfirmNotice" xslfoAttachScreenLocation="component://ecommerce/widget/EmailOrderScreens.xml#OrderConfirmNoticePdf" subject="OFBiz Demo - Order Confirmation #${orderId}" bccAddress="ofbiztest@example.com" fromAddress="ofbiztest@example.com"/>
 
+    <ProductStore productStoreId="9000ATP" storeName="OFBiz Ebay Store2-AU" companyName="Apache Ofbiz" title="Test OFBiz store2 atp" requirementMethodEnumId="PRODRQM_STOCK_ATP"/>
+    <Facility facilityId="WebStoreWarehouseAtp" facilityTypeId="WAREHOUSE" facilityName="Web Store Warehouse ATP" defaultInventoryItemTypeId="NON_SERIAL_INV_ITEM"/>
+    <ProductFacility productId="GZ-2644" facilityId="WebStoreWarehouseAtp" minimumStock="10" reorderQuantity="20" daysToShip="15"/>
+
     <!--Demo data for a sales order -->
     <OrderHeader orderId="TEST_DEMO10090" orderTypeId="SALES_ORDER" salesChannelEnumId="WEB_SALES_CHANNEL" orderDate="2008-04-23 16:49:27.392" entryDate="2008-04-23 16:49:27.392" priority="2" visitId="10002" statusId="ORDER_APPROVED" currencyUom="USD" productStoreId="9000" remainingSubTotal="38.40" grandTotal="50.85"  invoicePerShipment="Y" webSiteId="WebStore"/>
     <OrderItem orderId="TEST_DEMO10090" orderItemSeqId="00001" orderItemTypeId="PRODUCT_ORDER_ITEM" productId="GZ-2644" prodCatalogId="DemoCatalog" isPromo="N" quantity="2.0" selectedAmount="0.0" unitPrice="38.4" unitListPrice="48.0" isModifiedPrice="N" itemDescription="Round Gizmo" statusId="ITEM_APPROVED"/>
 
+    <OrderHeader orderId="TEST_DEMO10091" orderTypeId="SALES_ORDER" salesChannelEnumId="WEB_SALES_CHANNEL" orderDate="2008-04-23 16:49:27.392" entryDate="2008-04-23 16:49:27.392" priority="2" visitId="10002" statusId="ORDER_APPROVED" currencyUom="USD" productStoreId="9000ATP" remainingSubTotal="38.40" grandTotal="50.85"  invoicePerShipment="Y" webSiteId="WebStore"/>
+    <OrderItem orderId="TEST_DEMO10091" orderItemSeqId="00001" orderItemTypeId="PRODUCT_ORDER_ITEM" productId="GZ-2644" prodCatalogId="DemoCatalog" isPromo="N" quantity="2.0" selectedAmount="0.0" unitPrice="38.4" unitListPrice="48.0" isModifiedPrice="N" itemDescription="Round Gizmo" statusId="ITEM_APPROVED"/>
+
     <Party partyId="TestDemoCustomer" partyTypeId="PERSON" statusId="PARTY_ENABLED"/>
     <Person partyId="TestDemoCustomer" firstName="Test" lastName="Customer"/>
     <PartyRole partyId="TestDemoCustomer" roleTypeId="CUSTOMER"/>
@@ -54,6 +61,9 @@ under the License.
     <OrderRole orderId="TEST_DEMO10090" partyId="TestDemoCustomer" roleTypeId="SHIP_TO_CUSTOMER"/>
     <OrderContactMech orderId="TEST_DEMO10090" contactMechPurposeTypeId="ORDER_EMAIL" contactMechId="TestContactMech"/>
     <WorkEffort workEffortId="9000" workEffortTypeId="PROJECT" currentStatusId="_NA_" lastStatusUpdate="2007-12-14 15:07:52.901" scopeEnumId="WES_PRIVATE" workEffortName="Demo Project1 Cust1" revisionNumber="1"/>
+    <ItemIssuance itemIssuanceId="9006" orderId="TEST_DEMO10090" orderItemSeqId="00001" shipGroupSeqId="00001" inventoryItemId="9001" quantity="2.000000"/>
+    <InventoryItem facilityId="WebStoreWarehouseAtp" inventoryItemId="9028" inventoryItemTypeId="NON_SERIAL_INV_ITEM" productId="GZ-2644" ownerPartyId="Company" currencyUomId="USD" unitCost="2.5"/>
+    <InventoryItemDetail inventoryItemId="9028" inventoryItemDetailSeqId="0001" effectiveDate="2001-05-13 12:00:00.0" availableToPromiseDiff="5" quantityOnHandDiff="5" accountingQuantityDiff="5"/>
     <Requirement requirementId="1000" requirementTypeId="CUSTOMER_REQUIREMENT"/>
     <ReturnHeader returnId="1009" statusId="RETURN_RECEIVED" returnHeaderTypeId="CUSTOMER_RETURN"/>
     <ReturnItem returnId="1009" returnItemSeqId="00001" returnReasonId="RTN_NOT_WANT" returnTypeId="RTN_REFUND" returnItemTypeId="RET_FPROD_ITEM" returnQuantity="1.000000"/>