/* * Licensed to csti consulting * You may obtain a copy of the License at * * http://www.csticonsulting.com * Copyright (c) 2006-Aug 24, 2010 Consultation CS-TI inc. * * 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 com.salesmanager.core.module.impl.application.shipping; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.StringTokenizer; import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang.StringUtils; import com.opensymphony.xwork2.validator.ValidationException; import com.salesmanager.core.entity.merchant.MerchantConfiguration; import com.salesmanager.core.entity.merchant.MerchantStore; import com.salesmanager.core.entity.orders.OrderProduct; import com.salesmanager.core.entity.orders.OrderProductAttribute; import com.salesmanager.core.entity.shipping.PackageDetail; import com.salesmanager.core.module.model.application.CalculatePackingModule; import com.salesmanager.core.service.ServiceFactory; import com.salesmanager.core.service.merchant.ConfigurationRequest; import com.salesmanager.core.service.merchant.ConfigurationResponse; import com.salesmanager.core.service.merchant.MerchantService; import com.salesmanager.core.util.CurrencyUtil; import com.salesmanager.core.util.LabelUtil; import com.salesmanager.core.util.LocaleUtil; import com.salesmanager.core.util.LogMerchantUtil; public class CalculateBoxPackingModule implements CalculatePackingModule { public String getConfigurationOptionsFileName(Locale locale) throws Exception { return "packing-box.jsp"; } public PackageDetail getConfigurationOptions(MerchantConfiguration config, String currency) throws Exception { if (config == null || config.getConfigurationValue1() == null) { return null; } PackageDetail details = new PackageDetail(); StringTokenizer st = new StringTokenizer(config .getConfigurationValue1(), "|"); Map parseTokens = new HashMap(); int i = 1; while (st.hasMoreTokens()) { String token = st.nextToken(); if (i == 1) { details.setShippingWidth(new BigDecimal(token).doubleValue()); } else if (i == 2) { details.setShippingHeight(new BigDecimal(token).doubleValue()); } else if (i == 3) { details.setShippingLength(new BigDecimal(token).doubleValue()); } else if (i == 4) { details.setShippingWeight(new BigDecimal(token).doubleValue()); } else if (i == 5) { details.setShippingMaxWeight(new BigDecimal(token) .doubleValue()); } else if (i == 6) { details.setTreshold(Integer.parseInt(token)); } i++; } details.setCurrency(currency); return details; } public Collection<PackageDetail> calculatePacking( Collection<OrderProduct> products, MerchantConfiguration config, int merchantId) throws Exception { if (products == null) { throw new Exception("Product list cannot be null !!"); } double width = 0; double length = 0; double height = 0; double weight = 0; double maxweight = 0; int treshold = 0; // get box details from merchantconfiguration String values = config.getConfigurationValue1(); if (!StringUtils.isBlank(values)) { StringTokenizer st = new StringTokenizer(config .getConfigurationValue1(), "|"); Map parseTokens = new HashMap(); int i = 1; while (st.hasMoreTokens()) { String token = st.nextToken(); if (i == 1) { width = new BigDecimal(token).doubleValue(); } else if (i == 2) { height = new BigDecimal(token).doubleValue(); } else if (i == 3) { length = new BigDecimal(token).doubleValue(); } else if (i == 4) { weight = new BigDecimal(token).doubleValue(); } else if (i == 5) { maxweight = new BigDecimal(token).doubleValue(); } else if (i == 6) { treshold = Integer.parseInt(token); } i++; } } else { LogMerchantUtil.log(merchantId, "Shipping Box information is not configured adequatly"); throw new Exception("Cannot determine box size"); } List boxes = new ArrayList(); // maximum number of boxes int maxBox = 100; int iterCount = 0; Collection leftProducts = new ArrayList(); // need to put items individualy Iterator prodIter = products.iterator(); while (prodIter.hasNext()) { OrderProduct op = (OrderProduct) prodIter.next(); if (!op.isShipping()) { continue; } int qty = op.getProductQuantity(); Set attrs = op.getOrderattributes(); // set attributes values BigDecimal w = op.getProductWeight(); if (attrs != null && attrs.size() > 0) { Iterator attributesIterator = attrs.iterator(); OrderProductAttribute opa = (OrderProductAttribute) attributesIterator .next(); w = w.add(opa.getProductAttributeWeight()); } if (qty > 1) { for (int i = 1; i <= qty; i++) { OrderProduct tempop = new OrderProduct(); tempop.setProductHeight(op.getProductHeight()); tempop.setProductLength(op.getProductLength()); tempop.setProductWidth(op.getProductWidth()); tempop.setProductWeight(w); tempop.setProductQuantity(1); tempop.setOrderattributes(attrs); leftProducts.add(tempop); } } else { op.setProductWeight(w); leftProducts.add(op); } iterCount++; } if (iterCount == 0) { return null; } int productCount = leftProducts.size(); if (productCount < treshold) { throw new Exception("Number of item smaller than treshold"); } List usedBoxesList = new ArrayList(); PackingBox b = new PackingBox(); // set box max volume double maxVolume = width * length * height; if (maxVolume == 0 || maxweight == 0) { LogMerchantUtil.log(merchantId, "Check shipping box configuration, it has a volume of " + maxVolume + " and a maximum weight of " + maxweight + ". Those values must be greater than 0."); } b.setVolumeLeft(maxVolume); b.setWeightLeft(maxweight); usedBoxesList.add(b); int boxCount = 1; Collection assignedProducts = new ArrayList(); // calculate the volume for the next object if (assignedProducts.size() > 0) { leftProducts.removeAll(assignedProducts); assignedProducts = new ArrayList(); } Iterator prodIterator = leftProducts.iterator(); boolean productAssigned = false; while (prodIterator.hasNext()) { OrderProduct op = (OrderProduct) prodIterator.next(); Collection attributes = op.getOrderattributes(); productAssigned = false; double productWeight = op.getProductWeight().doubleValue(); // validate if product fits in the box if (op.getProductWidth().doubleValue() > width || op.getProductHeight().doubleValue() > height || op.getProductLength().doubleValue() > length) { // log message to customer LogMerchantUtil .log( merchantId, "Product " + op.getProductId() + " has a demension larger than the box size specified. Will use per item calculation."); // exit this process and let shipping calculator calculate // individual items throw new Exception( "Product configuration exceeds box configuraton"); } if (productWeight > maxweight) { LogMerchantUtil .log( merchantId, "Product " + op.getProductId() + " has a weight larger than the box maximum weight specified. Will use per item calculation."); throw new Exception("Product weight exceeds box maximum weight"); } double productVolume = (op.getProductWidth().doubleValue() * op.getProductHeight().doubleValue() * op .getProductLength().doubleValue()); if (productVolume == 0) { LogMerchantUtil .log( merchantId, "Product " + op.getProductId() + " has one of the dimension set to 0 and therefore cannot calculate the volume"); throw new Exception("Cannot calculate volume"); } List boxesList = usedBoxesList; // try each box Iterator boxIter = boxesList.iterator(); while (boxIter.hasNext()) { PackingBox pb = (PackingBox) boxIter.next(); double volumeLeft = pb.getVolumeLeft(); double weightLeft = pb.getWeightLeft(); if (pb.getVolumeLeft() >= productVolume && pb.getWeightLeft() >= productWeight) {// fit the item // in this // box // fit in the current box volumeLeft = volumeLeft - productVolume; pb.setVolumeLeft(volumeLeft); weightLeft = weightLeft - productWeight; pb.setWeightLeft(weightLeft); assignedProducts.add(op); productCount--; double w = pb.getWeight(); w = w + productWeight; pb.setWeight(w); productAssigned = true; maxBox--; break; } } if (!productAssigned) {// create a new box b = new PackingBox(); // set box max volume b.setVolumeLeft(maxVolume); b.setWeightLeft(maxweight); usedBoxesList.add(b); double volumeLeft = b.getVolumeLeft() - productVolume; b.setVolumeLeft(volumeLeft); double weightLeft = b.getWeightLeft() - productWeight; b.setWeightLeft(weightLeft); assignedProducts.add(op); productCount--; double w = b.getWeight(); w = w + productWeight; b.setWeight(w); maxBox--; } } // now prepare the shipping info // number of boxes Iterator ubIt = usedBoxesList.iterator(); System.out.println("###################################"); System.out.println("Number of boxex " + usedBoxesList.size()); System.out.println("###################################"); while (ubIt.hasNext()) { PackingBox box = (PackingBox) ubIt.next(); PackageDetail details = new PackageDetail(); details.setShippingHeight(height); details.setShippingLength(length); details.setShippingWeight(weight + box.getWeight()); details.setShippingWidth(width); boxes.add(details); } return boxes; } public void storeConfiguration(int merchantId, ConfigurationResponse vo, HttpServletRequest request) throws Exception { // get the store information MerchantService mservice = (MerchantService) ServiceFactory .getService(ServiceFactory.MerchantService); MerchantStore store = mservice.getMerchantStore(merchantId); // id - merchantId - SHP_PACK - packing-item/packing/box - [values] - // null - packing-item/packing/box // validate submited values // box_maxweight // box_weight // box_length // box_height // box_width Locale locale = LocaleUtil.getLocale(request); StringBuffer buf = new StringBuffer(); try { BigDecimal width = CurrencyUtil.validateMeasure(request .getParameter("box_width"), store.getCurrency()); // int width = Integer.parseInt(); buf.append(width.toString()).append("|"); } catch (Exception e) { throw new ValidationException(LabelUtil.getInstance().getText( locale, "module.box.invalid.width")); } try { BigDecimal height = CurrencyUtil.validateMeasure(request .getParameter("box_height"), store.getCurrency()); // int height = // Integer.parseInt(request.getParameter("box_height")); buf.append(height.toString()).append("|"); } catch (Exception e) { throw new ValidationException(LabelUtil.getInstance().getText( locale, "module.box.invalid.height")); } try { BigDecimal length = CurrencyUtil.validateMeasure(request .getParameter("box_length"), store.getCurrency()); // int length = // Integer.parseInt(request.getParameter("box_length")); buf.append(length.toString()).append("|"); } catch (Exception e) { throw new ValidationException(LabelUtil.getInstance().getText( locale, "module.box.invalid.length")); } try { BigDecimal weight = CurrencyUtil.validateMeasure(request .getParameter("box_weight"), store.getCurrency()); buf.append(weight.toString()).append("|"); } catch (Exception e) { throw new ValidationException(LabelUtil.getInstance().getText( locale, "module.box.invalid.weight")); } try { BigDecimal maxweight = CurrencyUtil.validateMeasure(request .getParameter("box_maxweight"), store.getCurrency()); buf.append(maxweight.toString()).append("|"); } catch (Exception e) { throw new ValidationException(LabelUtil.getInstance().getText( locale, "module.box.invalid.maxweight")); } try { int treshold = Integer.parseInt(request .getParameter("box_treshold")); buf.append(treshold); } catch (Exception e) { throw new ValidationException(LabelUtil.getInstance().getText( locale, "module.box.invalid.treshold")); } ConfigurationRequest vr = new ConfigurationRequest(merchantId, "SHP_PACK"); ConfigurationResponse resp = mservice.getConfiguration(vr); MerchantConfiguration conf = null; if (resp == null || resp.getMerchantConfiguration("SHP_PACK") == null) { conf = new MerchantConfiguration(); } else { conf = resp.getMerchantConfiguration("SHP_PACK"); } conf.setMerchantId(merchantId); conf.setConfigurationKey("SHP_PACK"); conf.setConfigurationValue("packing-box"); conf.setConfigurationValue1(buf.toString()); mservice.saveOrUpdateMerchantConfiguration(conf); } public ConfigurationResponse getConfiguration( MerchantConfiguration configurations, ConfigurationResponse vo) { // nothing specific return vo; } } class PackingBox { private double volumeLeft; private double weightLeft; private double weight; public double getVolumeLeft() { return volumeLeft; } public void setVolumeLeft(double volumeLeft) { this.volumeLeft = volumeLeft; } public double getWeight() { return weight; } public void setWeight(double weight) { this.weight = weight; } public double getWeightLeft() { return weightLeft; } public void setWeightLeft(double weightLeft) { this.weightLeft = weightLeft; } }