/******************************************************************************* * 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.shipment.shipment; import java.math.BigDecimal; import java.math.MathContext; 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.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.EntityUtilProperties; import org.apache.ofbiz.service.DispatchContext; import org.apache.ofbiz.service.GenericServiceException; import org.apache.ofbiz.service.LocalDispatcher; import org.apache.ofbiz.service.ModelService; /** * ShipmentWorker - Worker methods for Shipment and related entities */ public final class ShipmentWorker { public static final String module = ShipmentWorker.class.getName(); private static final MathContext generalRounding = new MathContext(10); private ShipmentWorker() {} /* * Returns the value of a given ShipmentPackageContent record. Calculated by working out the total value (from the OrderItems) of all ItemIssuances * for the ShipmentItem then dividing that by the total quantity issued for the same to get an average item value then multiplying that by the package * content quantity. * Note: No rounding of the calculation is performed so you will need to round it to the accuracy that you require */ public static BigDecimal getShipmentPackageContentValue(GenericValue shipmentPackageContent) { BigDecimal quantity = shipmentPackageContent.getBigDecimal("quantity"); BigDecimal value = new BigDecimal("0"); // lookup the issuance to find the order List<GenericValue> issuances = null; try { GenericValue shipmentItem = shipmentPackageContent.getRelatedOne("ShipmentItem", false); issuances = shipmentItem.getRelated("ItemIssuance", null, null, false); } catch (GenericEntityException e) { Debug.logError(e, module); } BigDecimal totalIssued = BigDecimal.ZERO; BigDecimal totalValue = BigDecimal.ZERO; if (UtilValidate.isNotEmpty(issuances)) { for (GenericValue issuance : issuances) { // we only need one BigDecimal issuanceQuantity = issuance.getBigDecimal("quantity"); BigDecimal issuanceCancelQuantity = issuance.getBigDecimal("cancelQuantity"); if (issuanceCancelQuantity != null) { issuanceQuantity = issuanceQuantity.subtract(issuanceCancelQuantity); } // get the order item GenericValue orderItem = null; try { orderItem = issuance.getRelatedOne("OrderItem", false); } catch (GenericEntityException e) { Debug.logError(e, module); } if (orderItem != null) { // get the value per unit - (base price * amount) BigDecimal selectedAmount = orderItem.getBigDecimal("selectedAmount"); if (selectedAmount == null || selectedAmount.compareTo(BigDecimal.ZERO) <= 0) { selectedAmount = BigDecimal.ONE; } BigDecimal unitPrice = orderItem.getBigDecimal("unitPrice"); BigDecimal itemValue = unitPrice.multiply(selectedAmount); // total value for package (per unit * quantity) totalIssued = totalIssued.add(issuanceQuantity); totalValue = totalValue.add(itemValue.multiply(issuanceQuantity)); } } } // take the average value of the issuances and multiply it by the shipment package content quantity value = totalValue.divide(totalIssued, 10, BigDecimal.ROUND_HALF_EVEN).multiply(quantity); return value; } public static List<Map<String, BigDecimal>> getPackageSplit(DispatchContext dctx, List<Map<String, Object>> shippableItemInfo, BigDecimal maxWeight) { // create the package list w/ the first package List<Map<String, BigDecimal>> packages = new LinkedList<Map<String,BigDecimal>>(); if (UtilValidate.isNotEmpty(shippableItemInfo)) { for (Map<String, Object> itemInfo: shippableItemInfo) { long pieces = ((Long) itemInfo.get("piecesIncluded")).longValue(); BigDecimal totalQuantity = (BigDecimal) itemInfo.get("quantity"); BigDecimal totalWeight = (BigDecimal) itemInfo.get("weight"); String productId = (String) itemInfo.get("productId"); // sanity check if (pieces < 1) { pieces = 1; // can NEVER be less than one } BigDecimal weight = totalWeight.divide(BigDecimal.valueOf(pieces), generalRounding); for (int z = 1; z <= totalQuantity.intValue(); z++) { BigDecimal partialQty = pieces > 1 ? BigDecimal.ONE.divide(BigDecimal.valueOf(pieces), generalRounding) : BigDecimal.ONE; for (long x = 0; x < pieces; x++) { if (weight.compareTo(maxWeight) >= 0) { Map<String, BigDecimal> newPackage = new HashMap<String, BigDecimal>(); newPackage.put(productId, partialQty); packages.add(newPackage); } else if (totalWeight.compareTo(BigDecimal.ZERO) > 0) { // create the first package if (packages.size() == 0) { packages.add(new HashMap<String, BigDecimal>()); } // package loop boolean addedToPackage = false; for (Map<String, BigDecimal> packageMap: packages) { if (!addedToPackage) { BigDecimal packageWeight = calcPackageWeight(dctx, packageMap, shippableItemInfo, weight); if (packageWeight.compareTo(maxWeight) <= 0) { BigDecimal qty = packageMap.get(productId); qty = UtilValidate.isEmpty(qty) ? BigDecimal.ZERO : qty; packageMap.put(productId, qty.add(partialQty)); addedToPackage = true; } } } if (!addedToPackage) { Map<String, BigDecimal> packageMap = new HashMap<String, BigDecimal>(); packageMap.put(productId, partialQty); packages.add(packageMap); } } } } } } return packages; } public static BigDecimal calcPackageWeight(DispatchContext dctx, Map<String, BigDecimal> packageMap, List<Map<String, Object>> shippableItemInfo, BigDecimal additionalWeight) { LocalDispatcher dispatcher = dctx.getDispatcher(); Delegator delegator = dctx.getDelegator(); BigDecimal totalWeight = BigDecimal.ZERO; String defaultWeightUomId = EntityUtilProperties.getPropertyValue("shipment", "shipment.default.weight.uom", delegator); for (Map.Entry<String, BigDecimal> entry: packageMap.entrySet()) { String productId = entry.getKey(); Map<String, Object> productInfo = getProductItemInfo(shippableItemInfo, productId); BigDecimal productWeight = (BigDecimal) productInfo.get("productWeight"); BigDecimal quantity = packageMap.get(productId); String weightUomId = (String) productInfo.get("weightUomId"); Debug.logInfo("Product Id : " + productId.toString() + " Product Weight : " + String.valueOf(productWeight) + " Product UomId : " + weightUomId + " assuming " + defaultWeightUomId + " if null. Quantity : " + String.valueOf(quantity), module); if (UtilValidate.isEmpty(weightUomId)) { weightUomId = defaultWeightUomId; } if (!"WT_lb".equals(weightUomId)) { // attempt a conversion to pounds Map<String, Object> result = new HashMap<String, Object>(); try { result = dispatcher.runSync("convertUom", UtilMisc.<String, Object>toMap("uomId", weightUomId, "uomIdTo", "WT_lb", "originalValue", productWeight)); } catch (GenericServiceException ex) { Debug.logError(ex, module); } if (result.get(ModelService.RESPONSE_MESSAGE).equals(ModelService.RESPOND_SUCCESS) && UtilValidate.isNotEmpty(result.get("convertedValue"))) { productWeight = (BigDecimal) result.get("convertedValue"); } else { Debug.logError("Unsupported weightUom [" + weightUomId + "] for calcPackageWeight running productId " + productId + ", could not find a conversion factor to WT_lb",module); } } totalWeight = totalWeight.add(productWeight.multiply(quantity)); } Debug.logInfo("Package Weight : " + String.valueOf(totalWeight) + " lbs.", module); return totalWeight.add(additionalWeight); } public static Map<String, Object> getProductItemInfo(List<Map<String, Object>> shippableItemInfo, String productId) { if (UtilValidate.isNotEmpty(shippableItemInfo)) { for (Map<String, Object> itemInfoMap: shippableItemInfo) { String compareProductId = (String) itemInfoMap.get("productId"); if (productId.equals(compareProductId)) { return itemInfoMap; } } } return null; } }