svn commit: r601039 - in /ofbiz/trunk/applications/order: servicedef/secas.xml servicedef/services_requirement.xml src/org/ofbiz/order/requirement/RequirementServices.java

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

svn commit: r601039 - in /ofbiz/trunk/applications/order: servicedef/secas.xml servicedef/services_requirement.xml src/org/ofbiz/order/requirement/RequirementServices.java

sichen
Author: sichen
Date: Tue Dec  4 11:03:31 2007
New Revision: 601039

URL: http://svn.apache.org/viewvc?rev=601039&view=rev
Log:
Refactor ATP requirements based on minimum stock so that the service can be run on order status change rather than on inventory reservation.

Modified:
    ofbiz/trunk/applications/order/servicedef/secas.xml
    ofbiz/trunk/applications/order/servicedef/services_requirement.xml
    ofbiz/trunk/applications/order/src/org/ofbiz/order/requirement/RequirementServices.java

Modified: ofbiz/trunk/applications/order/servicedef/secas.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/order/servicedef/secas.xml?rev=601039&r1=601038&r2=601039&view=diff
==============================================================================
--- ofbiz/trunk/applications/order/servicedef/secas.xml (original)
+++ ofbiz/trunk/applications/order/servicedef/secas.xml Tue Dec  4 11:03:31 2007
@@ -282,15 +282,15 @@
     </eca>
     <eca service="reserveOrderItemInventory" event="commit">
         <condition field-name="quantity" value="0" operator="greater" type="Double"/>
-        <action service="createRequirementFromItemATP" mode="sync" run-as-user="system"/>
         <action service="checkCreateStockRequirementAtp" mode="sync" run-as-user="system"/>
     </eca>
-    <!-- create the automatic requirements for sales orders but only if the status changes from created to approved -->
+    <!-- create the automatic and ATP requirements for sales orders but only if the status changes from created to approved -->
     <eca service="changeOrderStatus" event="commit" run-on-error="false">
         <condition field-name="oldStatusId" operator="equals" value="ORDER_CREATED"/>
         <condition field-name="statusId" operator="equals" value="ORDER_APPROVED"/>
         <condition field-name="orderTypeId" operator="equals" value="SALES_ORDER"/>
         <action service="createAutoRequirementsForOrder" mode="sync"/>
+        <action service="createATPRequirementsForOrder" mode="sync"/>
     </eca>
 
     <!-- WorkEffort -->

Modified: ofbiz/trunk/applications/order/servicedef/services_requirement.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/order/servicedef/services_requirement.xml?rev=601039&r1=601038&r2=601039&view=diff
==============================================================================
--- ofbiz/trunk/applications/order/servicedef/services_requirement.xml (original)
+++ ofbiz/trunk/applications/order/servicedef/services_requirement.xml Tue Dec  4 11:03:31 2007
@@ -186,5 +186,14 @@
         </description>
         <attribute name="orderId" type="String" mode="IN" optional="false"/>
     </service>
+    <service name="createATPRequirementsForOrder" engine="java"
+            location="org.ofbiz.order.requirement.RequirementServices" invoke="createATPRequirementsForOrder" auth="true">
+        <description>
+            Creates requirements for any products with requirementMethodEnumId PRODRQM_ATP in the given sales order when
+            the ATP falls below or is below the minimum stock for the order facility.  ProductFacility.minimumStock must
+            be configured for requirements to be generated.  ProductFacility.reorderQuantity is not currently supported.
+        </description>
+        <attribute name="orderId" type="String" mode="IN" optional="false"/>
+    </service>
 </services>
 

Modified: ofbiz/trunk/applications/order/src/org/ofbiz/order/requirement/RequirementServices.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/order/src/org/ofbiz/order/requirement/RequirementServices.java?rev=601039&r1=601038&r2=601039&view=diff
==============================================================================
--- ofbiz/trunk/applications/order/src/org/ofbiz/order/requirement/RequirementServices.java (original)
+++ ofbiz/trunk/applications/order/src/org/ofbiz/order/requirement/RequirementServices.java Tue Dec  4 11:03:31 2007
@@ -232,5 +232,93 @@
         }
         return ServiceUtil.returnSuccess();
     }
+
+    // note that this service is designed to work only when a sales order status changes from CREATED -> APPROVED because HOLD -> APPROVED is too complex
+    public static Map createATPRequirementsForOrder(DispatchContext ctx, Map context) {
+        GenericDelegator delegator = ctx.getDelegator();
+        LocalDispatcher dispatcher = ctx.getDispatcher();
+        GenericValue userLogin = (GenericValue) context.get("userLogin");
+
+        /*
+         * The strategy in this service is to begin making requirements when the product falls below the
+         * ProductFacility.minimumStock.  Because the minimumStock is an upper bound, the quantity to be required
+         * is either that required to bring the ATP back up to the minimumStock level or the amount ordered,
+         * whichever is less.
+         *
+         * If there is a way to support reorderQuantity without losing the order item -> requirement association data,
+         * then this service should be updated.
+         *
+         * The result is that this service generates many small requirements when stock levels are low for a product,
+         * which is perfectly fine since the system is capable of creating POs in bulk from aggregate requirements.
+         * The only concern would be a UI to manage numerous requirements with ease, preferrably by aggregating
+         * on productId.
+         */
+        String orderId = (String) context.get("orderId");
+        try {
+            GenericValue order = delegator.findByPrimaryKey("OrderHeader", UtilMisc.toMap("orderId", orderId));
+            GenericValue productStore = order.getRelatedOneCache("ProductStore");
+            String facilityId = productStore.getString("inventoryFacilityId");
+            List orderItems = order.getRelated("OrderItem");
+            for (Iterator iter = orderItems.iterator(); iter.hasNext(); ) {
+                GenericValue item = (GenericValue) iter.next();
+                GenericValue product = item.getRelatedOne("Product");
+                if (product == null) continue;
+                if (! "PRODRQM_ATP".equals(product.get("requirementMethodEnumId"))) continue;
+
+                Double quantity = item.getDouble("quantity");
+                Double cancelQuantity = item.getDouble("cancelQuantity");
+                double ordered = quantity.doubleValue() - (cancelQuantity == null ? 0.0 : cancelQuantity.doubleValue());
+                if (ordered <= 0.0) continue;
+
+                // get the minimum stock for this facility (don't do anything if not configured)
+                GenericValue productFacility = delegator.findByPrimaryKey("ProductFacility", UtilMisc.toMap("facilityId", facilityId, "productId", product.get("productId")));
+                if (productFacility == null || productFacility.get("minimumStock") == null) continue;
+                double minimumStock = productFacility.getDouble("minimumStock").doubleValue();
+
+                // get the facility ATP for product, which should be updated for this item's reservation
+                Map results = dispatcher.runSync("getInventoryAvailableByFacility", UtilMisc.toMap("userLogin", userLogin, "productId", product.get("productId"), "facilityId", facilityId));
+                if (ServiceUtil.isError(results)) return results;
+                double atp = ((Double) results.get("availableToPromiseTotal")).doubleValue(); // safe since this is a required OUT param
+
+                // the minimum stock is an upper bound, therefore we either require up to the minimum stock or the input required quantity, whichever is less
+                double shortfall = minimumStock - atp;
+                double required = Math.min(ordered, shortfall);
+                if (required <= 0.0) continue;
+
+                // count all current requirements for this product
+                double requirementQty = 0.0;
+                List conditions = UtilMisc.toList(
+                        new EntityExpr("facilityId", EntityOperator.EQUALS, facilityId),
+                        new EntityExpr("productId", EntityOperator.EQUALS, product.get("productId")),
+                        new EntityExpr("requirementTypeId", EntityOperator.EQUALS, "PRODUCT_REQUIREMENT"),
+                        new EntityExpr("statusId", EntityOperator.NOT_EQUAL, "REQ_ORDERED"),
+                        new EntityExpr("statusId", EntityOperator.NOT_EQUAL, "REQ_REJECTED")
+                );
+                List requirements = delegator.findByAnd("Requirement", conditions);
+                for (Iterator riter = requirements.iterator(); riter.hasNext(); ) {
+                    GenericValue requirement = (GenericValue) riter.next();
+                    requirementQty += (requirement.get("quantity") == null ? 0.0 : requirement.getDouble("quantity").doubleValue());
+                }
+
+                // if we the existing requirements are not enough, then create a new requirement for the difference
+                required -= requirementQty;
+                if (required <= 0.0) continue;
+
+                Map input = UtilMisc.toMap("userLogin", userLogin, "facilityId", facilityId, "productId", product.get("productId"), "quantity", new Double(required), "requirementTypeId", "PRODUCT_REQUIREMENT");
+                results = dispatcher.runSync("createRequirement", input);
+                if (ServiceUtil.isError(results)) return results;
+                String requirementId = (String) results.get("requirementId");
+
+                input = UtilMisc.toMap("userLogin", userLogin, "orderId", order.get("orderId"), "orderItemSeqId", item.get("orderItemSeqId"), "requirementId", requirementId, "quantity", new Double(required));
+                results = dispatcher.runSync("createOrderRequirementCommitment", input);
+                if (ServiceUtil.isError(results)) return results;
+            }
+        } catch (GenericEntityException e) {
+            Debug.logError(e, module);
+        } catch (GenericServiceException e) {
+            Debug.logError(e, module);
+        }
+        return ServiceUtil.returnSuccess();
+    }
 }