This is an automated email from the ASF dual-hosted git repository. diveshdut pushed a commit to branch trunk in repository https://gitbox.apache.org/repos/asf/ofbiz-framework.git
commit cff2b47bb22effc2dea8963e4d67eb76c2b18664 Author: diveshdut <[email protected]> AuthorDate: Fri Jul 24 21:02:39 2026 +0530 OFBIZ-13460: Add durable MRP run history and provenance --- .../entitydef/manufacturing-entitymodel.xml | 33 ++ .../manufacturing/servicedef/services_mrp.xml | 12 + .../ofbiz/manufacturing/mrp/MrpServices.java | 331 +++++++++++++++++---- 3 files changed, 323 insertions(+), 53 deletions(-) diff --git a/applications/datamodel/entitydef/manufacturing-entitymodel.xml b/applications/datamodel/entitydef/manufacturing-entitymodel.xml index f03a1e2ce9..d82722cc44 100644 --- a/applications/datamodel/entitydef/manufacturing-entitymodel.xml +++ b/applications/datamodel/entitydef/manufacturing-entitymodel.xml @@ -163,6 +163,39 @@ under the License. <prim-key field="mrpEventTypeId"/> </entity> + <entity entity-name="MrpRunLog" + package-name="org.apache.ofbiz.manufacturing.mrp" + title="MRP Run Log"> + <description>Durable run-level provenance for MRP executions.</description> + <field name="mrpRunLogId" type="id"></field> + <field name="mrpId" type="id"></field> + <field name="jobId" type="id"></field> + <field name="mrpName" type="name"></field> + <field name="facilityId" type="id"></field> + <field name="facilityGroupId" type="id"></field> + <field name="defaultYearsOffset" type="numeric"></field> + <field name="runByUserLoginId" type="id-vlong"></field> + <field name="runStatusId" type="id"></field> + <field name="outcomeMessage" type="very-long"></field> + <field name="mrpEventCount" type="numeric"></field> + <field name="proposedRequirementCount" type="numeric"></field> + <field name="failureReason" type="name"></field> + <field name="failureMessage" type="very-long"></field> + <field name="startedAt" type="date-time"></field> + <field name="finishedAt" type="date-time"></field> + <field name="durationMillis" type="numeric"></field> + <prim-key field="mrpRunLogId"/> + <relation type="one" fk-name="MRPRUN_FAC" rel-entity-name="Facility"> + <key-map field-name="facilityId"/> + </relation> + <relation type="one" fk-name="MRPRUN_STTS" rel-entity-name="StatusItem"> + <key-map field-name="runStatusId" rel-field-name="statusId"/> + </relation> + <relation type="one" fk-name="MRPRUN_USER" rel-entity-name="UserLogin"> + <key-map field-name="runByUserLoginId" rel-field-name="userLoginId"/> + </relation> + </entity> + <entity entity-name="MrpEvent" package-name="org.apache.ofbiz.manufacturing.mrp" title="MRP Event"> diff --git a/applications/manufacturing/servicedef/services_mrp.xml b/applications/manufacturing/servicedef/services_mrp.xml index d1df909a4c..e8e3251f61 100644 --- a/applications/manufacturing/servicedef/services_mrp.xml +++ b/applications/manufacturing/servicedef/services_mrp.xml @@ -33,8 +33,20 @@ under the License. <attribute name="facilityId" type="String" mode="IN" optional="true"/> <attribute name="mrpName" type="String" mode="IN" optional="true"/> <attribute name="defaultYearsOffset" type="Integer" mode="IN" optional="true"/> + <attribute name="mrpRunLogId" type="String" mode="IN" optional="true"/> + <attribute name="jobTrackerId" type="String" mode="IN" optional="true"/> <attribute name="msgResult" type="List" mode="OUT" optional="false"/> </service> + <service name="createMrpRunLog" default-entity-name="MrpRunLog" engine="entity-auto" invoke="create" auth="true"> + <description>Create durable metadata for an MRP execution.</description> + <auto-attributes include="pk" mode="INOUT" optional="false"/> + <auto-attributes include="nonpk" mode="IN" optional="true"/> + </service> + <service name="updateMrpRunLog" default-entity-name="MrpRunLog" engine="entity-auto" invoke="update" auth="true"> + <description>Update durable metadata for an MRP execution.</description> + <auto-attributes include="pk" mode="IN" optional="false"/> + <auto-attributes include="nonpk" mode="IN" optional="true"/> + </service> <service name="initMrpEvents" engine="java" location="org.apache.ofbiz.manufacturing.mrp.MrpServices" invoke="initMrpEvents" auth="true"> <description>Initialize data for the MRP</description> diff --git a/applications/manufacturing/src/main/java/org/apache/ofbiz/manufacturing/mrp/MrpServices.java b/applications/manufacturing/src/main/java/org/apache/ofbiz/manufacturing/mrp/MrpServices.java index 6c0549bde1..33e6f2ffcc 100644 --- a/applications/manufacturing/src/main/java/org/apache/ofbiz/manufacturing/mrp/MrpServices.java +++ b/applications/manufacturing/src/main/java/org/apache/ofbiz/manufacturing/mrp/MrpServices.java @@ -675,6 +675,177 @@ public class MrpServices { } } + private static String mrpRunLogIdFromContext(Delegator delegator, Map<String, ? extends Object> context) { + String mrpRunLogId = (String) context.get("mrpRunLogId"); + if (UtilValidate.isNotEmpty(mrpRunLogId)) { + return mrpRunLogId; + } + mrpRunLogId = (String) context.get("jobTrackerId"); + return UtilValidate.isNotEmpty(mrpRunLogId) ? mrpRunLogId : delegator.getNextSeqId("MrpRunLog"); + } + + private static String startMrpRunLog(DispatchContext ctx, Map<String, ? extends Object> context, GenericValue userLogin, String mrpRunLogId, + Timestamp startedAt) { + if (UtilValidate.isEmpty(mrpRunLogId)) { + return null; + } + LocalDispatcher dispatcher = ctx.getDispatcher(); + Delegator delegator = ctx.getDelegator(); + String userLoginId = userLogin != null ? userLogin.getString("userLoginId") : null; + try { + GenericValue existingMrpRunLog = EntityQuery.use(delegator).from("MrpRunLog").where("mrpRunLogId", mrpRunLogId).queryOne(); + if (existingMrpRunLog == null) { + Map<String, Object> createParameters = UtilMisc.toMap("mrpRunLogId", mrpRunLogId, "runStatusId", "SERVICE_RUNNING", + "startedAt", startedAt, "userLogin", userLogin); + if (UtilValidate.isNotEmpty((String) context.get("mrpName"))) { + createParameters.put("mrpName", context.get("mrpName")); + } + if (UtilValidate.isNotEmpty((String) context.get("facilityId"))) { + createParameters.put("facilityId", context.get("facilityId")); + } + if (UtilValidate.isNotEmpty((String) context.get("facilityGroupId"))) { + createParameters.put("facilityGroupId", context.get("facilityGroupId")); + } + if (context.get("defaultYearsOffset") != null) { + createParameters.put("defaultYearsOffset", context.get("defaultYearsOffset")); + } + if (UtilValidate.isNotEmpty((String) context.get("jobId"))) { + createParameters.put("jobId", context.get("jobId")); + } + if (UtilValidate.isNotEmpty(userLoginId)) { + createParameters.put("runByUserLoginId", userLoginId); + } + Map<String, Object> createResponse = dispatcher.runSync("createMrpRunLog", createParameters, 60, true); + if (ServiceUtil.isError(createResponse)) { + Debug.logWarning("Unable to create MrpRunLog [" + mrpRunLogId + "]: " + ServiceUtil.getErrorMessage(createResponse), MODULE); + } else { + return mrpRunLogId; + } + } + } catch (GenericEntityException e) { + Debug.logWarning(e, "Unable to query MrpRunLog [" + mrpRunLogId + "] before starting", MODULE); + } catch (GenericServiceException e) { + Debug.logWarning(e, "Unable to call createMrpRunLog for [" + mrpRunLogId + "]", MODULE); + } + updateMrpRunLog(dispatcher, UtilMisc.toMap("mrpRunLogId", mrpRunLogId, "runStatusId", "SERVICE_RUNNING", + "startedAt", startedAt, "userLogin", userLogin), "mark as running", mrpRunLogId); + return mrpRunLogId; + } + + private static String failureReasonFromResult(Map<String, Object> result) { + return ServiceUtil.isError(result) ? "SERVICE_ERROR" : null; + } + + private static String failureReasonFromException(RuntimeException e) { + return "RUNTIME_EXCEPTION"; + } + + private static String cleanFailureMessage(Map<String, Object> result) { + return conciseFailureMessage(ServiceUtil.getErrorMessage(result)); + } + + private static String cleanFailureMessage(RuntimeException e) { + String errorMessage = e.getMessage(); + return conciseFailureMessage(UtilValidate.isNotEmpty(errorMessage) ? errorMessage : e.toString()); + } + + private static String conciseFailureMessage(String errorMessage) { + if (UtilValidate.isEmpty(errorMessage)) { + return null; + } + String cleanedMessage = errorMessage.replaceAll("\\s+", " ").trim(); + // Keep only the user-facing failure summary in MrpRunLog, not serialized debug payloads such as "{key=value}". + cleanedMessage = stripTrailingSerializedMap(cleanedMessage); + return UtilValidate.isNotEmpty(cleanedMessage) ? cleanedMessage : null; + } + + private static String stripTrailingSerializedMap(String errorMessage) { + int mapStart = errorMessage.lastIndexOf('{'); + if (mapStart < 0 || !errorMessage.endsWith("}")) { + return errorMessage; + } + String trailingValue = errorMessage.substring(mapStart); + if (!trailingValue.contains("=")) { + return errorMessage; + } + String prefix = errorMessage.substring(0, mapStart).trim(); + while (!prefix.isEmpty() && ",;:".indexOf(prefix.charAt(prefix.length() - 1)) >= 0) { + prefix = prefix.substring(0, prefix.length() - 1).trim(); + } + return prefix; + } + + private static Long countMrpEventsForRun(Delegator delegator, String mrpId) { + if (UtilValidate.isEmpty(mrpId)) { + return null; + } + try { + return EntityQuery.use(delegator).from("MrpEvent").where("mrpId", mrpId).queryCount(); + } catch (GenericEntityException e) { + Debug.logWarning(e, "Unable to count MrpEvent rows for mrpId [" + mrpId + "]", MODULE); + return null; + } + } + + private static String buildOutcomeMessage(Long mrpEventCount, long proposedRequirementCount) { + if (mrpEventCount == null) { + return null; + } + return "Created " + mrpEventCount + " MRP events and " + proposedRequirementCount + " proposed requirements"; + } + + private static Map<String, Object> finishMrpRunLog(LocalDispatcher dispatcher, GenericValue userLogin, String mrpRunLogId, Timestamp startedAt, + String mrpId, String failureReason, String failureMessage, Long mrpEventCount, + Long proposedRequirementCount, String outcomeMessage, Map<String, Object> result) { + if (UtilValidate.isEmpty(mrpRunLogId)) { + return result; + } + Timestamp finishedAt = UtilDateTime.nowTimestamp(); + String statusId = ServiceUtil.isError(result) ? "SERVICE_FAILED" : "SERVICE_FINISHED"; + try { + Map<String, Object> parameters = UtilMisc.toMap("mrpRunLogId", mrpRunLogId, "runStatusId", statusId, "startedAt", startedAt, + "finishedAt", finishedAt, "userLogin", userLogin); + if (UtilValidate.isNotEmpty(mrpId)) { + parameters.put("mrpId", mrpId); + } + if (UtilValidate.isNotEmpty(failureReason)) { + parameters.put("failureReason", failureReason); + } + if (UtilValidate.isNotEmpty(failureMessage)) { + parameters.put("failureMessage", failureMessage); + } + if (!ServiceUtil.isError(result)) { + parameters.put("failureReason", null); + parameters.put("failureMessage", null); + parameters.put("mrpEventCount", mrpEventCount); + parameters.put("proposedRequirementCount", proposedRequirementCount); + parameters.put("outcomeMessage", outcomeMessage); + } else { + parameters.put("mrpEventCount", null); + parameters.put("proposedRequirementCount", null); + parameters.put("outcomeMessage", outcomeMessage); + } + if (startedAt != null && finishedAt != null) { + parameters.put("durationMillis", finishedAt.getTime() - startedAt.getTime()); + } + updateMrpRunLog(dispatcher, parameters, "finish", mrpRunLogId); + } catch (RuntimeException e) { + Debug.logWarning(e, "Unable to prepare MrpRunLog finish parameters for [" + mrpRunLogId + "]", MODULE); + } + return result; + } + + private static void updateMrpRunLog(LocalDispatcher dispatcher, Map<String, Object> parameters, String action, String mrpRunLogId) { + try { + Map<String, Object> response = dispatcher.runSync("updateMrpRunLog", parameters, 60, true); + if (ServiceUtil.isError(response)) { + Debug.logWarning("Unable to " + action + " MrpRunLog [" + mrpRunLogId + "]: " + ServiceUtil.getErrorMessage(response), MODULE); + } + } catch (GenericServiceException e) { + Debug.logWarning(e, "Unable to call updateMrpRunLog to " + action + " [" + mrpRunLogId + "]", MODULE); + } + } + /** * Launch the MRP. * <ul> @@ -693,48 +864,64 @@ public class MrpServices { LocalDispatcher dispatcher = ctx.getDispatcher(); GenericValue userLogin = (GenericValue) context.get("userLogin"); Timestamp now = UtilDateTime.nowTimestamp(); + String mrpRunLogId = mrpRunLogIdFromContext(delegator, context); + mrpRunLogId = startMrpRunLog(ctx, context, userLogin, mrpRunLogId, now); Locale locale = (Locale) context.get("locale"); String mrpName = (String) context.get("mrpName"); Integer defaultYearsOffset = (Integer) context.get("defaultYearsOffset"); String facilityGroupId = (String) context.get("facilityGroupId"); String facilityId = (String) context.get("facilityId"); String manufacturingFacilityId = null; - if (UtilValidate.isEmpty(facilityId) && UtilValidate.isEmpty(facilityGroupId)) { - return ServiceUtil.returnError(UtilProperties.getMessage(RESOURCE, "ManufacturingMrpFacilityNotAvailable", locale)); - } - if (UtilValidate.isEmpty(facilityId)) { - try { - GenericValue facilityGroup = EntityQuery.use(delegator).from("FacilityGroup").where("facilityGroupId", facilityGroupId).queryOne(); - if (UtilValidate.isEmpty(facilityGroup)) { - return ServiceUtil.returnError(UtilProperties.getMessage(RESOURCE, "ManufacturingMrpFacilityGroupIsNotValid", UtilMisc.toMap( - "facilityGroupId", facilityGroupId), locale)); - } - List<GenericValue> facilities = facilityGroup.getRelated("FacilityGroupMember", null, UtilMisc.toList("sequenceNum"), false); - if (UtilValidate.isEmpty(facilities)) { - return ServiceUtil.returnError(UtilProperties.getMessage(RESOURCE, "ManufacturingMrpFacilityGroupIsNotAssociatedToFacility", - UtilMisc.toMap("facilityGroupId", facilityGroupId), locale)); - } - for (GenericValue facilityMember : facilities) { - GenericValue facility = facilityMember.getRelatedOne("Facility", false); - if ("WAREHOUSE".equals(facility.getString("facilityTypeId")) && UtilValidate.isEmpty(facilityId)) { - facilityId = facility.getString("facilityId"); + String mrpId = null; + long proposedRequirementCount = 0; + try { + if (UtilValidate.isEmpty(facilityId) && UtilValidate.isEmpty(facilityGroupId)) { + Map<String, Object> errorResult = ServiceUtil.returnError(UtilProperties.getMessage(RESOURCE, "ManufacturingMrpFacilityNotAvailable", + locale)); + return finishMrpRunLog(dispatcher, userLogin, mrpRunLogId, now, mrpId, failureReasonFromResult(errorResult), + cleanFailureMessage(errorResult), null, null, null, errorResult); + } + if (UtilValidate.isEmpty(facilityId)) { + try { + GenericValue facilityGroup = EntityQuery.use(delegator).from("FacilityGroup").where("facilityGroupId", facilityGroupId).queryOne(); + if (UtilValidate.isEmpty(facilityGroup)) { + Map<String, Object> errorResult = ServiceUtil.returnError(UtilProperties.getMessage(RESOURCE, + "ManufacturingMrpFacilityGroupIsNotValid", UtilMisc.toMap("facilityGroupId", facilityGroupId), locale)); + return finishMrpRunLog(dispatcher, userLogin, mrpRunLogId, now, mrpId, failureReasonFromResult(errorResult), + cleanFailureMessage(errorResult), null, null, null, errorResult); } - if ("PLANT".equals(facility.getString("facilityTypeId")) && UtilValidate.isEmpty(manufacturingFacilityId)) { - manufacturingFacilityId = facility.getString("facilityId"); + List<GenericValue> facilities = facilityGroup.getRelated("FacilityGroupMember", null, UtilMisc.toList("sequenceNum"), false); + if (UtilValidate.isEmpty(facilities)) { + Map<String, Object> errorResult = ServiceUtil.returnError(UtilProperties.getMessage(RESOURCE, + "ManufacturingMrpFacilityGroupIsNotAssociatedToFacility", UtilMisc.toMap("facilityGroupId", facilityGroupId), locale)); + return finishMrpRunLog(dispatcher, userLogin, mrpRunLogId, now, mrpId, failureReasonFromResult(errorResult), + cleanFailureMessage(errorResult), null, null, null, errorResult); } + for (GenericValue facilityMember : facilities) { + GenericValue facility = facilityMember.getRelatedOne("Facility", false); + if ("WAREHOUSE".equals(facility.getString("facilityTypeId")) && UtilValidate.isEmpty(facilityId)) { + facilityId = facility.getString("facilityId"); + } + if ("PLANT".equals(facility.getString("facilityTypeId")) && UtilValidate.isEmpty(manufacturingFacilityId)) { + manufacturingFacilityId = facility.getString("facilityId"); + } + } + } catch (GenericEntityException e) { + Map<String, Object> errorResult = ServiceUtil.returnError(UtilProperties.getMessage(RESOURCE, + "ManufacturingMrpFacilityGroupCannotBeLoad", UtilMisc.toMap("errorString", e.getMessage()), locale)); + return finishMrpRunLog(dispatcher, userLogin, mrpRunLogId, now, mrpId, failureReasonFromResult(errorResult), + cleanFailureMessage(errorResult), null, null, null, errorResult); } - } catch (GenericEntityException e) { - return ServiceUtil.returnError(UtilProperties.getMessage(RESOURCE, "ManufacturingMrpFacilityGroupCannotBeLoad", UtilMisc.toMap( - "errorString", e.getMessage()), locale)); + } else { + manufacturingFacilityId = facilityId; } - } else { - manufacturingFacilityId = facilityId; - } - if (UtilValidate.isEmpty(facilityId) || UtilValidate.isEmpty(manufacturingFacilityId)) { - return ServiceUtil.returnError(UtilProperties.getMessage(RESOURCE, "ManufacturingMrpFacilityOrManufacturingFacilityNotAvailable", - locale)); - } + if (UtilValidate.isEmpty(facilityId) || UtilValidate.isEmpty(manufacturingFacilityId)) { + Map<String, Object> errorResult = ServiceUtil.returnError(UtilProperties.getMessage(RESOURCE, + "ManufacturingMrpFacilityOrManufacturingFacilityNotAvailable", locale)); + return finishMrpRunLog(dispatcher, userLogin, mrpRunLogId, now, mrpId, failureReasonFromResult(errorResult), + cleanFailureMessage(errorResult), null, null, null, errorResult); + } int bomLevelWithNoEvent = 0; BigDecimal stockTmp = BigDecimal.ZERO; @@ -750,7 +937,7 @@ public class MrpServices { boolean isBuilt = false; GenericValue routing = null; - String mrpId = delegator.getNextSeqId("MrpEvent"); + mrpId = delegator.getNextSeqId("MrpEvent"); Map<String, Object> result = null; Map<String, Object> parameters = null; @@ -765,11 +952,15 @@ public class MrpServices { try { result = dispatcher.runSync("initMrpEvents", parameters); if (ServiceUtil.isError(result)) { - return ServiceUtil.returnError(ServiceUtil.getErrorMessage(result)); + Map<String, Object> errorResult = ServiceUtil.returnError(ServiceUtil.getErrorMessage(result)); + return finishMrpRunLog(dispatcher, userLogin, mrpRunLogId, now, mrpId, failureReasonFromResult(errorResult), + cleanFailureMessage(errorResult), null, null, null, errorResult); } } catch (GenericServiceException e) { - return ServiceUtil.returnError(UtilProperties.getMessage(RESOURCE, "ManufacturingMrpErrorRunningInitMrpEvents", UtilMisc.toMap( - "errorString", e.getMessage()), locale)); + Map<String, Object> errorResult = ServiceUtil.returnError(UtilProperties.getMessage(RESOURCE, + "ManufacturingMrpErrorRunningInitMrpEvents", UtilMisc.toMap("errorString", e.getMessage()), locale)); + return finishMrpRunLog(dispatcher, userLogin, mrpRunLogId, now, mrpId, failureReasonFromResult(errorResult), + cleanFailureMessage(errorResult), null, null, null, errorResult); } long bomLevel = 0; do { @@ -789,8 +980,10 @@ public class MrpServices { .queryList(); } catch (GenericEntityException e) { Long bomLevelToString = bomLevel; - return ServiceUtil.returnError(UtilProperties.getMessage(RESOURCE, "ManufacturingMrpErrorForBomLevel", UtilMisc.toMap("bomLevel", - bomLevelToString.toString(), "errorString", e.getMessage()), locale)); + Map<String, Object> errorResult = ServiceUtil.returnError(UtilProperties.getMessage(RESOURCE, "ManufacturingMrpErrorForBomLevel", + UtilMisc.toMap("bomLevel", bomLevelToString.toString(), "errorString", e.getMessage()), locale)); + return finishMrpRunLog(dispatcher, userLogin, mrpRunLogId, now, mrpId, failureReasonFromResult(errorResult), + cleanFailureMessage(errorResult), null, null, null, errorResult); } if (UtilValidate.isNotEmpty(listInventoryEventForMRP)) { @@ -814,7 +1007,10 @@ public class MrpServices { productFacility = EntityUtil.getFirst(product.getRelated("ProductFacility", UtilMisc.toMap("facilityId", facilityId), null, true)); } catch (GenericEntityException e) { - return ServiceUtil.returnError(UtilProperties.getMessage(RESOURCE, "ManufacturingMrpCannotFindProductForEvent", locale)); + Map<String, Object> errorResult = ServiceUtil.returnError(UtilProperties.getMessage(RESOURCE, + "ManufacturingMrpCannotFindProductForEvent", locale)); + return finishMrpRunLog(dispatcher, userLogin, mrpRunLogId, now, mrpId, failureReasonFromResult(errorResult), + cleanFailureMessage(errorResult), null, null, null, errorResult); } stockTmp = findProductMrpQoh(mrpId, product, facilityId, dispatcher, delegator); try { @@ -823,8 +1019,10 @@ public class MrpServices { "mrpEventTypeId", "INITIAL_QOH", "eventDate", now), stockTmp, facilityId, null, false, delegator); } catch (GenericEntityException e) { - return ServiceUtil.returnError(UtilProperties.getMessage(RESOURCE, "ManufacturingMrpCreateOrUpdateEvent", - UtilMisc.toMap("parameters", parameters), locale)); + Map<String, Object> errorResult = ServiceUtil.returnError(UtilProperties.getMessage(RESOURCE, + "ManufacturingMrpCreateOrUpdateEvent", UtilMisc.toMap("parameters", parameters), locale)); + return finishMrpRunLog(dispatcher, userLogin, mrpRunLogId, now, mrpId, failureReasonFromResult(errorResult), + cleanFailureMessage(errorResult), null, null, null, errorResult); } // days to ship is only relevant for sales order to plan for preparatory days to ship. Otherwise MRP will push event dates // for manufacturing parts @@ -846,11 +1044,15 @@ public class MrpServices { product.getString("productId"), "quantity", positiveEventQuantity, "excludeWIPs", Boolean.FALSE, "userLogin", userLogin)); if (ServiceUtil.isError(serviceResponse)) { - return ServiceUtil.returnError(ServiceUtil.getErrorMessage(serviceResponse)); + Map<String, Object> errorResult = ServiceUtil.returnError(ServiceUtil.getErrorMessage(serviceResponse)); + return finishMrpRunLog(dispatcher, userLogin, mrpRunLogId, now, mrpId, failureReasonFromResult(errorResult), + cleanFailureMessage(errorResult), null, null, null, errorResult); } } catch (GenericServiceException e) { - return ServiceUtil.returnError(UtilProperties.getMessage(RESOURCE, "ManufacturingMrpErrorExplodingProduct", - UtilMisc.toMap("productId", product.getString("productId")), locale)); + Map<String, Object> errorResult = ServiceUtil.returnError(UtilProperties.getMessage(RESOURCE, + "ManufacturingMrpErrorExplodingProduct", UtilMisc.toMap("productId", product.getString("productId")), locale)); + return finishMrpRunLog(dispatcher, userLogin, mrpRunLogId, now, mrpId, failureReasonFromResult(errorResult), + cleanFailureMessage(errorResult), null, null, null, errorResult); } components = UtilGenerics.cast(serviceResponse.get("components")); if (UtilValidate.isNotEmpty(components)) { @@ -885,11 +1087,15 @@ public class MrpServices { product.getString("productId"), "quantity", proposedOrder.getQuantity(), "excludeWIPs", Boolean.FALSE, "userLogin", userLogin)); if (ServiceUtil.isError(serviceResponse)) { - return ServiceUtil.returnError(ServiceUtil.getErrorMessage(serviceResponse)); + Map<String, Object> errorResult = ServiceUtil.returnError(ServiceUtil.getErrorMessage(serviceResponse)); + return finishMrpRunLog(dispatcher, userLogin, mrpRunLogId, now, mrpId, failureReasonFromResult(errorResult), + cleanFailureMessage(errorResult), null, null, null, errorResult); } } catch (GenericServiceException e) { - return ServiceUtil.returnError(UtilProperties.getMessage(RESOURCE, "ManufacturingMrpErrorExplodingProduct", - UtilMisc.toMap("productId", product.getString("productId")), locale)); + Map<String, Object> errorResult = ServiceUtil.returnError(UtilProperties.getMessage(RESOURCE, + "ManufacturingMrpErrorExplodingProduct", UtilMisc.toMap("productId", product.getString("productId")), locale)); + return finishMrpRunLog(dispatcher, userLogin, mrpRunLogId, now, mrpId, failureReasonFromResult(errorResult), + cleanFailureMessage(errorResult), null, null, null, errorResult); } components = UtilGenerics.cast(serviceResponse.get("components")); String routingId = (String) serviceResponse.get("workEffortId"); @@ -897,8 +1103,10 @@ public class MrpServices { try { routing = EntityQuery.use(delegator).from("WorkEffort").where("workEffortId", routingId).queryOne(); } catch (GenericEntityException e) { - return ServiceUtil.returnError(UtilProperties.getMessage(RESOURCE, "ManufacturingMrpCannotFindProductForEvent", - locale)); + Map<String, Object> errorResult = ServiceUtil.returnError(UtilProperties.getMessage(RESOURCE, + "ManufacturingMrpCannotFindProductForEvent", locale)); + return finishMrpRunLog(dispatcher, userLogin, mrpRunLogId, now, mrpId, failureReasonFromResult(errorResult), + cleanFailureMessage(errorResult), null, null, null, errorResult); } } else { routing = null; @@ -922,8 +1130,10 @@ public class MrpServices { try { supplierLeadTimeDays = getSupplierProductLeadTimeDays(delegator, product.getString("productId"), eventDate); } catch (GenericEntityException e) { - return ServiceUtil.returnError(UtilProperties.getMessage(RESOURCE, "ManufacturingMrpCannotFindProductForEvent", - locale)); + Map<String, Object> errorResult = ServiceUtil.returnError(UtilProperties.getMessage(RESOURCE, + "ManufacturingMrpCannotFindProductForEvent", locale)); + return finishMrpRunLog(dispatcher, userLogin, mrpRunLogId, now, mrpId, failureReasonFromResult(errorResult), + cleanFailureMessage(errorResult), null, null, null, errorResult); } } @@ -940,6 +1150,9 @@ public class MrpServices { if (productFacility != null) { requirementId = proposedOrder.create(ctx, userLogin); } + if (UtilValidate.isNotEmpty(requirementId)) { + proposedRequirementCount += 1; + } if (UtilValidate.isEmpty(productFacility) && !isBuilt) { logMrpError(mrpId, productId, facilityId, now, "No ProductFacility record for [" + facilityId + "]; no requirement created.", delegator); @@ -957,8 +1170,10 @@ public class MrpServices { InventoryEventPlannedServices.createOrUpdateMrpEvent(eventMap, proposedOrder.getQuantity(), eventFacilityId, eventName, isProposedOrderLate(proposedOrder.getRequirementStartDate(), now, inventoryEventForMRP), delegator); } catch (GenericEntityException e) { - return ServiceUtil.returnError(UtilProperties.getMessage(RESOURCE, "ManufacturingMrpCreateOrUpdateEvent", - UtilMisc.toMap("parameters", parameters), locale)); + Map<String, Object> errorResult = ServiceUtil.returnError(UtilProperties.getMessage(RESOURCE, + "ManufacturingMrpCreateOrUpdateEvent", UtilMisc.toMap("parameters", parameters), locale)); + return finishMrpRunLog(dispatcher, userLogin, mrpRunLogId, now, mrpId, failureReasonFromResult(errorResult), + cleanFailureMessage(errorResult), null, null, null, errorResult); } // stockTmp = stockTmp.add(proposedOrder.getQuantity()); @@ -977,6 +1192,16 @@ public class MrpServices { result.put("msgResult", msgResult); result.put(ModelService.RESPONSE_MESSAGE, ModelService.RESPOND_SUCCESS); Debug.logInfo("return from executeMrp", MODULE); - return result; + Long mrpEventCount = countMrpEventsForRun(delegator, mrpId); + Long requirementCount = proposedRequirementCount; + String outcomeMessage = buildOutcomeMessage(mrpEventCount, proposedRequirementCount); + return finishMrpRunLog(dispatcher, userLogin, mrpRunLogId, now, mrpId, null, null, mrpEventCount, requirementCount, + outcomeMessage, result); + } catch (RuntimeException e) { + String errorMessage = cleanFailureMessage(e); + finishMrpRunLog(dispatcher, userLogin, mrpRunLogId, now, mrpId, failureReasonFromException(e), errorMessage, + null, null, null, ServiceUtil.returnError(errorMessage)); + throw e; + } } }

