| /******************************************************************************* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| *******************************************************************************/ |
| |
| package org.apache.ofbiz.manufacturing.bom; |
| |
| import java.math.BigDecimal; |
| import java.sql.Timestamp; |
| import java.util.Date; |
| import java.util.HashMap; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.apache.ofbiz.base.util.Debug; |
| import org.apache.ofbiz.base.util.UtilDateTime; |
| import org.apache.ofbiz.base.util.UtilGenerics; |
| import org.apache.ofbiz.base.util.UtilMisc; |
| import org.apache.ofbiz.base.util.UtilValidate; |
| import org.apache.ofbiz.entity.Delegator; |
| import org.apache.ofbiz.entity.GenericEntityException; |
| import org.apache.ofbiz.entity.GenericValue; |
| import org.apache.ofbiz.entity.util.EntityQuery; |
| import org.apache.ofbiz.entity.util.EntityUtil; |
| import org.apache.ofbiz.manufacturing.mrp.ProposedOrder; |
| import org.apache.ofbiz.service.GenericServiceException; |
| import org.apache.ofbiz.service.LocalDispatcher; |
| |
| /** An ItemCoinfigurationNode represents a component in a bill of materials. |
| */ |
| |
| public class BOMNode { |
| public static final String module = BOMNode.class.getName(); |
| |
| protected LocalDispatcher dispatcher = null; |
| protected Delegator delegator = null; |
| protected GenericValue userLogin = null; |
| |
| private BOMTree tree; // the tree to which this node belongs |
| private BOMNode parentNode; // the parent node (null if it's not present) |
| private BOMNode substitutedNode; // The virtual node (if any) that this instance substitutes |
| private GenericValue ruleApplied; // The rule (if any) that that has been applied to configure the current node |
| private String productForRules; |
| private GenericValue product; // the current product (from Product entity) |
| private GenericValue productAssoc; // the product assoc record (from ProductAssoc entity) in which the current product is in productIdTo |
| private List<GenericValue> children; // current node's children (ProductAssocs) |
| private List<BOMNode> childrenNodes; // current node's children nodes (BOMNode) |
| private BigDecimal quantityMultiplier; // the necessary quantity as declared in the bom (from ProductAssocs or ProductManufacturingRule) |
| private BigDecimal scrapFactor; // the scrap factor as declared in the bom (from ProductAssocs) |
| // Runtime fields |
| private int depth; // the depth of this node in the current tree |
| private BigDecimal quantity; // the quantity of this node in the current tree |
| private String bomTypeId; // the type of the current tree |
| |
| public BOMNode(GenericValue product, LocalDispatcher dispatcher, GenericValue userLogin) { |
| this.product = product; |
| this.delegator = product.getDelegator(); |
| this.dispatcher = dispatcher; |
| this.userLogin = userLogin; |
| children = new LinkedList<GenericValue>(); |
| childrenNodes = new LinkedList<BOMNode>(); |
| parentNode = null; |
| productForRules = null; |
| bomTypeId = null; |
| quantityMultiplier = BigDecimal.ONE; |
| scrapFactor = BigDecimal.ONE; |
| // Now we initialize the fields used in breakdowns |
| depth = 0; |
| quantity = BigDecimal.ZERO; |
| } |
| |
| public BOMNode(String productId, Delegator delegator, LocalDispatcher dispatcher, GenericValue userLogin) throws GenericEntityException { |
| this(EntityQuery.use(delegator).from("Product").where("productId", productId).queryOne(), dispatcher, userLogin); |
| } |
| |
| protected void loadChildren(String partBomTypeId, Date inDate, List<GenericValue> productFeatures, int type) throws GenericEntityException { |
| if (product == null) { |
| throw new GenericEntityException("product is null"); |
| } |
| // If the date is null, set it to today. |
| if (inDate == null) inDate = new Date(); |
| bomTypeId = partBomTypeId; |
| List<GenericValue> rows = EntityQuery.use(delegator).from("ProductAssoc") |
| .where("productId", product.get("productId"), |
| "productAssocTypeId", partBomTypeId) |
| .orderBy("sequenceNum", "productIdTo") |
| .filterByDate(inDate).queryList(); |
| if ((UtilValidate.isEmpty(rows)) && substitutedNode != null) { |
| // If no child is found and this is a substituted node |
| // we try to search for substituted node's children. |
| rows = EntityQuery.use(delegator).from("ProductAssoc") |
| .where("productId", substitutedNode.getProduct().get("productId"), |
| "productAssocTypeId", partBomTypeId) |
| .orderBy("sequenceNum") |
| .filterByDate(inDate).queryList(); |
| } |
| children = new LinkedList<GenericValue>(); |
| children.addAll(rows); |
| childrenNodes = new LinkedList<BOMNode>(); |
| BOMNode oneChildNode = null; |
| for (GenericValue oneChild : children) { |
| // Configurator |
| oneChildNode = configurator(oneChild, productFeatures, getRootNode().getProductForRules(), inDate); |
| // If the node is null this means that the node has been discarded by the rules. |
| if (oneChildNode != null) { |
| oneChildNode.setParentNode(this); |
| switch (type) { |
| case BOMTree.EXPLOSION: |
| oneChildNode.loadChildren(partBomTypeId, inDate, productFeatures, BOMTree.EXPLOSION); |
| break; |
| case BOMTree.EXPLOSION_MANUFACTURING: |
| // for manufacturing trees, do not look through and create production runs for children unless there is no warehouse stocking of this node item |
| if (!oneChildNode.isWarehouseManaged(null)) { // FIXME: we will need to pass a facilityId here |
| oneChildNode.loadChildren(partBomTypeId, inDate, productFeatures, type); |
| } |
| break; |
| } |
| } |
| childrenNodes.add(oneChildNode); |
| } |
| } |
| |
| private BOMNode substituteNode(BOMNode oneChildNode, List<GenericValue> productFeatures, |
| List<GenericValue> productPartRules) throws GenericEntityException { |
| if (productPartRules != null) { |
| GenericValue rule = null; |
| for (int i = 0; i < productPartRules.size(); i++) { |
| rule = productPartRules.get(i); |
| String ruleCondition = (String)rule.get("productFeature"); |
| String ruleOperator = (String)rule.get("ruleOperator"); |
| String newPart = (String)rule.get("productIdInSubst"); |
| BigDecimal ruleQuantity = BigDecimal.ZERO; |
| try { |
| ruleQuantity = rule.getBigDecimal("quantity"); |
| } catch (Exception exc) { |
| ruleQuantity = BigDecimal.ZERO; |
| } |
| |
| GenericValue feature = null; |
| boolean ruleSatisfied = false; |
| if (ruleCondition == null || ruleCondition.equals("")) { |
| ruleSatisfied = true; |
| } else { |
| if (productFeatures != null) { |
| for (int j = 0; j < productFeatures.size(); j++) { |
| feature = productFeatures.get(j); |
| if (ruleCondition.equals(feature.get("productFeatureId"))) { |
| ruleSatisfied = true; |
| break; |
| } |
| } |
| } |
| } |
| if (ruleSatisfied && ruleOperator.equals("OR")) { |
| BOMNode tmpNode = oneChildNode; |
| if (newPart == null || newPart.equals("")) { |
| oneChildNode = null; |
| } else { |
| BOMNode origNode = oneChildNode; |
| oneChildNode = new BOMNode(newPart, delegator, dispatcher, userLogin); |
| oneChildNode.setTree(tree); |
| oneChildNode.setSubstitutedNode(tmpNode); |
| oneChildNode.setRuleApplied(rule); |
| oneChildNode.setProductAssoc(origNode.getProductAssoc()); |
| oneChildNode.setScrapFactor(origNode.getScrapFactor()); |
| if (ruleQuantity.compareTo(BigDecimal.ZERO) > 0) { |
| oneChildNode.setQuantityMultiplier(ruleQuantity); |
| } else { |
| oneChildNode.setQuantityMultiplier(origNode.getQuantityMultiplier()); |
| } |
| } |
| break; |
| } |
| // FIXME: AND operator still not implemented |
| } // end of for |
| |
| } |
| return oneChildNode; |
| } |
| |
| private BOMNode configurator(GenericValue node, List<GenericValue> productFeatures, |
| String productIdForRules, Date inDate) throws GenericEntityException { |
| BOMNode oneChildNode = new BOMNode((String)node.get("productIdTo"), delegator, dispatcher, userLogin); |
| oneChildNode.setTree(tree); |
| oneChildNode.setProductAssoc(node); |
| try { |
| oneChildNode.setQuantityMultiplier(node.getBigDecimal("quantity")); |
| } catch (Exception nfe) { |
| oneChildNode.setQuantityMultiplier(BigDecimal.ONE); |
| } |
| try { |
| BigDecimal percScrapFactor = node.getBigDecimal("scrapFactor"); |
| |
| // A negative scrap factor is a salvage factor |
| BigDecimal bdHundred = new BigDecimal("100"); |
| if (percScrapFactor.compareTo(bdHundred.negate()) > 0 && percScrapFactor.compareTo(bdHundred) < 0) { |
| percScrapFactor = BigDecimal.ONE.add(percScrapFactor.movePointLeft(2)); |
| } else { |
| Debug.logWarning("A scrap factor of [" + percScrapFactor + "] was ignored", module); |
| percScrapFactor = BigDecimal.ONE; |
| } |
| oneChildNode.setScrapFactor(percScrapFactor); |
| } catch (Exception nfe) { |
| oneChildNode.setScrapFactor(BigDecimal.ONE); |
| } |
| BOMNode newNode = oneChildNode; |
| // CONFIGURATOR |
| if (oneChildNode.isVirtual()) { |
| // If the part is VIRTUAL and |
| // productFeatures and productPartRules are not null |
| // we have to substitute the part with the right part's variant |
| List<GenericValue> productPartRules = EntityQuery.use(delegator).from("ProductManufacturingRule") |
| .where("productId", productIdForRules, |
| "productIdFor", node.get("productId"), |
| "productIdIn", node.get("productIdTo")) |
| .filterByDate(inDate).queryList(); |
| if (substitutedNode != null) { |
| productPartRules.addAll(EntityQuery.use(delegator).from("ProductManufacturingRule") |
| .where("productId", productIdForRules, |
| "productIdFor", substitutedNode.getProduct().get("productId"), |
| "productIdIn", node.get("productIdTo")) |
| .filterByDate(inDate).queryList()); |
| } |
| newNode = substituteNode(oneChildNode, productFeatures, productPartRules); |
| if (newNode.equals(oneChildNode)) { |
| // If no substitution has been done (no valid rule applied), |
| // we try to search for a generic link-rule |
| List<GenericValue> genericLinkRules = EntityQuery.use(delegator).from("ProductManufacturingRule") |
| .where("productIdFor", node.get("productId"), |
| "productIdIn", node.get("productIdTo")) |
| .filterByDate(inDate).queryList(); |
| if (substitutedNode != null) { |
| genericLinkRules.addAll(EntityQuery.use(delegator).from("ProductManufacturingRule") |
| .where("productIdFor", substitutedNode.getProduct().get("productId"), |
| "productIdIn", node.get("productIdTo")) |
| .filterByDate(inDate).queryList()); |
| } |
| newNode = substituteNode(oneChildNode, productFeatures, genericLinkRules); |
| if (newNode.equals(oneChildNode)) { |
| // If no substitution has been done (no valid rule applied), |
| // we try to search for a generic node-rule |
| List<GenericValue> genericNodeRules = EntityQuery.use(delegator).from("ProductManufacturingRule") |
| .where("productIdIn", node.get("productIdTo")) |
| .orderBy("ruleSeqId") |
| .filterByDate(inDate).queryList(); |
| newNode = null; |
| newNode = substituteNode(oneChildNode, productFeatures, genericNodeRules); |
| if (newNode.equals(oneChildNode)) { |
| // If no substitution has been done (no valid rule applied), |
| // we try to set the default (first) node-substitution |
| if (UtilValidate.isNotEmpty(genericNodeRules)) { |
| // FIXME |
| //... |
| } |
| // ----------------------------------------------------------- |
| // We try to apply directly the selected features |
| if (newNode.equals(oneChildNode)) { |
| Map<String, String> selectedFeatures = new HashMap<String, String>(); |
| if (productFeatures != null) { |
| GenericValue feature = null; |
| for (int j = 0; j < productFeatures.size(); j++) { |
| feature = productFeatures.get(j); |
| selectedFeatures.put(feature.getString("productFeatureTypeId"), feature.getString("productFeatureId")); // FIXME |
| } |
| } |
| |
| if (selectedFeatures.size() > 0) { |
| Map<String, Object> context = new HashMap<String, Object>(); |
| context.put("productId", node.get("productIdTo")); |
| context.put("selectedFeatures", selectedFeatures); |
| Map<String, Object> storeResult = null; |
| GenericValue variantProduct = null; |
| try { |
| storeResult = dispatcher.runSync("getProductVariant", context); |
| List<GenericValue> variantProducts = UtilGenerics.checkList(storeResult.get("products")); |
| if (variantProducts.size() == 1) { |
| variantProduct = variantProducts.get(0); |
| } |
| } catch (GenericServiceException e) { |
| Debug.logError("Error calling getProductVariant service " + e.getMessage(), module); |
| } |
| if (variantProduct != null) { |
| newNode = new BOMNode(variantProduct, dispatcher, userLogin); |
| newNode.setTree(tree); |
| newNode.setSubstitutedNode(oneChildNode); |
| newNode.setQuantityMultiplier(oneChildNode.getQuantityMultiplier()); |
| newNode.setScrapFactor(oneChildNode.getScrapFactor()); |
| newNode.setProductAssoc(oneChildNode.getProductAssoc()); |
| } |
| } |
| |
| } |
| // ----------------------------------------------------------- |
| } |
| } |
| } |
| } // end of if (isVirtual()) |
| return newNode; |
| } |
| |
| protected void loadParents(String partBomTypeId, Date inDate, List<GenericValue> productFeatures) throws GenericEntityException { |
| if (product == null) { |
| throw new GenericEntityException("product is null"); |
| } |
| // If the date is null, set it to today. |
| if (inDate == null) inDate = new Date(); |
| |
| bomTypeId = partBomTypeId; |
| List<GenericValue> rows = EntityQuery.use(delegator).from("ProductAssoc") |
| .where("productIdTo", product.get("productId"), |
| "productAssocTypeId", partBomTypeId) |
| .orderBy("sequenceNum") |
| .filterByDate(inDate).queryList(); |
| if ((UtilValidate.isEmpty(rows)) && substitutedNode != null) { |
| // If no parent is found and this is a substituted node |
| // we try to search for substituted node's parents. |
| rows = EntityQuery.use(delegator).from("ProductAssoc") |
| .where("productIdTo", substitutedNode.getProduct().get("productId"), |
| "productAssocTypeId", partBomTypeId) |
| .orderBy("sequenceNum") |
| .filterByDate(inDate).queryList(); |
| } |
| children = new LinkedList<GenericValue>(); |
| children.addAll(rows); |
| childrenNodes = new LinkedList<BOMNode>(); |
| |
| BOMNode oneChildNode = null; |
| for (GenericValue oneChild : children) { |
| oneChildNode = new BOMNode(oneChild.getString("productId"), delegator, dispatcher, userLogin); |
| // Configurator |
| // If the node is null this means that the node has been discarded by the rules. |
| if (oneChildNode != null) { |
| oneChildNode.setParentNode(this); |
| oneChildNode.setTree(tree); |
| oneChildNode.loadParents(partBomTypeId, inDate, productFeatures); |
| } |
| childrenNodes.add(oneChildNode); |
| } |
| } |
| |
| |
| /** Getter for property parentNode. |
| * @return Value of property parentNode. |
| * |
| */ |
| public BOMNode getParentNode() { |
| return parentNode; |
| } |
| |
| public BOMNode getRootNode() { |
| return (parentNode != null? getParentNode(): this); |
| } |
| /** Setter for property parentNode. |
| * @param parentNode New value of property parentNode. |
| * |
| */ |
| public void setParentNode(BOMNode parentNode) { |
| this.parentNode = parentNode; |
| } |
| // ------------------------------------ |
| // Method used for TEST and DEBUG purposes |
| public void print(StringBuffer sb, BigDecimal quantity, int depth) { |
| for (int i = 0; i < depth; i++) { |
| sb.append("<b> * </b>"); |
| } |
| sb.append(product.get("productId")); |
| sb.append(" - "); |
| sb.append(quantity); |
| GenericValue oneChild = null; |
| BOMNode oneChildNode = null; |
| depth++; |
| for (int i = 0; i < children.size(); i++) { |
| oneChild = children.get(i); |
| BigDecimal bomQuantity = BigDecimal.ZERO; |
| try { |
| bomQuantity = oneChild.getBigDecimal("quantity"); |
| } catch (Exception exc) { |
| bomQuantity = BigDecimal.ONE; |
| } |
| oneChildNode = childrenNodes.get(i); |
| sb.append("<br/>"); |
| if (oneChildNode != null) { |
| oneChildNode.print(sb, quantity.multiply(bomQuantity), depth); |
| } |
| } |
| } |
| |
| public void print(List<BOMNode> arr, BigDecimal quantity, int depth, boolean excludeWIPs) { |
| // Now we set the depth and quantity of the current node |
| // in this breakdown. |
| this.depth = depth; |
| String serviceName = null; |
| if (this.productAssoc != null && this.productAssoc.getString("estimateCalcMethod") != null) { |
| try { |
| GenericValue genericService = productAssoc.getRelatedOne("CustomMethod", false); |
| if (genericService != null && genericService.getString("customMethodName") != null) { |
| serviceName = genericService.getString("customMethodName"); |
| } |
| } catch (Exception exc) { |
| } |
| } |
| if (serviceName != null) { |
| Map<String, Object> resultContext = null; |
| Map<String, Object> arguments = UtilMisc.<String, Object>toMap("neededQuantity", quantity.multiply(quantityMultiplier), "amount", tree != null ? tree.getRootAmount() : BigDecimal.ZERO); |
| BigDecimal width = null; |
| if (getProduct().get("productWidth") != null) { |
| width = getProduct().getBigDecimal("productWidth"); |
| } |
| if (width == null) { |
| width = BigDecimal.ZERO; |
| } |
| arguments.put("width", width); |
| Map<String, Object> inputContext = UtilMisc.<String, Object>toMap("arguments", arguments, "userLogin", userLogin); |
| try { |
| resultContext = dispatcher.runSync(serviceName, inputContext); |
| BigDecimal calcQuantity = (BigDecimal)resultContext.get("quantity"); |
| if (calcQuantity != null) { |
| this.quantity = calcQuantity; |
| } |
| } catch (GenericServiceException e) { |
| Debug.logError(e, "Problem calling the " + serviceName + " service (called by the createManufacturingOrder service)", module); |
| } |
| } else { |
| this.quantity = quantity.multiply(quantityMultiplier).multiply(scrapFactor); |
| } |
| // First of all we visit the current node. |
| arr.add(this); |
| // Now (recursively) we visit the children. |
| BOMNode oneChildNode = null; |
| depth++; |
| for (int i = 0; i < children.size(); i++) { |
| oneChildNode = childrenNodes.get(i); |
| if (excludeWIPs && "WIP".equals(oneChildNode.getProduct().getString("productTypeId"))) { |
| continue; |
| } |
| if (oneChildNode != null) { |
| oneChildNode.print(arr, this.quantity, depth, excludeWIPs); |
| } |
| } |
| } |
| |
| public void getProductsInPackages(List<BOMNode> arr, BigDecimal quantity, int depth, boolean excludeWIPs) { |
| // Now we set the depth and quantity of the current node |
| // in this breakdown. |
| this.depth = depth; |
| this.quantity = quantity.multiply(quantityMultiplier).multiply(scrapFactor); |
| // First of all we visit the current node. |
| if (this.getProduct().getString("defaultShipmentBoxTypeId") != null) { |
| arr.add(this); |
| } else { |
| BOMNode oneChildNode = null; |
| depth++; |
| for (int i = 0; i < children.size(); i++) { |
| oneChildNode = childrenNodes.get(i); |
| if (excludeWIPs && "WIP".equals(oneChildNode.getProduct().getString("productTypeId"))) { |
| continue; |
| } |
| if (oneChildNode != null) { |
| oneChildNode.getProductsInPackages(arr, this.quantity, depth, excludeWIPs); |
| } |
| } |
| } |
| } |
| |
| public void sumQuantity(Map<String, BOMNode> nodes) { |
| // First of all, we try to fetch a node with the same partId |
| BOMNode sameNode = nodes.get(product.getString("productId")); |
| // If the node is not found we create a new node for the current product |
| if (sameNode == null) { |
| sameNode = new BOMNode(product, dispatcher, userLogin); |
| nodes.put(product.getString("productId"), sameNode); |
| } |
| // Now we add the current quantity to the node |
| sameNode.setQuantity(sameNode.getQuantity().add(quantity)); |
| // Now (recursively) we visit the children. |
| BOMNode oneChildNode = null; |
| for (int i = 0; i < childrenNodes.size(); i++) { |
| oneChildNode = childrenNodes.get(i); |
| if (oneChildNode != null) { |
| oneChildNode.sumQuantity(nodes); |
| } |
| } |
| } |
| |
| public Map<String, Object> createManufacturingOrder(String facilityId, Date date, String workEffortName, String description, String routingId, String orderId, String orderItemSeqId, String shipGroupSeqId, String shipmentId, boolean useSubstitute, boolean ignoreSupplierProducts) throws GenericEntityException { |
| String productionRunId = null; |
| Timestamp endDate = null; |
| if (isManufactured(ignoreSupplierProducts)) { |
| BOMNode oneChildNode = null; |
| List<String> childProductionRuns = new LinkedList<String>(); |
| Timestamp maxEndDate = null; |
| for (int i = 0; i < childrenNodes.size(); i++) { |
| oneChildNode = childrenNodes.get(i); |
| if (oneChildNode != null) { |
| Map<String, Object> tmpResult = oneChildNode.createManufacturingOrder(facilityId, date, null, null, null, null, null, shipGroupSeqId, shipmentId, false, false); |
| String childProductionRunId = (String)tmpResult.get("productionRunId"); |
| Timestamp childEndDate = (Timestamp)tmpResult.get("endDate"); |
| if (maxEndDate == null) { |
| maxEndDate = childEndDate; |
| } |
| if (childEndDate != null && maxEndDate.compareTo(childEndDate) < 0) { |
| maxEndDate = childEndDate; |
| } |
| |
| if (childProductionRunId != null) { |
| childProductionRuns.add(childProductionRunId); |
| } |
| } |
| } |
| |
| Timestamp startDate = UtilDateTime.toTimestamp(UtilDateTime.toDateTimeString(date)); |
| Map<String, Object> serviceContext = new HashMap<String, Object>(); |
| if (!useSubstitute) { |
| serviceContext.put("productId", getProduct().getString("productId")); |
| serviceContext.put("facilityId", getProduct().getString("facilityId")); |
| } else { |
| serviceContext.put("productId", getSubstitutedNode().getProduct().getString("productId")); |
| serviceContext.put("facilityId", getSubstitutedNode().getProduct().getString("facilityId")); |
| } |
| if (UtilValidate.isNotEmpty(facilityId)) { |
| serviceContext.put("facilityId", facilityId); |
| } |
| if (UtilValidate.isNotEmpty(workEffortName)) { |
| serviceContext.put("workEffortName", workEffortName); |
| } |
| if (UtilValidate.isNotEmpty(description)) { |
| serviceContext.put("description", description); |
| } |
| if (UtilValidate.isNotEmpty(routingId)) { |
| serviceContext.put("routingId", routingId); |
| } |
| if (UtilValidate.isNotEmpty(shipmentId) && UtilValidate.isEmpty(workEffortName)) { |
| serviceContext.put("workEffortName", "SP_" + shipmentId + "_" + serviceContext.get("productId")); |
| } |
| |
| serviceContext.put("pRQuantity", getQuantity()); |
| if (UtilValidate.isNotEmpty(maxEndDate)) { |
| serviceContext.put("startDate", maxEndDate); |
| } else { |
| serviceContext.put("startDate", startDate); |
| } |
| serviceContext.put("userLogin", userLogin); |
| Map<String, Object> resultService = null; |
| try { |
| resultService = dispatcher.runSync("createProductionRun", serviceContext); |
| productionRunId = (String)resultService.get("productionRunId"); |
| endDate = (Timestamp)resultService.get("estimatedCompletionDate"); |
| } catch (GenericServiceException e) { |
| Debug.logError("Problem calling the createProductionRun service", module); |
| } |
| try { |
| if (productionRunId != null) { |
| if (orderId != null && orderItemSeqId != null) { |
| delegator.create("WorkOrderItemFulfillment", UtilMisc.toMap("workEffortId", productionRunId, "orderId", orderId, "orderItemSeqId", orderItemSeqId, "shipGroupSeqId", shipGroupSeqId)); |
| } |
| for (int i = 0; i < childProductionRuns.size(); i++) { |
| delegator.create("WorkEffortAssoc", UtilMisc.toMap("workEffortIdFrom", childProductionRuns.get(i), "workEffortIdTo", productionRunId, "workEffortAssocTypeId", "WORK_EFF_PRECEDENCY", "fromDate", startDate)); |
| } |
| } |
| } catch (GenericEntityException e) { |
| Debug.logError(e, "Problem calling the getManufacturingComponents service", module); |
| } |
| } |
| return UtilMisc.toMap("productionRunId", productionRunId, "endDate", endDate); |
| } |
| |
| public Timestamp getStartDate(String facilityId, Timestamp requiredBydate, boolean allNodes) { |
| Timestamp minStartDate = requiredBydate; |
| if ("WIP".equals(getProduct().getString("productTypeId")) || allNodes) { |
| ProposedOrder proposedOrder = new ProposedOrder(getProduct(), facilityId, facilityId, true, requiredBydate, getQuantity()); |
| proposedOrder.calculateStartDate(0, null, delegator, dispatcher, userLogin); |
| Timestamp startDate = proposedOrder.getRequirementStartDate(); |
| minStartDate = startDate; |
| for (int i = 0; i < childrenNodes.size(); i++) { |
| BOMNode oneChildNode = childrenNodes.get(i); |
| if (oneChildNode != null) { |
| Timestamp childStartDate = oneChildNode.getStartDate(facilityId, startDate, false); |
| if (childStartDate.compareTo(minStartDate) < 0) { |
| minStartDate = childStartDate; |
| } |
| } |
| } |
| } |
| return minStartDate; |
| } |
| |
| /** |
| * Returns false if the product of this BOM Node is of type "WIP" or if it has no ProductFacility records defined for it, |
| * meaning that no active stock targets are set for this product. |
| */ |
| public boolean isWarehouseManaged(String facilityId) { |
| boolean isWarehouseManaged = false; |
| try { |
| if ("WIP".equals(getProduct().getString("productTypeId"))) { |
| return false; |
| } |
| List<GenericValue> pfs = null; |
| if (UtilValidate.isEmpty(facilityId)) { |
| pfs = getProduct().getRelated("ProductFacility", null, null, true); |
| } else { |
| pfs = getProduct().getRelated("ProductFacility", UtilMisc.toMap("facilityId", facilityId), null, true); |
| } |
| if (UtilValidate.isEmpty(pfs)) { |
| if (getSubstitutedNode() != null && getSubstitutedNode().getProduct() != null) { |
| if (UtilValidate.isEmpty(facilityId)) { |
| pfs = getSubstitutedNode().getProduct().getRelated("ProductFacility", null, null, true); |
| } else { |
| pfs = getSubstitutedNode().getProduct().getRelated("ProductFacility", UtilMisc.toMap("facilityId", facilityId), null, true); |
| } |
| } |
| } |
| if (UtilValidate.isNotEmpty(pfs)) { |
| for (int i = 0; i < pfs.size(); i++) { |
| GenericValue pf = pfs.get(i); |
| if (UtilValidate.isNotEmpty(pf.get("minimumStock")) && UtilValidate.isNotEmpty(pf.get("reorderQuantity"))) { |
| isWarehouseManaged = true; |
| break; |
| } |
| } |
| } |
| } catch (GenericEntityException gee) { |
| Debug.logError("Problem in BOMNode.isWarehouseManaged()", module); |
| } |
| return isWarehouseManaged; |
| } |
| |
| /** |
| * A part is considered manufactured if it has child nodes AND unless ignoreSupplierProducts is set, if it also has no unexpired SupplierProducts defined |
| * @param ignoreSupplierProducts |
| * @return return if a part is considered manufactured |
| */ |
| public boolean isManufactured(boolean ignoreSupplierProducts) { |
| List<GenericValue> supplierProducts = null; |
| try { |
| supplierProducts = product.getRelated("SupplierProduct", UtilMisc.toMap("supplierPrefOrderId", "10_MAIN_SUPPL"), UtilMisc.toList("minimumOrderQuantity"), false); |
| } catch (GenericEntityException gee) { |
| Debug.logError("Problem in BOMNode.isManufactured()", module); |
| } |
| supplierProducts = EntityUtil.filterByDate(supplierProducts, UtilDateTime.nowTimestamp(), "availableFromDate", "availableThruDate", true); |
| return childrenNodes.size() > 0 && (ignoreSupplierProducts || UtilValidate.isEmpty(supplierProducts)); |
| } |
| |
| /** |
| * By default, a part is manufactured if it has child nodes and it has NO SupplierProducts defined |
| * @return return if a part is manufactured |
| */ |
| public boolean isManufactured() { |
| return isManufactured(false); |
| } |
| |
| public boolean isVirtual() { |
| return (product.get("isVirtual") != null? product.get("isVirtual").equals("Y"): false); |
| } |
| |
| public void isConfigured(List<BOMNode> arr) { |
| // First of all we visit the current node. |
| if (isVirtual()) { |
| arr.add(this); |
| } |
| // Now (recursively) we visit the children. |
| BOMNode oneChildNode = null; |
| for (int i = 0; i < children.size(); i++) { |
| oneChildNode = childrenNodes.get(i); |
| if (oneChildNode != null) { |
| oneChildNode.isConfigured(arr); |
| } |
| } |
| } |
| |
| /** Getter for property quantity. |
| * @return Value of property quantity. |
| * |
| */ |
| public BigDecimal getQuantity() { |
| return quantity; |
| } |
| |
| public void setQuantity(BigDecimal quantity) { |
| this.quantity = quantity; |
| } |
| |
| /** Getter for property depth. |
| * @return Value of property depth. |
| * |
| */ |
| |
| public int getDepth() { |
| return depth; |
| } |
| |
| public GenericValue getProduct() { |
| return product; |
| } |
| |
| /** Getter for property substitutedNode. |
| * @return Value of property substitutedNode. |
| * |
| */ |
| public BOMNode getSubstitutedNode() { |
| return substitutedNode; |
| } |
| |
| /** Setter for property substitutedNode. |
| * @param substitutedNode New value of property substitutedNode. |
| * |
| */ |
| public void setSubstitutedNode(BOMNode substitutedNode) { |
| this.substitutedNode = substitutedNode; |
| } |
| |
| public String getRootProductForRules() { |
| return getParentNode().getProductForRules(); |
| } |
| |
| /** Getter for property productForRules. |
| * @return Value of property productForRules. |
| * |
| */ |
| public String getProductForRules() { |
| return productForRules; |
| } |
| |
| /** Setter for property productForRules. |
| * @param productForRules New value of property productForRules. |
| * |
| */ |
| public void setProductForRules(String productForRules) { |
| this.productForRules = productForRules; |
| } |
| |
| /** Getter for property bomTypeId. |
| * @return Value of property bomTypeId. |
| * |
| */ |
| public java.lang.String getBomTypeId() { |
| return bomTypeId; |
| } |
| |
| /** Getter for property quantityMultiplier. |
| * @return Value of property quantityMultiplier. |
| * |
| */ |
| public BigDecimal getQuantityMultiplier() { |
| return quantityMultiplier; |
| } |
| |
| /** Setter for property quantityMultiplier. |
| * @param quantityMultiplier New value of property quantityMultiplier. |
| * |
| */ |
| public void setQuantityMultiplier(BigDecimal quantityMultiplier) { |
| if (quantityMultiplier != null) { |
| this.quantityMultiplier = quantityMultiplier; |
| } |
| } |
| |
| /** Getter for property ruleApplied. |
| * @return Value of property ruleApplied. |
| * |
| */ |
| public org.apache.ofbiz.entity.GenericValue getRuleApplied() { |
| return ruleApplied; |
| } |
| |
| /** Setter for property ruleApplied. |
| * @param ruleApplied New value of property ruleApplied. |
| * |
| */ |
| public void setRuleApplied(org.apache.ofbiz.entity.GenericValue ruleApplied) { |
| this.ruleApplied = ruleApplied; |
| } |
| |
| /** Getter for property scrapFactor. |
| * @return Value of property scrapFactor. |
| * |
| */ |
| public BigDecimal getScrapFactor() { |
| return scrapFactor; |
| } |
| |
| /** Setter for property scrapFactor. |
| * @param scrapFactor New value of property scrapFactor. |
| * |
| */ |
| public void setScrapFactor(BigDecimal scrapFactor) { |
| if (scrapFactor != null) { |
| this.scrapFactor = scrapFactor; |
| } |
| } |
| |
| /** Getter for property childrenNodes. |
| * @return Value of property childrenNodes. |
| * |
| */ |
| public List<BOMNode> getChildrenNodes() { |
| return childrenNodes; |
| } |
| |
| /** Setter for property childrenNodes. |
| * @param childrenNodes New value of property childrenNodes. |
| * |
| */ |
| public void setChildrenNodes(List<BOMNode> childrenNodes) { |
| this.childrenNodes = childrenNodes; |
| } |
| |
| /** Getter for property productAssoc. |
| * @return Value of property productAssoc. |
| * |
| */ |
| public org.apache.ofbiz.entity.GenericValue getProductAssoc() { |
| return productAssoc; |
| } |
| |
| /** Setter for property productAssoc. |
| * @param productAssoc New value of property productAssoc. |
| * |
| */ |
| public void setProductAssoc(org.apache.ofbiz.entity.GenericValue productAssoc) { |
| this.productAssoc = productAssoc; |
| } |
| |
| public void setTree(BOMTree tree) { |
| this.tree = tree; |
| } |
| |
| public BOMTree getTree() { |
| return tree; |
| } |
| |
| } |
| |