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(); + } } |
Free forum by Nabble | Edit this page |