0001: /*******************************************************************************
0002: * Licensed to the Apache Software Foundation (ASF) under one
0003: * or more contributor license agreements. See the NOTICE file
0004: * distributed with this work for additional information
0005: * regarding copyright ownership. The ASF licenses this file
0006: * to you under the Apache License, Version 2.0 (the
0007: * "License"); you may not use this file except in compliance
0008: * with the License. You may obtain a copy of the License at
0009: *
0010: * http://www.apache.org/licenses/LICENSE-2.0
0011: *
0012: * Unless required by applicable law or agreed to in writing,
0013: * software distributed under the License is distributed on an
0014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
0015: * KIND, either express or implied. See the License for the
0016: * specific language governing permissions and limitations
0017: * under the License.
0018: *******************************************************************************/package org.ofbiz.manufacturing.mrp;
0019:
0020: import java.sql.Timestamp;
0021: import java.util.Calendar;
0022: import java.util.HashMap;
0023: import java.util.Iterator;
0024: import java.util.LinkedList;
0025: import java.util.List;
0026: import java.util.ArrayList;
0027: import java.util.ListIterator;
0028: import java.util.Map;
0029:
0030: import org.ofbiz.base.util.Debug;
0031: import org.ofbiz.base.util.UtilDateTime;
0032: import org.ofbiz.base.util.UtilMisc;
0033: import org.ofbiz.base.util.UtilValidate;
0034: import org.ofbiz.entity.GenericDelegator;
0035: import org.ofbiz.entity.GenericEntityException;
0036: import org.ofbiz.entity.GenericValue;
0037: import org.ofbiz.entity.condition.EntityCondition;
0038: import org.ofbiz.entity.condition.EntityExpr;
0039: import org.ofbiz.entity.condition.EntityOperator;
0040: import org.ofbiz.entity.util.EntityUtil;
0041: import org.ofbiz.manufacturing.bom.BOMNode;
0042: import org.ofbiz.service.DispatchContext;
0043: import org.ofbiz.service.GenericServiceException;
0044: import org.ofbiz.service.LocalDispatcher;
0045: import org.ofbiz.service.ModelService;
0046: import org.ofbiz.service.ServiceUtil;
0047:
0048: /**
0049: * Services for running MRP
0050: *
0051: */
0052: public class MrpServices {
0053:
0054: public static final String module = MrpServices.class.getName();
0055: public static final String resource = "ManufacturingUiLabels";
0056:
0057: /**
0058: * Initialize the InventoryEventPlanned table.
0059: * <li>PreConditions : none</li>
0060: * <li>Result : The table InventoryEventPlannedForMRP is initialized</li>
0061: * <li>INPUT : Parameter to get from the context :</li><ul>
0062: * <li>Boolean reInitialize<br/>
0063: * if true : we must reinitialize the table, else we synchronize the table (not for the moment)</li></ul>
0064: *
0065: * <li>OUTPUT : Result to put in the map :</li><ul>
0066: * <li>none</li></ul>
0067: *
0068: * @param ctx The DispatchContext that this service is operating in.
0069: * @param context Map containing the input parameters.
0070: * @return Map with the result of the service, the output parameters.
0071: */
0072:
0073: public static Map initInventoryEventPlanned(DispatchContext ctx,
0074: Map context) {
0075: GenericDelegator delegator = ctx.getDelegator();
0076: Timestamp now = UtilDateTime.nowTimestamp();
0077:
0078: Integer defaultYearsOffset = (Integer) context
0079: .get("defaultYearsOffset");
0080:
0081: //Erases the old table for the moment and initializes it with the new orders,
0082: //Does not modify the old one now.
0083: Debug.logInfo("initInventoryEventPlanned called", module);
0084:
0085: List listResult = null;
0086: try {
0087: listResult = delegator.findAll("InventoryEventPlanned");
0088: //int numOfRecordsRemoved = delegator.removeByCondition("InventoryEventPlanned", null);
0089: } catch (GenericEntityException e) {
0090: Debug
0091: .logError(
0092: e,
0093: "Error : delegator.findAll(\"InventoryEventPlanned\")",
0094: module);
0095: return ServiceUtil
0096: .returnError("Problem, we can not find all the items of InventoryEventPlanned, for more detail look at the log");
0097: }
0098: if (listResult != null) {
0099: try {
0100: delegator.removeAll(listResult);
0101: } catch (GenericEntityException e) {
0102: Debug.logError(e,
0103: "Error : delegator.removeAll(listResult), listResult ="
0104: + listResult, module);
0105: return ServiceUtil
0106: .returnError("Problem, we can not remove the InventoryEventPlanned items, for more detail look at the log");
0107: }
0108: }
0109:
0110: // Proposed requirements are deleted
0111: listResult = null;
0112: List listResultRoles = new ArrayList();
0113: try {
0114: listResult = delegator.findByAnd("Requirement", UtilMisc
0115: .toMap("requirementTypeId", "PRODUCT_REQUIREMENT",
0116: "statusId", "REQ_PROPOSED"));
0117: } catch (GenericEntityException e) {
0118: return ServiceUtil
0119: .returnError("Problem, we can not find all the items of InventoryEventPlanned, for more detail look at the log");
0120: }
0121: if (listResult != null) {
0122: try {
0123: Iterator listResultIt = listResult.iterator();
0124: while (listResultIt.hasNext()) {
0125: GenericValue tmpRequirement = (GenericValue) listResultIt
0126: .next();
0127: listResultRoles.addAll(tmpRequirement
0128: .getRelated("RequirementRole"));
0129: //int numOfRecordsRemoved = delegator.removeRelated("RequirementRole", tmpRequirement);
0130: }
0131: delegator.removeAll(listResultRoles);
0132: delegator.removeAll(listResult);
0133: } catch (GenericEntityException e) {
0134: return ServiceUtil
0135: .returnError("Problem, we can not remove the InventoryEventPlanned items, for more detail look at the log");
0136: }
0137: }
0138: listResult = null;
0139: try {
0140: listResult = delegator.findByAnd("Requirement", UtilMisc
0141: .toMap("requirementTypeId", "INTERNAL_REQUIREMENT",
0142: "statusId", "REQ_PROPOSED"));
0143: } catch (GenericEntityException e) {
0144: return ServiceUtil
0145: .returnError("Problem, we can not find all the items of InventoryEventPlanned, for more detail look at the log");
0146: }
0147: if (listResult != null) {
0148: try {
0149: delegator.removeAll(listResult);
0150: } catch (GenericEntityException e) {
0151: return ServiceUtil
0152: .returnError("Problem, we can not remove the InventoryEventPlanned items, for more detail look at the log");
0153: }
0154: }
0155:
0156: GenericValue genericResult = null;
0157: Map parameters = null;
0158: List resultList = null;
0159: Iterator iteratorResult = null;
0160: // ----------------------------------------
0161: // Loads all the approved sales order items and purchase order items
0162: // ----------------------------------------
0163: // This is the default required date for orders without dates spesified:
0164: // by convention it is a date far in the future of 100 years.
0165: Timestamp notAssignedDate = null;
0166: if (UtilValidate.isEmpty(defaultYearsOffset)) {
0167: notAssignedDate = now;
0168: } else {
0169: Calendar calendar = UtilDateTime.toCalendar(now);
0170: calendar.add(Calendar.YEAR, defaultYearsOffset.intValue());
0171: notAssignedDate = new Timestamp(calendar.getTimeInMillis());
0172: }
0173: resultList = null;
0174: iteratorResult = null;
0175: parameters = UtilMisc.toMap("orderTypeId", "SALES_ORDER",
0176: "oiStatusId", "ITEM_APPROVED");
0177: try {
0178: resultList = delegator.findByAnd(
0179: "OrderHeaderItemAndShipGroup", parameters, UtilMisc
0180: .toList("orderId"));
0181: } catch (GenericEntityException e) {
0182: Debug
0183: .logError(
0184: e,
0185: "Error : delegator.findByAnd(\"OrderItem\", parameters\")",
0186: module);
0187: Debug.logError(e, "Error : parameters = " + parameters,
0188: module);
0189: return ServiceUtil
0190: .returnError("Problem, we can not find the order items, for more detail look at the log");
0191: }
0192: iteratorResult = resultList.iterator();
0193: while (iteratorResult.hasNext()) {
0194: genericResult = (GenericValue) iteratorResult.next();
0195: String productId = genericResult.getString("productId");
0196: Double shipGroupQuantity = genericResult
0197: .getDouble("quantity");
0198: Double cancelledQuantity = genericResult
0199: .getDouble("cancelQuantity");
0200: if (UtilValidate.isNotEmpty(cancelledQuantity)) {
0201: shipGroupQuantity = new Double(shipGroupQuantity
0202: .doubleValue()
0203: - cancelledQuantity.doubleValue());
0204: }
0205: Double eventQuantityTmp = new Double(-1.0
0206: * shipGroupQuantity.doubleValue());
0207: if (eventQuantityTmp.doubleValue() == 0) {
0208: continue;
0209: }
0210: // This is the order in which order dates are considered:
0211: // OrderItemShipGroup.shipByDate
0212: // OrderItemShipGroup.shipAfterDate
0213: // OrderItem.shipBeforeDate
0214: // OrderItem.shipAfterDate
0215: // OrderItem.estimatedDeliveryDate
0216: Timestamp requiredByDate = genericResult
0217: .getTimestamp("shipByDate");
0218: if (UtilValidate.isEmpty(requiredByDate)) {
0219: requiredByDate = genericResult
0220: .getTimestamp("shipAfterDate");
0221: if (UtilValidate.isEmpty(requiredByDate)) {
0222: requiredByDate = genericResult
0223: .getTimestamp("oiShipBeforeDate");
0224: if (UtilValidate.isEmpty(requiredByDate)) {
0225: requiredByDate = genericResult
0226: .getTimestamp("oiShipAfterDate");
0227: if (UtilValidate.isEmpty(requiredByDate)) {
0228: requiredByDate = genericResult
0229: .getTimestamp("oiEstimatedDeliveryDate");
0230: if (requiredByDate == null) {
0231: requiredByDate = notAssignedDate;
0232: }
0233: }
0234: }
0235: }
0236: }
0237: parameters = UtilMisc.toMap("productId", productId,
0238: "eventDate", requiredByDate,
0239: "inventoryEventPlanTypeId", "SALES_ORDER_SHIP");
0240: try {
0241: InventoryEventPlannedServices
0242: .createOrUpdateInventoryEventPlanned(
0243: parameters,
0244: eventQuantityTmp,
0245: null,
0246: genericResult.getString("orderId")
0247: + "-"
0248: + genericResult
0249: .getString("orderItemSeqId"),
0250: false, delegator);
0251: } catch (GenericEntityException e) {
0252: return ServiceUtil
0253: .returnError("Problem initializing the InventoryEventPlanned entity (SALES_ORDER_SHIP)");
0254: }
0255: }
0256: // ----------------------------------------
0257: // Loads all the approved product requirements (po requirements)
0258: // ----------------------------------------
0259: resultList = null;
0260: iteratorResult = null;
0261: parameters = UtilMisc.toMap("requirementTypeId",
0262: "PRODUCT_REQUIREMENT", "statusId", "REQ_APPROVED");
0263: try {
0264: resultList = delegator.findByAnd("Requirement", parameters);
0265: } catch (GenericEntityException e) {
0266: return ServiceUtil
0267: .returnError("Problem, we can not find all the items of InventoryEventPlanned, for more detail look at the log");
0268: }
0269: iteratorResult = resultList.iterator();
0270: while (iteratorResult.hasNext()) {
0271: genericResult = (GenericValue) iteratorResult.next();
0272: String productId = genericResult.getString("productId");
0273: Double eventQuantityTmp = genericResult
0274: .getDouble("quantity");
0275: if (productId == null || eventQuantityTmp == null) {
0276: continue;
0277: }
0278: Timestamp estimatedShipDate = genericResult
0279: .getTimestamp("requiredByDate");
0280: if (estimatedShipDate == null) {
0281: estimatedShipDate = now;
0282: }
0283:
0284: parameters = UtilMisc.toMap("productId", productId,
0285: "eventDate", estimatedShipDate,
0286: "inventoryEventPlanTypeId", "PROD_REQ_RECP");
0287: try {
0288: InventoryEventPlannedServices
0289: .createOrUpdateInventoryEventPlanned(
0290: parameters, eventQuantityTmp, null,
0291: genericResult
0292: .getString("requirementId"),
0293: false, delegator);
0294: } catch (GenericEntityException e) {
0295: return ServiceUtil
0296: .returnError("Problem initializing the InventoryEventPlanned entity (PROD_REQ_RECP)");
0297: }
0298: }
0299:
0300: // ----------------------------------------
0301: // Loads all the approved purchase order items
0302: // ----------------------------------------
0303: resultList = null;
0304: iteratorResult = null;
0305: String orderId = null;
0306: GenericValue orderDeliverySchedule = null;
0307: parameters = UtilMisc.toMap("orderTypeId", "PURCHASE_ORDER",
0308: "itemStatusId", "ITEM_APPROVED");
0309: try {
0310: resultList = delegator.findByAnd("OrderHeaderAndItems",
0311: parameters, UtilMisc.toList("orderId"));
0312: } catch (GenericEntityException e) {
0313: Debug
0314: .logError(
0315: e,
0316: "Error : delegator.findByAnd(\"OrderItem\", parameters\")",
0317: module);
0318: Debug.logError(e, "Error : parameters = " + parameters,
0319: module);
0320: return ServiceUtil
0321: .returnError("Problem, we can not find the order items, for more detail look at the log");
0322: }
0323: iteratorResult = resultList.iterator();
0324: while (iteratorResult.hasNext()) {
0325: genericResult = (GenericValue) iteratorResult.next();
0326: String newOrderId = genericResult.getString("orderId");
0327: if (!newOrderId.equals(orderId)) {
0328: orderDeliverySchedule = null;
0329: orderId = newOrderId;
0330: try {
0331: orderDeliverySchedule = delegator.findByPrimaryKey(
0332: "OrderDeliverySchedule", UtilMisc.toMap(
0333: "orderId", orderId,
0334: "orderItemSeqId", "_NA_"));
0335: } catch (GenericEntityException e) {
0336: }
0337: }
0338: String productId = genericResult.getString("productId");
0339: Double eventQuantityTmp = new Double(genericResult
0340: .getDouble("quantity").doubleValue());
0341: GenericValue orderItemDeliverySchedule = null;
0342: try {
0343: orderItemDeliverySchedule = delegator.findByPrimaryKey(
0344: "OrderDeliverySchedule", UtilMisc.toMap(
0345: "orderId", orderId, "orderItemSeqId",
0346: genericResult
0347: .getString("orderItemSeqId")));
0348: } catch (GenericEntityException e) {
0349: }
0350: Timestamp estimatedShipDate = null;
0351: if (orderItemDeliverySchedule != null
0352: && orderItemDeliverySchedule
0353: .get("estimatedReadyDate") != null) {
0354: estimatedShipDate = orderItemDeliverySchedule
0355: .getTimestamp("estimatedReadyDate");
0356: } else if (orderDeliverySchedule != null
0357: && orderDeliverySchedule.get("estimatedReadyDate") != null) {
0358: estimatedShipDate = orderDeliverySchedule
0359: .getTimestamp("estimatedReadyDate");
0360: } else {
0361: estimatedShipDate = genericResult
0362: .getTimestamp("estimatedDeliveryDate");
0363: }
0364: if (estimatedShipDate == null) {
0365: estimatedShipDate = now;
0366: }
0367:
0368: parameters = UtilMisc.toMap("productId", productId,
0369: "eventDate", estimatedShipDate,
0370: "inventoryEventPlanTypeId", "PUR_ORDER_RECP");
0371: try {
0372: InventoryEventPlannedServices
0373: .createOrUpdateInventoryEventPlanned(
0374: parameters,
0375: eventQuantityTmp,
0376: null,
0377: genericResult.getString("orderId")
0378: + "-"
0379: + genericResult
0380: .getString("orderItemSeqId"),
0381: false, delegator);
0382: } catch (GenericEntityException e) {
0383: return ServiceUtil
0384: .returnError("Problem initializing the InventoryEventPlanned entity (PUR_ORDER_RECP)");
0385: }
0386: }
0387:
0388: // ----------------------------------------
0389: // PRODUCTION Run: components
0390: // ----------------------------------------
0391: resultList = null;
0392: iteratorResult = null;
0393: parameters = UtilMisc.toMap("workEffortGoodStdTypeId",
0394: "PRUNT_PROD_NEEDED", "statusId", "WEGS_CREATED");
0395: try {
0396: resultList = delegator.findByAnd("WorkEffortAndGoods",
0397: parameters);
0398: } catch (GenericEntityException e) {
0399: Debug
0400: .logError(
0401: e,
0402: "Error : delegator.findByAnd(\"OrderItem\", parameters\")",
0403: module);
0404: Debug.logError(e, "Error : parameters = " + parameters,
0405: module);
0406: return ServiceUtil
0407: .returnError("Problem, we can not find the order items, for more detail look at the log");
0408: }
0409: iteratorResult = resultList.iterator();
0410: while (iteratorResult.hasNext()) {
0411: genericResult = (GenericValue) iteratorResult.next();
0412: String productId = genericResult.getString("productId");
0413: Double eventQuantityTmp = new Double(-1.0
0414: * genericResult.getDouble("estimatedQuantity")
0415: .doubleValue());
0416: Timestamp estimatedShipDate = genericResult
0417: .getTimestamp("estimatedStartDate");
0418: if (estimatedShipDate == null) {
0419: estimatedShipDate = now;
0420: }
0421:
0422: parameters = UtilMisc.toMap("productId", productId,
0423: "eventDate", estimatedShipDate,
0424: "inventoryEventPlanTypeId", "MANUF_ORDER_REQ");
0425: try {
0426: String eventName = (UtilValidate.isEmpty(genericResult
0427: .getString("workEffortParentId")) ? genericResult
0428: .getString("workEffortId")
0429: : genericResult.getString("workEffortParentId")
0430: + "-"
0431: + genericResult
0432: .getString("workEffortId"));
0433: InventoryEventPlannedServices
0434: .createOrUpdateInventoryEventPlanned(
0435: parameters, eventQuantityTmp, null,
0436: eventName, false, delegator);
0437: } catch (GenericEntityException e) {
0438: return ServiceUtil
0439: .returnError("Problem initializing the InventoryEventPlanned entity (MRP_REQUIREMENT)");
0440: }
0441: }
0442:
0443: // ----------------------------------------
0444: // PRODUCTION Run: product produced
0445: // ----------------------------------------
0446: resultList = null;
0447: iteratorResult = null;
0448: parameters = UtilMisc.toMap("workEffortGoodStdTypeId",
0449: "PRUN_PROD_DELIV", "statusId", "WEGS_CREATED",
0450: "workEffortTypeId", "PROD_ORDER_HEADER");
0451: try {
0452: resultList = delegator.findByAnd("WorkEffortAndGoods",
0453: parameters);
0454: } catch (GenericEntityException e) {
0455: Debug
0456: .logError(
0457: e,
0458: "Error : delegator.findByAnd(\"OrderItem\", parameters\")",
0459: module);
0460: Debug.logError(e, "Error : parameters = " + parameters,
0461: module);
0462: return ServiceUtil
0463: .returnError("Problem, we can not find the order items, for more detail look at the log");
0464: }
0465: iteratorResult = resultList.iterator();
0466: while (iteratorResult.hasNext()) {
0467: genericResult = (GenericValue) iteratorResult.next();
0468: if ("PRUN_CLOSED".equals(genericResult
0469: .getString("currentStatusId"))) {
0470: continue;
0471: }
0472: Double qtyToProduce = genericResult
0473: .getDouble("quantityToProduce");
0474: if (qtyToProduce == null) {
0475: qtyToProduce = new Double(0);
0476: }
0477: Double qtyProduced = genericResult
0478: .getDouble("quantityProduced");
0479: if (qtyProduced == null) {
0480: qtyProduced = new Double(0);
0481: }
0482: if (qtyProduced.compareTo(qtyToProduce) >= 0) {
0483: continue;
0484: }
0485: double qtyDiff = qtyToProduce.doubleValue()
0486: - qtyProduced.doubleValue();
0487: String productId = genericResult.getString("productId");
0488: Double eventQuantityTmp = new Double(qtyDiff);
0489: Timestamp estimatedShipDate = genericResult
0490: .getTimestamp("estimatedCompletionDate");
0491: if (estimatedShipDate == null) {
0492: estimatedShipDate = now;
0493: }
0494:
0495: parameters = UtilMisc.toMap("productId", productId,
0496: "eventDate", estimatedShipDate,
0497: "inventoryEventPlanTypeId", "MANUF_ORDER_RECP");
0498: try {
0499: InventoryEventPlannedServices
0500: .createOrUpdateInventoryEventPlanned(
0501: parameters,
0502: eventQuantityTmp,
0503: null,
0504: genericResult.getString("workEffortId"),
0505: false, delegator);
0506: } catch (GenericEntityException e) {
0507: return ServiceUtil
0508: .returnError("Problem initializing the InventoryEventPlanned entity (MANUF_ORDER_RECP)");
0509: }
0510: }
0511:
0512: Map result = new HashMap();
0513: result.put(ModelService.RESPONSE_MESSAGE,
0514: ModelService.RESPOND_SUCCESS);
0515: Debug.logInfo("return from initInventoryEventPlanned", module);
0516: return result;
0517: }
0518:
0519: /**
0520: * Create a List with all the event of InventotyEventPlanned for one billOfMaterialLevel, sorted by productId and eventDate.
0521: *
0522: * <li>INPUT : Parameter to get from the context : </li><ul>
0523: * <li>Integer billOfMaterialLevel : 0 for root for more detail see BomHelper.getMaxDepth</li></ul>
0524: *
0525: * <li>OUTPUT : Result to put in the map :</li><ul>
0526: * <li>List listInventoryEventForMRP : all the event of InventotyEventPlanned for one billOfMaterialLevel, sorted by productId and eventDate<br/>
0527: * @param ctx The DispatchContext that this service is operating in.
0528: * @param context Map containing the input parameters.
0529: * @return Map with the result of the service, the output parameters.
0530: */
0531: public static Map listProductForMrp(DispatchContext ctx, Map context) {
0532: Debug.logInfo("listProductForMrp called", module);
0533: // read parameters from context
0534: GenericDelegator delegator = ctx.getDelegator();
0535: Long billOfMaterialLevel = (Long) context
0536: .get("billOfMaterialLevel");
0537:
0538: // Find all products in MrpInventoryEventPlanned, ordered by bom and eventDate
0539: List listResult = null;
0540: // If billOfMaterialLevel == 0 the search must be done with (billOfMaterialLevel == 0 || billOfMaterialLevel == null)
0541: EntityCondition parameters = null;
0542: if (billOfMaterialLevel.intValue() == 0) {
0543: parameters = new EntityExpr(
0544: new EntityExpr("billOfMaterialLevel",
0545: EntityOperator.EQUALS, null),
0546: EntityOperator.OR, new EntityExpr(
0547: "billOfMaterialLevel",
0548: EntityOperator.EQUALS, billOfMaterialLevel));
0549: } else {
0550: parameters = new EntityExpr("billOfMaterialLevel",
0551: EntityOperator.EQUALS, billOfMaterialLevel);
0552: }
0553:
0554: List orderBy = UtilMisc.toList("productId", "eventDate");
0555: try {
0556: //listResult = delegator.findByAnd("MrpInventoryEventPlanned", parameters, orderBy);
0557: listResult = delegator.findByCondition(
0558: "MrpInventoryEventPlanned", parameters, null,
0559: orderBy);
0560: } catch (GenericEntityException e) {
0561: Debug
0562: .logError(
0563: e,
0564: "Error : delegator.findByCondition(\"MrpInventoryEventPlanned\", parameters, null, orderBy)",
0565: module);
0566: Debug.logError(e, "Error : parameters = " + parameters,
0567: module);
0568: Debug.logError(e, "Error : orderBy = " + orderBy, module);
0569: return ServiceUtil
0570: .returnError("Problem, we can not find the products, for more detail look at the log");
0571: }
0572: Map result = new HashMap();
0573: result.put("listInventoryEventForMrp", listResult);
0574: result.put(ModelService.RESPONSE_MESSAGE,
0575: ModelService.RESPOND_SUCCESS);
0576: Debug.logInfo("return from listProductForMrp "
0577: + billOfMaterialLevel, module);
0578: return result;
0579: }
0580:
0581: /**
0582: * Find the quantity on hand of products for MRP.
0583: * <li>PreConditions : none</li>
0584: * <li>Result : We get the quantity of product available in the stocks.</li>
0585: *
0586: * @param product the product for which the Quantity Available is required
0587: * @return the sum of all the totalAvailableToPromise of the inventoryItem related to the product, if the related facility is Mrp available (not yet implemented!!)
0588: */
0589: public static double findProductMrpQoh(GenericValue product,
0590: String facilityId, LocalDispatcher dispatcher,
0591: GenericDelegator delegator) {
0592: List orderBy = UtilMisc.toList("facilityId", "-receivedDate",
0593: "-inventoryItemId");
0594: Map resultMap = null;
0595: try {
0596: if (facilityId == null) {
0597: resultMap = dispatcher.runSync(
0598: "getProductInventoryAvailable", UtilMisc.toMap(
0599: "productId", product
0600: .getString("productId")));
0601: } else {
0602: resultMap = dispatcher.runSync(
0603: "getInventoryAvailableByFacility", UtilMisc
0604: .toMap("productId", product
0605: .getString("productId"),
0606: "facilityId", facilityId));
0607: }
0608: } catch (GenericServiceException e) {
0609: Debug
0610: .logError(
0611: e,
0612: "Error calling getProductInventoryAvailableByFacility service",
0613: module);
0614: logMrpError(product.getString("productId"),
0615: "Unable to count inventory", delegator);
0616: return 0;
0617: }
0618: return ((Double) resultMap.get("quantityOnHandTotal"))
0619: .doubleValue();
0620: }
0621:
0622: public static void logMrpError(String productId,
0623: String errorMessage, GenericDelegator delegator) {
0624: logMrpError(productId, UtilDateTime.nowTimestamp(),
0625: errorMessage, delegator);
0626: }
0627:
0628: public static void logMrpError(String productId,
0629: Timestamp eventDate, String errorMessage,
0630: GenericDelegator delegator) {
0631: try {
0632: if (UtilValidate.isNotEmpty(productId)
0633: && UtilValidate.isNotEmpty(errorMessage)) {
0634: GenericValue inventoryEventError = delegator.makeValue(
0635: "InventoryEventPlanned", UtilMisc.toMap(
0636: "productId", productId, "eventDate",
0637: eventDate, "inventoryEventPlanTypeId",
0638: "ERROR", "eventName", errorMessage));
0639: delegator.createOrStore(inventoryEventError);
0640: }
0641: } catch (GenericEntityException e) {
0642: Debug.logError(e,
0643: "Error calling logMrpError for productId ["
0644: + productId + "] and errorMessage ["
0645: + errorMessage + "]", module);
0646: }
0647: }
0648:
0649: /**
0650: * Process the bill of material (bom) of the product to insert components in the InventoryEventPlanned table.
0651: * Before inserting in the entity, test if there is the record already existing to add quantity rather to create a new one.
0652: *
0653: * @param product
0654: * @param eventQuantity the product quantity needed
0655: * @param startDate the startDate of the productionRun which will used to produce the product
0656: * @param routingTaskStartDate Map with all the routingTask as keys and startDate of each of them
0657: * @return None
0658: */
0659:
0660: public static void processBomComponent(GenericValue product,
0661: double eventQuantity, Timestamp startDate,
0662: Map routingTaskStartDate, List listComponent) {
0663: // TODO : change the return type to boolean to be able to test if all is ok or if it have had a exception
0664: GenericDelegator delegator = product.getDelegator();
0665:
0666: if (listComponent != null && listComponent.size() > 0) {
0667: Iterator listComponentIter = listComponent.iterator();
0668: while (listComponentIter.hasNext()) {
0669: BOMNode node = (BOMNode) listComponentIter.next();
0670: GenericValue productComponent = node.getProductAssoc();
0671: // read the startDate for the component
0672: String routingTask = node.getProductAssoc().getString(
0673: "routingWorkEffortId");
0674: Timestamp eventDate = (routingTask == null || !routingTaskStartDate
0675: .containsKey(routingTask)) ? startDate
0676: : (Timestamp) routingTaskStartDate
0677: .get(routingTask);
0678: // if the components is valid at the event Date create the Mrp requirement in the InventoryEventPlanned entity
0679: if (EntityUtil.isValueActive(productComponent,
0680: eventDate)) {
0681: //Map parameters = UtilMisc.toMap("productId", productComponent.getString("productIdTo"));
0682: Map parameters = UtilMisc.toMap("productId", node
0683: .getProduct().getString("productId"));
0684: parameters.put("eventDate", eventDate);
0685: parameters.put("inventoryEventPlanTypeId",
0686: "MRP_REQUIREMENT");
0687: double componentEventQuantity = node.getQuantity();
0688: try {
0689: InventoryEventPlannedServices
0690: .createOrUpdateInventoryEventPlanned(
0691: parameters,
0692: new Double(
0693: -1.0
0694: * componentEventQuantity),
0695: null, product.get("productId")
0696: + ": " + eventDate,
0697: false, delegator);
0698: } catch (GenericEntityException e) {
0699: Debug.logError(
0700: "Error : delegator.findByPrimaryKey(\"InventoryEventPlanned\", parameters) ="
0701: + parameters + "--"
0702: + e.getMessage(), module);
0703: logMrpError(
0704: node.getProduct()
0705: .getString("productId"),
0706: "Unable to create event (processBomComponent)",
0707: delegator);
0708: }
0709: }
0710: }
0711: }
0712: }
0713:
0714: /**
0715: * Launch the MRP.
0716: * <li>PreConditions : none</li>
0717: * <li>Result : The date when we must order or begin to build the products and subproducts we need are calclated</li>
0718: *
0719: * <li>INPUT : parameters to get from the context :</li><ul>
0720: * <li>String mrpName</li></ul>
0721: *
0722: * <li>OUTPUT : Result to put in the map :</li><ul>
0723: * <li>none</li></ul>
0724: * @param ctx The DispatchContext that this service is operating in.
0725: * @param context Map containing the input parameters, productId routingId, quantity, startDate.
0726: * @return Map with the result of the service, the output parameters.
0727: */
0728: public static Map executeMrp(DispatchContext ctx, Map context) {
0729: Debug.logInfo("executeMrp called", module);
0730:
0731: GenericDelegator delegator = ctx.getDelegator();
0732: LocalDispatcher dispatcher = ctx.getDispatcher();
0733: GenericValue userLogin = (GenericValue) context
0734: .get("userLogin");
0735: Timestamp now = UtilDateTime.nowTimestamp();
0736:
0737: String mrpName = (String) context.get("mrpName");
0738: Integer defaultYearsOffset = (Integer) context
0739: .get("defaultYearsOffset");
0740: String facilityGroupId = (String) context
0741: .get("facilityGroupId");
0742: String facilityId = (String) context.get("facilityId");
0743: String manufacturingFacilityId = null;
0744: if (UtilValidate.isEmpty(facilityId)
0745: && UtilValidate.isEmpty(facilityGroupId)) {
0746: return ServiceUtil
0747: .returnError("facilityId and facilityGroupId cannot be both null");
0748: }
0749: if (UtilValidate.isEmpty(facilityId)) {
0750: try {
0751: GenericValue facilityGroup = delegator
0752: .findByPrimaryKey("FacilityGroup", UtilMisc
0753: .toMap("facilityGroupId",
0754: facilityGroupId));
0755: if (UtilValidate.isEmpty(facilityGroup)) {
0756: return ServiceUtil.returnError("facilityGroupId ["
0757: + facilityGroupId + "] is not valid");
0758: }
0759: List facilities = facilityGroup.getRelated(
0760: "FacilityGroupMember", UtilMisc
0761: .toList("sequenceNum"));
0762: if (UtilValidate.isEmpty(facilities)) {
0763: return ServiceUtil
0764: .returnError("No facility associated to facilityGroupId ["
0765: + facilityGroupId + "]");
0766: }
0767: Iterator facilitiesIt = facilities.iterator();
0768: while (facilitiesIt.hasNext()) {
0769: GenericValue facilityMember = (GenericValue) facilitiesIt
0770: .next();
0771: GenericValue facility = facilityMember
0772: .getRelatedOne("Facility");
0773: if ("WAREHOUSE".equals(facility
0774: .getString("facilityTypeId"))
0775: && UtilValidate.isEmpty(facilityId)) {
0776: facilityId = facility.getString("facilityId");
0777: }
0778: if ("PLANT".equals(facility
0779: .getString("facilityTypeId"))
0780: && UtilValidate
0781: .isEmpty(manufacturingFacilityId)) {
0782: manufacturingFacilityId = facility
0783: .getString("facilityId");
0784: }
0785: }
0786: } catch (GenericEntityException e) {
0787: return ServiceUtil
0788: .returnError("Problem loading facility group information: "
0789: + e.getMessage());
0790: }
0791: } else {
0792: manufacturingFacilityId = facilityId;
0793: }
0794:
0795: if (UtilValidate.isEmpty(facilityId)
0796: || UtilValidate.isEmpty(manufacturingFacilityId)) {
0797: return ServiceUtil
0798: .returnError("facilityId and manufacturingFacilityId cannot be null");
0799: }
0800:
0801: int bomLevelWithNoEvent = 0;
0802: double stockTmp = 0;
0803: String oldProductId = null;
0804: String productId = null;
0805: GenericValue product = null;
0806: GenericValue productFacility = null;
0807: double eventQuantity = 0;
0808: Timestamp eventDate = null;
0809: boolean isNegative = false;
0810: double quantityNeeded = 0;
0811: double reorderQuantity = 0;
0812: double minimumStock = 0;
0813: int daysToShip = 0;
0814: List components = null;
0815: boolean isBuilt = false;
0816: GenericValue routing = null;
0817:
0818: Map result = null;
0819: Map parameters = null;
0820: List listInventoryEventForMRP = null;
0821: ListIterator iteratorListInventoryEventForMRP = null;
0822: GenericValue inventoryEventForMRP = null;
0823:
0824: // Initialisation of the InventoryEventPlanned table, This table will contain the products we want to buy or build.
0825: parameters = UtilMisc.toMap("reInitialize", Boolean.TRUE,
0826: "defaultYearsOffset", defaultYearsOffset, "userLogin",
0827: userLogin);
0828: try {
0829: result = dispatcher.runSync("initInventoryEventPlanned",
0830: parameters);
0831: } catch (GenericServiceException e) {
0832: Debug.logError("Error : initInventoryEventPlanned", module);
0833: Debug
0834: .logError("Error : parameters = " + parameters,
0835: module);
0836: return ServiceUtil
0837: .returnError("Problem, can not initialise the table InventoryEventPlanned, for more detail look at the log");
0838: }
0839: long bomLevel = 0;
0840: do {
0841: //get the products from the InventoryEventPlanned table for the current billOfMaterialLevel (ie. BOM)
0842: parameters = UtilMisc.toMap("billOfMaterialLevel",
0843: new Long(bomLevel), "userLogin", userLogin);
0844: try {
0845: result = dispatcher.runSync("listProductForMrp",
0846: parameters);
0847: } catch (GenericServiceException e) {
0848: Debug.logError(
0849: "Error : listProductForMrp, parameters ="
0850: + parameters, module);
0851: return ServiceUtil
0852: .returnError("Problem, can not list the products for the MRP, for more detail look at the log");
0853: }
0854: listInventoryEventForMRP = (List) result
0855: .get("listInventoryEventForMrp");
0856:
0857: if (listInventoryEventForMRP != null
0858: && listInventoryEventForMRP.size() > 0) {
0859: bomLevelWithNoEvent = 0;
0860: iteratorListInventoryEventForMRP = listInventoryEventForMRP
0861: .listIterator();
0862:
0863: oldProductId = "";
0864: while (iteratorListInventoryEventForMRP.hasNext()) {
0865: inventoryEventForMRP = (GenericValue) iteratorListInventoryEventForMRP
0866: .next();
0867: productId = inventoryEventForMRP
0868: .getString("productId");
0869: eventQuantity = inventoryEventForMRP.getDouble(
0870: "eventQuantity").doubleValue();
0871:
0872: if (!productId.equals(oldProductId)) {
0873: double positiveEventQuantity = (eventQuantity > 0 ? eventQuantity
0874: : -1 * eventQuantity);
0875: // It's a new product, so it's necessary to read the MrpQoh
0876: try {
0877: product = inventoryEventForMRP
0878: .getRelatedOneCache("Product");
0879: productFacility = EntityUtil
0880: .getFirst(product
0881: .getRelatedByAndCache(
0882: "ProductFacility",
0883: UtilMisc
0884: .toMap(
0885: "facilityId",
0886: facilityId)));
0887: } catch (GenericEntityException e) {
0888: return ServiceUtil
0889: .returnError("Problem, can not find the product for a event, for more detail look at the log");
0890: }
0891: stockTmp = findProductMrpQoh(product,
0892: facilityId, dispatcher, delegator);
0893: try {
0894: InventoryEventPlannedServices
0895: .createOrUpdateInventoryEventPlanned(
0896: UtilMisc
0897: .toMap(
0898: "productId",
0899: product
0900: .getString("productId"),
0901: "inventoryEventPlanTypeId",
0902: "INITIAL_QOH",
0903: "eventDate",
0904: now),
0905: new Double(stockTmp),
0906: facilityId, null, false,
0907: delegator);
0908: } catch (GenericEntityException e) {
0909: return ServiceUtil
0910: .returnError("Problem running createOrUpdateInventoryEventPlanned");
0911: }
0912: // 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
0913: // as well and cause problems
0914: daysToShip = 0;
0915: if (productFacility != null) {
0916: reorderQuantity = (productFacility
0917: .getDouble("reorderQuantity") != null ? productFacility
0918: .getDouble("reorderQuantity")
0919: .doubleValue()
0920: : -1);
0921: minimumStock = (productFacility
0922: .getDouble("minimumStock") != null ? productFacility
0923: .getDouble("minimumStock")
0924: .doubleValue()
0925: : 0);
0926: if ("SALES_ORDER_SHIP"
0927: .equals(inventoryEventForMRP
0928: .getString("inventoryEventPlanTypeId"))) {
0929: daysToShip = (productFacility
0930: .getLong("daysToShip") != null ? productFacility
0931: .getLong("daysToShip")
0932: .intValue()
0933: : 0);
0934: }
0935: } else {
0936: minimumStock = 0;
0937: reorderQuantity = -1;
0938: }
0939: // -----------------------------------------------------
0940: // The components are also loaded thru the configurator
0941: Map serviceResponse = null;
0942: try {
0943: serviceResponse = dispatcher
0944: .runSync(
0945: "getManufacturingComponents",
0946: UtilMisc
0947: .toMap(
0948: "productId",
0949: product
0950: .getString("productId"),
0951: "quantity",
0952: new Double(
0953: positiveEventQuantity),
0954: "excludeWIPs",
0955: Boolean.FALSE,
0956: "userLogin",
0957: userLogin));
0958: } catch (Exception e) {
0959: return ServiceUtil
0960: .returnError("An error occurred exploding the product ["
0961: + product
0962: .getString("productId")
0963: + "]");
0964: }
0965: components = (List) serviceResponse
0966: .get("components");
0967: if (components != null && components.size() > 0) {
0968: BOMNode node = ((BOMNode) components.get(0))
0969: .getParentNode();
0970: isBuilt = node.isManufactured();
0971: } else {
0972: isBuilt = false;
0973: }
0974: // #####################################################
0975:
0976: oldProductId = productId;
0977: }
0978:
0979: stockTmp = stockTmp + eventQuantity;
0980: if (stockTmp < minimumStock) {
0981: double qtyToStock = minimumStock - stockTmp;
0982: //need to buy or build the product as we have not enough stock
0983: eventDate = inventoryEventForMRP
0984: .getTimestamp("eventDate");
0985: // to be just before the requirement
0986: eventDate.setTime(eventDate.getTime() - 1);
0987: ProposedOrder proposedOrder = new ProposedOrder(
0988: product, facilityId,
0989: manufacturingFacilityId, isBuilt,
0990: eventDate, qtyToStock);
0991: proposedOrder.setMrpName(mrpName);
0992: // calculate the ProposedOrder quantity and update the quantity object property.
0993: proposedOrder.calculateQuantityToSupply(
0994: reorderQuantity, minimumStock,
0995: iteratorListInventoryEventForMRP);
0996:
0997: // -----------------------------------------------------
0998: // The components are also loaded thru the configurator
0999: Map serviceResponse = null;
1000: try {
1001: serviceResponse = dispatcher.runSync(
1002: "getManufacturingComponents",
1003: UtilMisc.toMap("productId", product
1004: .getString("productId"),
1005: "quantity",
1006: new Double(proposedOrder
1007: .getQuantity()),
1008: "excludeWIPs",
1009: Boolean.FALSE, "userLogin",
1010: userLogin));
1011: } catch (Exception e) {
1012: return ServiceUtil
1013: .returnError("An error occurred exploding the product ["
1014: + product
1015: .getString("productId")
1016: + "]");
1017: }
1018: components = (List) serviceResponse
1019: .get("components");
1020: String routingId = (String) serviceResponse
1021: .get("workEffortId");
1022: if (routingId != null) {
1023: try {
1024: routing = delegator.findByPrimaryKey(
1025: "WorkEffort", UtilMisc.toMap(
1026: "workEffortId",
1027: routingId));
1028: } catch (GenericEntityException e) {
1029: return ServiceUtil
1030: .returnError("Problem, can not find the product for a event, for more detail look at the log");
1031: }
1032: } else {
1033: routing = null;
1034: }
1035: if (components != null && components.size() > 0) {
1036: BOMNode node = ((BOMNode) components.get(0))
1037: .getParentNode();
1038: isBuilt = node.isManufactured();
1039: } else {
1040: isBuilt = false;
1041: }
1042: // #####################################################
1043:
1044: // calculate the ProposedOrder requirementStartDate and update the requirementStartDate object property.
1045: Map routingTaskStartDate = proposedOrder
1046: .calculateStartDate(daysToShip,
1047: routing, delegator, dispatcher,
1048: userLogin);
1049: if (isBuilt) {
1050: // process the product components
1051: processBomComponent(product, proposedOrder
1052: .getQuantity(), proposedOrder
1053: .getRequirementStartDate(),
1054: routingTaskStartDate, components);
1055: }
1056: // create the ProposedOrder (only if the product is warehouse managed), and the InventoryEventPlanned associated
1057: String requirementId = null;
1058: if (productFacility != null) {
1059: requirementId = proposedOrder.create(ctx,
1060: userLogin);
1061: }
1062: if (UtilValidate.isEmpty(productFacility)
1063: && !isBuilt) {
1064: logMrpError(
1065: productId,
1066: now,
1067: "No ProductFacility record for ["
1068: + facilityId
1069: + "]; no requirement created.",
1070: delegator);
1071: }
1072: String eventName = null;
1073: if (UtilValidate.isNotEmpty(requirementId)) {
1074: eventName = "*"
1075: + requirementId
1076: + " ("
1077: + proposedOrder
1078: .getRequirementStartDate()
1079: + ")*";
1080: }
1081: Map eventMap = UtilMisc.toMap("productId",
1082: product.getString("productId"),
1083: "eventDate", eventDate,
1084: "inventoryEventPlanTypeId",
1085: (isBuilt ? "PROP_MANUF_O_RECP"
1086: : "PROP_PUR_O_RECP"));
1087: try {
1088: InventoryEventPlannedServices
1089: .createOrUpdateInventoryEventPlanned(
1090: eventMap,
1091: new Double(proposedOrder
1092: .getQuantity()),
1093: null,
1094: eventName,
1095: (proposedOrder
1096: .getRequirementStartDate()
1097: .compareTo(now) < 0),
1098: delegator);
1099: } catch (GenericEntityException e) {
1100: return ServiceUtil
1101: .returnError("Problem running createOrUpdateInventoryEventPlanned");
1102: }
1103: //
1104: stockTmp = stockTmp
1105: + proposedOrder.getQuantity();
1106: }
1107: }
1108: } else {
1109: bomLevelWithNoEvent += 1;
1110: }
1111:
1112: bomLevel += 1;
1113: // if there are 3 levels with no inventoryEvenPanned we stop
1114: } while (bomLevelWithNoEvent < 3);
1115:
1116: result = new HashMap();
1117: List msgResult = new LinkedList();
1118: result.put("msgResult", msgResult);
1119: result.put(ModelService.RESPONSE_MESSAGE,
1120: ModelService.RESPOND_SUCCESS);
1121: Debug.logInfo("return from executeMrp", module);
1122: return result;
1123: }
1124: }
|