blob: 4e28e3eedea7bfe8bb43807fa57585ede167d253 [file] [log] [blame]
/*******************************************************************************
* 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.ofbiz.manufacturing.bom;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import javolution.util.FastList;
import javolution.util.FastMap;
import org.ofbiz.base.util.Debug;
import org.ofbiz.base.util.UtilGenerics;
import org.ofbiz.base.util.UtilMisc;
import org.ofbiz.base.util.UtilProperties;
import org.ofbiz.base.util.UtilValidate;
import org.ofbiz.entity.Delegator;
import org.ofbiz.entity.GenericEntityException;
import org.ofbiz.entity.GenericValue;
import org.ofbiz.entity.util.EntityUtil;
import org.ofbiz.order.order.OrderReadHelper;
import org.ofbiz.service.DispatchContext;
import org.ofbiz.service.GenericServiceException;
import org.ofbiz.service.LocalDispatcher;
import org.ofbiz.service.ServiceUtil;
/** Bills of Materials' services implementation.
* These services are useful when dealing with product's
* bills of materials.
*/
public class BOMServices {
public static final String module = BOMServices.class.getName();
public static final String resource = "ManufacturingUiLabels";
/** Returns the product's low level code (llc) i.e. the maximum depth
* in which the productId can be found in any of the
* bills of materials of bomType type.
* If the bomType input field is not passed then the depth is searched for all the bom types and the lowest depth is returned.
* @param dctx the dispatch context
* @param context the context
* @return returns the product's low level code (llc) i.e. the maximum depth
*/
public static Map<String, Object> getMaxDepth(DispatchContext dctx, Map<String, ? extends Object> context) {
Map<String, Object> result = FastMap.newInstance();
Delegator delegator = dctx.getDelegator();
String productId = (String) context.get("productId");
String fromDateStr = (String) context.get("fromDate");
String bomType = (String) context.get("bomType");
Locale locale = (Locale) context.get("locale");
Date fromDate = null;
if (UtilValidate.isNotEmpty(fromDateStr)) {
try {
fromDate = Timestamp.valueOf(fromDateStr);
} catch (Exception e) {
}
}
if (fromDate == null) {
fromDate = new Date();
}
List<String> bomTypes = FastList.newInstance();
if (bomType == null) {
try {
List<GenericValue> bomTypesValues = delegator.findByAnd("ProductAssocType", UtilMisc.toMap("parentTypeId", "PRODUCT_COMPONENT"));
Iterator<GenericValue> bomTypesValuesIt = bomTypesValues.iterator();
while (bomTypesValuesIt.hasNext()) {
bomTypes.add((bomTypesValuesIt.next()).getString("productAssocTypeId"));
}
} catch (GenericEntityException gee) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "ManufacturingBomErrorRunningMaxDethAlgorithm", UtilMisc.toMap("errorString", gee.getMessage()), locale));
}
} else {
bomTypes.add(bomType);
}
int depth = 0;
int maxDepth = 0;
Iterator<String> bomTypesIt = bomTypes.iterator();
try {
while (bomTypesIt.hasNext()) {
String oneBomType = bomTypesIt.next();
depth = BOMHelper.getMaxDepth(productId, oneBomType, fromDate, delegator);
if (depth > maxDepth) {
maxDepth = depth;
}
}
} catch (GenericEntityException gee) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "ManufacturingBomErrorRunningMaxDethAlgorithm", UtilMisc.toMap("errorString", gee.getMessage()), locale));
}
result.put("depth", Long.valueOf(maxDepth));
return result;
}
/** Updates the product's low level code (llc)
* Given a product id, computes and updates the product's low level code (field billOfMaterialLevel in Product entity).
* It also updates the llc of all the product's descendants.
* For the llc only the manufacturing bom ("MANUF_COMPONENT") is considered.
* @param dctx the distach context
* @param context the context
* @return the results of the updates the product's low level code
*/
public static Map<String, Object> updateLowLevelCode(DispatchContext dctx, Map<String, ? extends Object> context) {
Map<String, Object> result = FastMap.newInstance();
Delegator delegator = dctx.getDelegator();
LocalDispatcher dispatcher = dctx.getDispatcher();
String productId = (String) context.get("productIdTo");
Boolean alsoComponents = (Boolean) context.get("alsoComponents");
Locale locale = (Locale) context.get("locale");
if (alsoComponents == null) {
alsoComponents = Boolean.TRUE;
}
Boolean alsoVariants = (Boolean) context.get("alsoVariants");
if (alsoVariants == null) {
alsoVariants = Boolean.TRUE;
}
Long llc = null;
try {
GenericValue product = delegator.findByPrimaryKey("Product", UtilMisc.toMap("productId", productId));
Map<String, Object> depthResult = dispatcher.runSync("getMaxDepth",
UtilMisc.toMap("productId", productId, "bomType", "MANUF_COMPONENT"));
llc = (Long)depthResult.get("depth");
// If the product is a variant of a virtual, then the billOfMaterialLevel cannot be
// lower than the billOfMaterialLevel of the virtual product.
List<GenericValue> virtualProducts = delegator.findByAnd("ProductAssoc",
UtilMisc.toMap("productIdTo", productId, "productAssocTypeId", "PRODUCT_VARIANT"));
virtualProducts = EntityUtil.filterByDate(virtualProducts);
int virtualMaxDepth = 0;
Iterator<GenericValue> virtualProductsIt = virtualProducts.iterator();
while (virtualProductsIt.hasNext()) {
int virtualDepth = 0;
GenericValue oneVirtualProductAssoc = virtualProductsIt.next();
GenericValue virtualProduct = delegator.findByPrimaryKey("Product", UtilMisc.toMap("productId", oneVirtualProductAssoc.getString("productId")));
if (virtualProduct.get("billOfMaterialLevel") != null) {
virtualDepth = virtualProduct.getLong("billOfMaterialLevel").intValue();
} else {
virtualDepth = 0;
}
if (virtualDepth > virtualMaxDepth) {
virtualMaxDepth = virtualDepth;
}
}
if (virtualMaxDepth > llc.intValue()) {
llc = Long.valueOf(virtualMaxDepth);
}
product.set("billOfMaterialLevel", llc);
product.store();
if (alsoComponents.booleanValue()) {
Map<String, Object> treeResult = dispatcher.runSync("getBOMTree", UtilMisc.toMap("productId", productId, "bomType", "MANUF_COMPONENT"));
BOMTree tree = (BOMTree)treeResult.get("tree");
List<BOMNode> products = FastList.newInstance();
tree.print(products, llc.intValue());
for (int i = 0; i < products.size(); i++) {
BOMNode oneNode = products.get(i);
GenericValue oneProduct = oneNode.getProduct();
int lev = 0;
if (oneProduct.get("billOfMaterialLevel") != null) {
lev = oneProduct.getLong("billOfMaterialLevel").intValue();
}
if (lev < oneNode.getDepth()) {
oneProduct.set("billOfMaterialLevel", Long.valueOf(oneNode.getDepth()));
oneProduct.store();
}
}
}
if (alsoVariants.booleanValue()) {
List<GenericValue> variantProducts = delegator.findByAnd("ProductAssoc",
UtilMisc.toMap("productId", productId, "productAssocTypeId", "PRODUCT_VARIANT"));
variantProducts = EntityUtil.filterByDate(variantProducts, true);
Iterator<GenericValue> variantProductsIt = variantProducts.iterator();
while (variantProductsIt.hasNext()) {
GenericValue oneVariantProductAssoc = variantProductsIt.next();
GenericValue variantProduct = delegator.findByPrimaryKey("Product", UtilMisc.toMap("productId", oneVariantProductAssoc.getString("productId")));
variantProduct.set("billOfMaterialLevel", llc);
variantProduct.store();
}
}
} catch (Exception e) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "ManufacturingBomErrorRunningUpdateLowLevelCode", UtilMisc.toMap("errorString", e.getMessage()), locale));
}
result.put("lowLevelCode", llc);
return result;
}
/** Updates the product's low level code (llc) for all the products in the Product entity.
* For the llc only the manufacturing bom ("MANUF_COMPONENT") is considered.
* @param dctx the distach context
* @param context the context
* @return the results of the updates the product's low level code
*/
public static Map<String, Object> initLowLevelCode(DispatchContext dctx, Map<String, ? extends Object> context) {
Map<String, Object> result = FastMap.newInstance();
Delegator delegator = dctx.getDelegator();
LocalDispatcher dispatcher = dctx.getDispatcher();
Locale locale = (Locale) context.get("locale");
try {
List<GenericValue> products = delegator.findList("Product", null, null,
UtilMisc.toList("isVirtual DESC"), null, false);
Iterator<GenericValue> productsIt = products.iterator();
Long zero = Long.valueOf(0);
List<GenericValue> allProducts = FastList.newInstance();
while (productsIt.hasNext()) {
GenericValue product = productsIt.next();
product.set("billOfMaterialLevel", zero);
allProducts.add(product);
}
delegator.storeAll(allProducts);
Debug.logInfo("Low Level Code set to 0 for all the products", module);
productsIt = products.iterator();
while (productsIt.hasNext()) {
GenericValue product = productsIt.next();
try {
Map<String, Object> depthResult = dispatcher.runSync("updateLowLevelCode", UtilMisc.<String, Object>toMap("productIdTo", product.getString("productId"), "alsoComponents", Boolean.valueOf(false), "alsoVariants", Boolean.valueOf(false)));
Debug.logInfo("Product [" + product.getString("productId") + "] Low Level Code [" + depthResult.get("lowLevelCode") + "]", module);
} catch (Exception exc) {
Debug.logWarning(exc.getMessage(), module);
}
}
// FIXME: also all the variants llc should be updated?
} catch (Exception e) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "ManufacturingBomErrorRunningInitLowLevelCode", UtilMisc.toMap("errorString", e.getMessage()), locale));
}
return result;
}
/** Returns the ProductAssoc generic value for a duplicate productIdKey
* ancestor if present, null otherwise.
* Useful to avoid loops when adding new assocs (components)
* to a bill of materials.
* @param dctx the distach context
* @param context the context
* @return returns the ProductAssoc generic value for a duplicate productIdKey ancestor if present
*/
public static Map<String, Object> searchDuplicatedAncestor(DispatchContext dctx, Map<String, ? extends Object> context) {
Map<String, Object> result = FastMap.newInstance();
Delegator delegator = dctx.getDelegator();
LocalDispatcher dispatcher = dctx.getDispatcher();
GenericValue userLogin = (GenericValue)context.get("userLogin");
Locale locale = (Locale) context.get("locale");
String productId = (String) context.get("productId");
String productIdKey = (String) context.get("productIdTo");
Timestamp fromDate = (Timestamp) context.get("fromDate");
String bomType = (String) context.get("productAssocTypeId");
if (fromDate == null) {
fromDate = Timestamp.valueOf((new Date()).toString());
}
GenericValue duplicatedProductAssoc = null;
try {
duplicatedProductAssoc = BOMHelper.searchDuplicatedAncestor(productId, productIdKey, bomType, fromDate, delegator, dispatcher, userLogin);
} catch (GenericEntityException gee) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "ManufacturingBomErrorRunningDuplicatedAncestorSearch", UtilMisc.toMap("errorString", gee.getMessage()), locale));
}
result.put("duplicatedProductAssoc", duplicatedProductAssoc);
return result;
}
/** It reads the product's bill of materials,
* if necessary configures it, and it returns
* an object (see {@link BOMTree}
* and {@link BOMNode}) that represents a
* configured bill of material tree.
* Useful for tree traversal (breakdown, explosion, implosion).
* @param dctx the distach context
* @param context the context
* @return return the bill of material tree
*/
public static Map<String, Object> getBOMTree(DispatchContext dctx, Map<String, ? extends Object> context) {
Map<String, Object> result = FastMap.newInstance();
Delegator delegator = dctx.getDelegator();
LocalDispatcher dispatcher = dctx.getDispatcher();
GenericValue userLogin = (GenericValue)context.get("userLogin");
String productId = (String) context.get("productId");
String fromDateStr = (String) context.get("fromDate");
String bomType = (String) context.get("bomType");
Integer type = (Integer) context.get("type");
BigDecimal quantity = (BigDecimal) context.get("quantity");
BigDecimal amount = (BigDecimal) context.get("amount");
Locale locale = (Locale) context.get("locale");
if (type == null) {
type = Integer.valueOf(0);
}
Date fromDate = null;
if (UtilValidate.isNotEmpty(fromDateStr)) {
try {
fromDate = Timestamp.valueOf(fromDateStr);
} catch (Exception e) {
}
}
if (fromDate == null) {
fromDate = new Date();
}
BOMTree tree = null;
try {
tree = new BOMTree(productId, bomType, fromDate, type.intValue(), delegator, dispatcher, userLogin);
} catch (GenericEntityException gee) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "ManufacturingBomErrorCreatingBillOfMaterialsTree", UtilMisc.toMap("errorString", gee.getMessage()), locale));
}
if (tree != null && quantity != null) {
tree.setRootQuantity(quantity);
}
if (tree != null && amount != null) {
tree.setRootAmount(amount);
}
result.put("tree", tree);
return result;
}
/** It reads the product's bill of materials,
* if necessary configures it, and it returns its (possibly configured) components in
* a List of {@link BOMNode}).
* @param dctx the distach context
* @param context the context
* @return return the list of manufacturing components
*/
public static Map<String, Object> getManufacturingComponents(DispatchContext dctx, Map<String, ? extends Object> context) {
Map<String, Object> result = FastMap.newInstance();
Delegator delegator = dctx.getDelegator();
LocalDispatcher dispatcher = dctx.getDispatcher();
GenericValue userLogin = (GenericValue)context.get("userLogin");
String productId = (String) context.get("productId");
BigDecimal quantity = (BigDecimal) context.get("quantity");
BigDecimal amount = (BigDecimal) context.get("amount");
String fromDateStr = (String) context.get("fromDate");
Boolean excludeWIPs = (Boolean) context.get("excludeWIPs");
Locale locale = (Locale) context.get("locale");
if (quantity == null) {
quantity = BigDecimal.ONE;
}
if (amount == null) {
amount = BigDecimal.ZERO;
}
Date fromDate = null;
if (UtilValidate.isNotEmpty(fromDateStr)) {
try {
fromDate = Timestamp.valueOf(fromDateStr);
} catch (Exception e) {
}
}
if (fromDate == null) {
fromDate = new Date();
}
if (excludeWIPs == null) {
excludeWIPs = Boolean.TRUE;
}
//
// Components
//
BOMTree tree = null;
List<BOMNode> components = FastList.newInstance();
try {
tree = new BOMTree(productId, "MANUF_COMPONENT", fromDate, BOMTree.EXPLOSION_SINGLE_LEVEL, delegator, dispatcher, userLogin);
tree.setRootQuantity(quantity);
tree.setRootAmount(amount);
tree.print(components, excludeWIPs.booleanValue());
if (components.size() > 0) components.remove(0);
} catch (GenericEntityException gee) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "ManufacturingBomErrorCreatingBillOfMaterialsTree", UtilMisc.toMap("errorString", gee.getMessage()), locale));
}
//
// Product routing
//
String workEffortId = null;
try {
Map<String, Object> routingInMap = UtilMisc.toMap("productId", productId, "ignoreDefaultRouting", "Y", "userLogin", userLogin);
Map<String, Object> routingOutMap = dispatcher.runSync("getProductRouting", routingInMap);
GenericValue routing = (GenericValue)routingOutMap.get("routing");
if (routing == null) {
// try to find a routing linked to the virtual product
routingInMap = UtilMisc.toMap("productId", tree.getRoot().getProduct().getString("productId"), "userLogin", userLogin);
routingOutMap = dispatcher.runSync("getProductRouting", routingInMap);
routing = (GenericValue)routingOutMap.get("routing");
}
if (routing != null) {
workEffortId = routing.getString("workEffortId");
}
} catch (GenericServiceException gse) {
Debug.logWarning(gse.getMessage(), module);
}
if (workEffortId != null) {
result.put("workEffortId", workEffortId);
}
result.put("components", components);
// also return a componentMap (useful in scripts and simple language code)
List<Map<String, Object>> componentsMap = FastList.newInstance();
Iterator<BOMNode> componentsIt = components.iterator();
while (componentsIt.hasNext()) {
Map<String, Object> componentMap = FastMap.newInstance();
BOMNode node = componentsIt.next();
componentMap.put("product", node.getProduct());
componentMap.put("quantity", node.getQuantity());
componentsMap.add(componentMap);
}
result.put("componentsMap", componentsMap);
return result;
}
public static Map<String, Object> getNotAssembledComponents(DispatchContext dctx, Map<String, ? extends Object> context) {
Map<String, Object> result = FastMap.newInstance();
Delegator delegator = dctx.getDelegator();
LocalDispatcher dispatcher = dctx.getDispatcher();
String productId = (String) context.get("productId");
BigDecimal quantity = (BigDecimal) context.get("quantity");
BigDecimal amount = (BigDecimal) context.get("amount");
String fromDateStr = (String) context.get("fromDate");
GenericValue userLogin = (GenericValue)context.get("userLogin");
Locale locale = (Locale) context.get("locale");
if (quantity == null) {
quantity = BigDecimal.ONE;
}
if (amount == null) {
amount = BigDecimal.ZERO;
}
Date fromDate = null;
if (UtilValidate.isNotEmpty(fromDateStr)) {
try {
fromDate = Timestamp.valueOf(fromDateStr);
} catch (Exception e) {
}
}
if (fromDate == null) {
fromDate = new Date();
}
BOMTree tree = null;
List<BOMNode> components = FastList.newInstance();
List<BOMNode> notAssembledComponents = FastList.newInstance();
try {
tree = new BOMTree(productId, "MANUF_COMPONENT", fromDate, BOMTree.EXPLOSION_MANUFACTURING, delegator, dispatcher, userLogin);
tree.setRootQuantity(quantity);
tree.setRootAmount(amount);
tree.print(components);
} catch (GenericEntityException gee) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "ManufacturingBomErrorCreatingBillOfMaterialsTree", UtilMisc.toMap("errorString", gee.getMessage()), locale));
}
Iterator<BOMNode> componentsIt = components.iterator();
while (componentsIt.hasNext()) {
BOMNode oneComponent = componentsIt.next();
if (!oneComponent.isManufactured()) {
notAssembledComponents.add(oneComponent);
}
}
result.put("notAssembledComponents" , notAssembledComponents);
return result;
}
// ---------------------------------------------
// Service for the Product (Shipment) component
//
public static Map<String, Object> createShipmentPackages(DispatchContext dctx, Map<String, ? extends Object> context) {
Map<String, Object> result = FastMap.newInstance();
Delegator delegator = dctx.getDelegator();
LocalDispatcher dispatcher = dctx.getDispatcher();
Locale locale = (Locale) context.get("locale");
GenericValue userLogin = (GenericValue)context.get("userLogin");
String shipmentId = (String) context.get("shipmentId");
try {
List<GenericValue> packages = delegator.findByAnd("ShipmentPackage", UtilMisc.toMap("shipmentId", shipmentId));
if (!UtilValidate.isEmpty(packages)) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "ManufacturingBomPackageAlreadyFound", locale));
}
} catch (GenericEntityException gee) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "ManufacturingBomErrorLoadingShipmentPackages", locale));
}
// ShipmentItems are loaded
List<GenericValue> shipmentItems = null;
try {
shipmentItems = delegator.findByAnd("ShipmentItem", UtilMisc.toMap("shipmentId", shipmentId));
} catch (GenericEntityException gee) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "ManufacturingBomErrorLoadingShipmentItems", locale));
}
Iterator<GenericValue> shipmentItemsIt = shipmentItems.iterator();
Map<String, Object> orderReadHelpers = FastMap.newInstance();
Map<String, Object> partyOrderShipments = FastMap.newInstance();
while (shipmentItemsIt.hasNext()) {
GenericValue shipmentItem = shipmentItemsIt.next();
// Get the OrderShipments
List<GenericValue> orderShipments = null;
try {
orderShipments = delegator.findByAnd("OrderShipment", UtilMisc.toMap("shipmentId", shipmentId, "shipmentItemSeqId", shipmentItem.getString("shipmentItemSeqId")));
} catch (GenericEntityException e) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "ManufacturingPackageConfiguratorError", locale));
}
GenericValue orderShipment = org.ofbiz.entity.util.EntityUtil.getFirst(orderShipments);
if (orderShipment != null && !orderReadHelpers.containsKey(orderShipment.getString("orderId"))) {
orderReadHelpers.put(orderShipment.getString("orderId"), new OrderReadHelper(delegator, orderShipment.getString("orderId")));
}
OrderReadHelper orderReadHelper = (OrderReadHelper)orderReadHelpers.get(orderShipment.getString("orderId"));
if (orderReadHelper != null) {
Map<String, Object> orderShipmentReadMap = UtilMisc.toMap("orderShipment", orderShipment, "orderReadHelper", orderReadHelper);
String partyId = (orderReadHelper.getPlacingParty() != null? orderReadHelper.getPlacingParty().getString("partyId"): null); // FIXME: is it the customer?
if (partyId != null) {
if (!partyOrderShipments.containsKey(partyId)) {
List<Map<String, Object>> orderShipmentReadMapList = FastList.newInstance();
partyOrderShipments.put(partyId, orderShipmentReadMapList);
}
List<Map<String, Object>> orderShipmentReadMapList = UtilGenerics.checkList(partyOrderShipments.get(partyId));
orderShipmentReadMapList.add(orderShipmentReadMap);
}
}
}
// For each party: try to expand the shipment item products
// (search for components that needs to be packaged).
for(Map.Entry<String, Object> partyOrderShipment : partyOrderShipments.entrySet()) {
List<Map<String, Object>> orderShipmentReadMapList = UtilGenerics.checkList(partyOrderShipment.getValue());
for (int i = 0; i < orderShipmentReadMapList.size(); i++) {
Map<String, Object> orderShipmentReadMap = UtilGenerics.checkMap(orderShipmentReadMapList.get(i));
GenericValue orderShipment = (GenericValue)orderShipmentReadMap.get("orderShipment");
OrderReadHelper orderReadHelper = (OrderReadHelper)orderShipmentReadMap.get("orderReadHelper");
GenericValue orderItem = orderReadHelper.getOrderItem(orderShipment.getString("orderItemSeqId"));
// getProductsInPackages
Map<String, Object> serviceContext = FastMap.newInstance();
serviceContext.put("productId", orderItem.getString("productId"));
serviceContext.put("quantity", orderShipment.getBigDecimal("quantity"));
Map<String, Object> resultService = null;
try {
resultService = dispatcher.runSync("getProductsInPackages", serviceContext);
} catch (GenericServiceException e) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "ManufacturingPackageConfiguratorError", locale));
}
List<BOMNode> productsInPackages = UtilGenerics.checkList(resultService.get("productsInPackages"));
if (productsInPackages.size() == 1) {
BOMNode root = productsInPackages.get(0);
String rootProductId = (root.getSubstitutedNode() != null? root.getSubstitutedNode().getProduct().getString("productId"): root.getProduct().getString("productId"));
if (orderItem.getString("productId").equals(rootProductId)) {
productsInPackages = null;
}
}
if (productsInPackages != null && productsInPackages.size() == 0) {
productsInPackages = null;
}
if (UtilValidate.isNotEmpty(productsInPackages)) {
orderShipmentReadMap.put("productsInPackages", productsInPackages);
}
}
}
// Group together products and components
// of the same box type.
Map<String, GenericValue> boxTypes = FastMap.newInstance();
for(Map.Entry<String, Object> partyOrderShipment : partyOrderShipments.entrySet()) {
Map<String, List<Map<String, Object>>> boxTypeContent = FastMap.newInstance();
List<Map<String, Object>> orderShipmentReadMapList = UtilGenerics.checkList(partyOrderShipment.getValue());
for (int i = 0; i < orderShipmentReadMapList.size(); i++) {
Map<String, Object> orderShipmentReadMap = UtilGenerics.checkMap(orderShipmentReadMapList.get(i));
GenericValue orderShipment = (GenericValue)orderShipmentReadMap.get("orderShipment");
OrderReadHelper orderReadHelper = (OrderReadHelper)orderShipmentReadMap.get("orderReadHelper");
List<BOMNode> productsInPackages = UtilGenerics.checkList(orderShipmentReadMap.get("productsInPackages"));
if (productsInPackages != null) {
// there are subcomponents:
// this is a multi package shipment item
for (int j = 0; j < productsInPackages.size(); j++) {
BOMNode component = productsInPackages.get(j);
Map<String, Object> boxTypeContentMap = FastMap.newInstance();
boxTypeContentMap.put("content", orderShipmentReadMap);
boxTypeContentMap.put("componentIndex", Integer.valueOf(j));
GenericValue product = component.getProduct();
String boxTypeId = product.getString("shipmentBoxTypeId");
if (boxTypeId != null) {
if (!boxTypes.containsKey(boxTypeId)) {
GenericValue boxType = null;
try {
boxType = delegator.findByPrimaryKey("ShipmentBoxType", UtilMisc.toMap("shipmentBoxTypeId", boxTypeId));
} catch (GenericEntityException e) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "ManufacturingPackageConfiguratorError", locale));
}
boxTypes.put(boxTypeId, boxType);
List<Map<String, Object>> box = FastList.newInstance();
boxTypeContent.put(boxTypeId, box);
}
List<Map<String, Object>> boxTypeContentList = UtilGenerics.checkList(boxTypeContent.get(boxTypeId));
boxTypeContentList.add(boxTypeContentMap);
}
}
} else {
// no subcomponents, the product has its own package:
// this is a single package shipment item
Map<String, Object> boxTypeContentMap = FastMap.newInstance();
boxTypeContentMap.put("content", orderShipmentReadMap);
GenericValue orderItem = orderReadHelper.getOrderItem(orderShipment.getString("orderItemSeqId"));
GenericValue product = null;
try {
product = orderItem.getRelatedOne("Product");
} catch (GenericEntityException e) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "ManufacturingPackageConfiguratorError", locale));
}
String boxTypeId = product.getString("shipmentBoxTypeId");
if (boxTypeId != null) {
if (!boxTypes.containsKey(boxTypeId)) {
GenericValue boxType = null;
try {
boxType = delegator.findByPrimaryKey("ShipmentBoxType", UtilMisc.toMap("shipmentBoxTypeId", boxTypeId));
} catch (GenericEntityException e) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "ManufacturingPackageConfiguratorError", locale));
}
boxTypes.put(boxTypeId, boxType);
List<Map<String, Object>> box = FastList.newInstance();
boxTypeContent.put(boxTypeId, box);
}
List<Map<String, Object>> boxTypeContentList = UtilGenerics.checkList(boxTypeContent.get(boxTypeId));
boxTypeContentList.add(boxTypeContentMap);
}
}
}
// The packages and package contents are created.
for (Map.Entry<String, List<Map<String, Object>>> boxTypeContentEntry : boxTypeContent.entrySet()) {
String boxTypeId = boxTypeContentEntry.getKey();
List<Map<String, Object>> contentList = UtilGenerics.checkList(boxTypeContentEntry.getValue());
GenericValue boxType = boxTypes.get(boxTypeId);
BigDecimal boxWidth = boxType.getBigDecimal("boxLength");
BigDecimal totalWidth = BigDecimal.ZERO;
if (boxWidth == null) {
boxWidth = BigDecimal.ZERO;
}
String shipmentPackageSeqId = null;
for (int i = 0; i < contentList.size(); i++) {
Map<String, Object> contentMap = UtilGenerics.checkMap(contentList.get(i));
Map<String, Object> content = UtilGenerics.checkMap(contentMap.get("content"));
OrderReadHelper orderReadHelper = (OrderReadHelper)content.get("orderReadHelper");
List<BOMNode> productsInPackages = UtilGenerics.checkList(content.get("productsInPackages"));
GenericValue orderShipment = (GenericValue)content.get("orderShipment");
GenericValue product = null;
BigDecimal quantity = BigDecimal.ZERO;
boolean subProduct = contentMap.containsKey("componentIndex");
if (subProduct) {
// multi package
Integer index = (Integer)contentMap.get("componentIndex");
BOMNode component = productsInPackages.get(index.intValue());
product = component.getProduct();
quantity = component.getQuantity();
} else {
// single package
GenericValue orderItem = orderReadHelper.getOrderItem(orderShipment.getString("orderItemSeqId"));
try {
product = orderItem.getRelatedOne("Product");
} catch (GenericEntityException e) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "ManufacturingPackageConfiguratorError", locale));
}
quantity = orderShipment.getBigDecimal("quantity");
}
BigDecimal productDepth = product.getBigDecimal("shippingDepth");
if (productDepth == null) {
productDepth = product.getBigDecimal("productDepth");
}
if (productDepth == null) {
productDepth = BigDecimal.ONE;
}
BigDecimal firstMaxNumOfProducts = boxWidth.subtract(totalWidth).divide(productDepth, 0, BigDecimal.ROUND_FLOOR);
if (firstMaxNumOfProducts.compareTo(BigDecimal.ZERO) == 0) firstMaxNumOfProducts = BigDecimal.ONE;
//
BigDecimal maxNumOfProducts = boxWidth.divide(productDepth, 0, BigDecimal.ROUND_FLOOR);
if (maxNumOfProducts.compareTo(BigDecimal.ZERO) == 0) maxNumOfProducts = BigDecimal.ONE;
BigDecimal remQuantity = quantity;
boolean isFirst = true;
while (remQuantity.compareTo(BigDecimal.ZERO) > 0) {
BigDecimal maxQuantity = BigDecimal.ZERO;
if (isFirst) {
maxQuantity = firstMaxNumOfProducts;
isFirst = false;
} else {
maxQuantity = maxNumOfProducts;
}
BigDecimal qty = (remQuantity.compareTo(maxQuantity) < 0 ? remQuantity : maxQuantity);
// If needed, create the package
if (shipmentPackageSeqId == null) {
try {
Map<String, Object> resultService = dispatcher.runSync("createShipmentPackage", UtilMisc.<String, Object>toMap("shipmentId", orderShipment.getString("shipmentId"), "shipmentBoxTypeId", boxTypeId, "userLogin", userLogin));
shipmentPackageSeqId = (String)resultService.get("shipmentPackageSeqId");
} catch (GenericServiceException e) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "ManufacturingPackageConfiguratorError", locale));
}
totalWidth = BigDecimal.ZERO;
}
try {
Map<String, Object> inputMap = null;
if (subProduct) {
inputMap = UtilMisc.toMap("shipmentId", orderShipment.getString("shipmentId"),
"shipmentPackageSeqId", shipmentPackageSeqId,
"shipmentItemSeqId", orderShipment.getString("shipmentItemSeqId"),
"subProductId", product.getString("productId"),
"userLogin", userLogin,
"subProductQuantity", qty);
} else {
inputMap = UtilMisc.toMap("shipmentId", orderShipment.getString("shipmentId"),
"shipmentPackageSeqId", shipmentPackageSeqId,
"shipmentItemSeqId", orderShipment.getString("shipmentItemSeqId"),
"userLogin", userLogin,
"quantity", qty);
}
dispatcher.runSync("createShipmentPackageContent", inputMap);
} catch (GenericServiceException e) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "ManufacturingPackageConfiguratorError", locale));
}
totalWidth = totalWidth.add(qty.multiply(productDepth));
if (qty.compareTo(maxQuantity) == 0) shipmentPackageSeqId = null;
remQuantity = remQuantity.subtract(qty);
}
}
}
}
return result;
}
/** It reads the product's bill of materials,
* if necessary configures it, and it returns its (possibly configured) components in
* a List of {@link BOMNode}).
* @param dctx the distach context
* @param context the context
* @return returns the list of products in packages
*/
public static Map<String, Object> getProductsInPackages(DispatchContext dctx, Map<String, ? extends Object> context) {
Map<String, Object> result = FastMap.newInstance();
Delegator delegator = dctx.getDelegator();
LocalDispatcher dispatcher = dctx.getDispatcher();
GenericValue userLogin = (GenericValue)context.get("userLogin");
Locale locale = (Locale) context.get("locale");
String productId = (String) context.get("productId");
BigDecimal quantity = (BigDecimal) context.get("quantity");
String fromDateStr = (String) context.get("fromDate");
if (quantity == null) {
quantity = BigDecimal.ONE;
}
Date fromDate = null;
if (UtilValidate.isNotEmpty(fromDateStr)) {
try {
fromDate = Timestamp.valueOf(fromDateStr);
} catch (Exception e) {
}
}
if (fromDate == null) {
fromDate = new Date();
}
//
// Components
//
BOMTree tree = null;
List<BOMNode> components = FastList.newInstance();
try {
tree = new BOMTree(productId, "MANUF_COMPONENT", fromDate, BOMTree.EXPLOSION_MANUFACTURING, delegator, dispatcher, userLogin);
tree.setRootQuantity(quantity);
tree.getProductsInPackages(components);
} catch (GenericEntityException gee) {
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "ManufacturingBomErrorCreatingBillOfMaterialsTree", UtilMisc.toMap("errorString", gee.getMessage()), locale));
}
result.put("productsInPackages", components);
return result;
}
}