| /******************************************************************************* |
| * 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.packing; |
| |
| import java.math.BigDecimal; |
| import java.util.AbstractMap; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.TreeSet; |
| |
| import org.apache.ofbiz.base.util.Debug; |
| import org.apache.ofbiz.base.util.GeneralException; |
| import org.apache.ofbiz.base.util.UtilFormatOut; |
| 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.DelegatorFactory; |
| 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.product.product.ProductWorker; |
| import org.apache.ofbiz.service.GenericServiceException; |
| import org.apache.ofbiz.service.LocalDispatcher; |
| import org.apache.ofbiz.service.ServiceContainer; |
| import org.apache.ofbiz.service.ServiceUtil; |
| |
| @SuppressWarnings("serial") |
| public class PackingSession implements java.io.Serializable { |
| |
| public static final String module = PackingSession.class.getName(); |
| |
| protected GenericValue userLogin = null; |
| protected String pickerPartyId = null; |
| protected String primaryOrderId = null; |
| protected String primaryShipGrp = null; |
| protected String dispatcherName = null; |
| protected String delegatorName = null; |
| protected String picklistBinId = null; |
| protected String facilityId = null; |
| protected String shipmentId = null; |
| protected String instructions = null; |
| protected String weightUomId = null; |
| protected String shipmentBoxTypeId = null; |
| protected BigDecimal additionalShippingCharge = null; |
| protected Map<Integer, BigDecimal> packageWeights = null; |
| protected List<PackingEvent> packEvents = null; |
| protected List<PackingSessionLine> packLines = null; |
| protected List<ItemDisplay> itemInfos = null; |
| protected int packageSeq = -1; |
| protected int status = 1; |
| protected Map<Integer, String> shipmentBoxTypes = null; |
| |
| private transient Delegator _delegator = null; |
| private transient LocalDispatcher _dispatcher = null; |
| |
| public PackingSession(LocalDispatcher dispatcher, GenericValue userLogin, String facilityId, String binId, String orderId, String shipGrp) { |
| this._dispatcher = dispatcher; |
| this.dispatcherName = dispatcher.getName(); |
| |
| this._delegator = _dispatcher.getDelegator(); |
| this.delegatorName = _delegator.getDelegatorName(); |
| |
| this.primaryOrderId = orderId; |
| this.primaryShipGrp = shipGrp; |
| this.picklistBinId = binId; |
| this.userLogin = userLogin; |
| this.facilityId = facilityId; |
| this.packLines = new LinkedList<PackingSessionLine>(); |
| this.packEvents = new LinkedList<PackingEvent>(); |
| this.itemInfos = new LinkedList<PackingSession.ItemDisplay>(); |
| this.packageSeq = 1; |
| this.packageWeights = new HashMap<Integer, BigDecimal>(); |
| this.shipmentBoxTypes = new HashMap<Integer, String>(); |
| } |
| |
| public PackingSession(LocalDispatcher dispatcher, GenericValue userLogin, String facilityId) { |
| this(dispatcher, userLogin, facilityId, null, null, null); |
| } |
| |
| public PackingSession(LocalDispatcher dispatcher, GenericValue userLogin) { |
| this(dispatcher, userLogin, null, null, null, null); |
| } |
| |
| public void addOrIncreaseLine(String orderId, String orderItemSeqId, String shipGroupSeqId, String productId, BigDecimal quantity, int packageSeqId, BigDecimal weight, boolean update) throws GeneralException { |
| // reset the session if we just completed |
| if (status == 0) { |
| throw new GeneralException("Packing session has been completed; be sure to CLEAR before packing a new order! [000]"); |
| } |
| |
| // do nothing if we are trying to add a quantity of 0 |
| if (!update && quantity.compareTo(BigDecimal.ZERO) == 0) { |
| return; |
| } |
| |
| // find the actual product ID |
| productId = ProductWorker.findProductId(this.getDelegator(), productId); |
| |
| // set the default null values - primary is the assumed first item |
| if (orderId == null) { |
| orderId = primaryOrderId; |
| } |
| if (shipGroupSeqId == null) { |
| shipGroupSeqId = primaryShipGrp; |
| } |
| if (orderItemSeqId == null && productId != null) { |
| orderItemSeqId = this.findOrderItemSeqId(productId, orderId, shipGroupSeqId, quantity); |
| } |
| |
| // get the reservations for the item |
| Map<String, Object> invLookup = new HashMap<String, Object>(); |
| invLookup.put("orderId", orderId); |
| invLookup.put("orderItemSeqId", orderItemSeqId); |
| invLookup.put("shipGroupSeqId", shipGroupSeqId); |
| List<GenericValue> reservations = this.getDelegator().findByAnd("OrderItemShipGrpInvRes", invLookup, UtilMisc.toList("quantity DESC"), false); |
| |
| // no reservations we cannot add this item |
| if (UtilValidate.isEmpty(reservations)) { |
| throw new GeneralException("No inventory reservations available; cannot pack this item! [101]"); |
| } |
| |
| // find the inventoryItemId to use |
| if (reservations.size() == 1) { |
| GenericValue res = EntityUtil.getFirst(reservations); |
| int checkCode = this.checkLineForAdd(res, orderId, orderItemSeqId, shipGroupSeqId, productId, quantity, packageSeqId, update); |
| this.createPackLineItem(checkCode, res, orderId, orderItemSeqId, shipGroupSeqId, productId, quantity, weight, packageSeqId); |
| } else { |
| // more than one reservation found |
| Map<GenericValue, BigDecimal> toCreateMap = new HashMap<GenericValue, BigDecimal>(); |
| Iterator<GenericValue> i = reservations.iterator(); |
| BigDecimal qtyRemain = quantity; |
| |
| while (i.hasNext() && qtyRemain.compareTo(BigDecimal.ZERO) > 0) { |
| GenericValue res = i.next(); |
| |
| // Check that the inventory item product match with the current product to pack |
| if (!productId.equals(res.getRelatedOne("InventoryItem", false).getString("productId"))) { |
| continue; |
| } |
| |
| BigDecimal resQty = res.getBigDecimal("quantity"); |
| BigDecimal resPackedQty = this.getPackedQuantity(orderId, orderItemSeqId, shipGroupSeqId, productId, res.getString("inventoryItemId"), -1); |
| if (resPackedQty.compareTo(resQty) >= 0) { |
| continue; |
| } else if (!update) { |
| resQty = resQty.subtract(resPackedQty); |
| } |
| |
| BigDecimal thisQty = resQty.compareTo(qtyRemain) > 0 ? qtyRemain : resQty; |
| |
| int thisCheck = this.checkLineForAdd(res, orderId, orderItemSeqId, shipGroupSeqId, productId, thisQty, packageSeqId, update); |
| switch (thisCheck) { |
| case 2: |
| Debug.logInfo("Packing check returned '2' - new pack line will be created!", module); |
| toCreateMap.put(res, thisQty); |
| qtyRemain = qtyRemain.subtract(thisQty); |
| break; |
| case 1: |
| Debug.logInfo("Packing check returned '1' - existing pack line has been updated!", module); |
| qtyRemain = qtyRemain.subtract(thisQty); |
| break; |
| case 0: |
| Debug.logInfo("Packing check returned '0' - doing nothing.", module); |
| break; |
| } |
| } |
| |
| if (qtyRemain.compareTo(BigDecimal.ZERO) == 0) { |
| for (Map.Entry<GenericValue, BigDecimal> entry: toCreateMap.entrySet()) { |
| GenericValue res = entry.getKey(); |
| BigDecimal qty = entry.getValue(); |
| this.createPackLineItem(2, res, orderId, orderItemSeqId, shipGroupSeqId, productId, qty, weight, packageSeqId); |
| } |
| } else { |
| throw new GeneralException("Not enough inventory reservation available; cannot pack the item! [103]"); |
| } |
| } |
| |
| // run the add events |
| this.runEvents(PackingEvent.EVENT_CODE_ADD); |
| } |
| |
| public void addOrIncreaseLine(String orderId, String orderItemSeqId, String shipGroupSeqId, BigDecimal quantity, int packageSeqId) throws GeneralException { |
| this.addOrIncreaseLine(orderId, orderItemSeqId, shipGroupSeqId, null, quantity, packageSeqId, BigDecimal.ZERO, false); |
| } |
| |
| public void addOrIncreaseLine(String productId, BigDecimal quantity, int packageSeqId) throws GeneralException { |
| this.addOrIncreaseLine(null, null, null, productId, quantity, packageSeqId, BigDecimal.ZERO, false); |
| } |
| |
| public PackingSessionLine findLine(String orderId, String orderItemSeqId, String shipGroupSeqId, String productId, String inventoryItemId, int packageSeq) { |
| for (PackingSessionLine line: this.getLines()) { |
| if (orderId.equals(line.getOrderId()) && |
| orderItemSeqId.equals(line.getOrderItemSeqId()) && |
| shipGroupSeqId.equals(line.getShipGroupSeqId()) && |
| productId.equals(line.getProductId()) && |
| inventoryItemId.equals(line.getInventoryItemId()) && |
| packageSeq == line.getPackageSeq()) { |
| return line; |
| } |
| } |
| return null; |
| } |
| |
| protected void createPackLineItem(int checkCode, GenericValue res, String orderId, String orderItemSeqId, String shipGroupSeqId, String productId, BigDecimal quantity, BigDecimal weight, int packageSeqId) throws GeneralException { |
| // process the result; add new item if necessary |
| switch (checkCode) { |
| case 0: |
| // not enough reserved |
| throw new GeneralException("Not enough inventory reservation available; cannot pack the item! [201]"); |
| case 1: |
| // we're all good to go; quantity already updated |
| break; |
| case 2: |
| // need to create a new item |
| String invItemId = res.getString("inventoryItemId"); |
| packLines.add(new PackingSessionLine(orderId, orderItemSeqId, shipGroupSeqId, productId, invItemId, quantity, weight, packageSeqId)); |
| break; |
| } |
| |
| // Add the line weight to the package weight |
| if (weight.compareTo(BigDecimal.ZERO) > 0) this.addToPackageWeight(packageSeqId, weight); |
| |
| // update the package sequence |
| if (packageSeqId > packageSeq) { |
| this.packageSeq = packageSeqId; |
| } |
| } |
| |
| protected String findOrderItemSeqId(String productId, String orderId, String shipGroupSeqId, BigDecimal quantity) throws GeneralException { |
| Map<String, Object> lookupMap = new HashMap<String, Object>(); |
| lookupMap.put("orderId", orderId); |
| lookupMap.put("productId", productId); |
| lookupMap.put("statusId", "ITEM_APPROVED"); |
| lookupMap.put("shipGroupSeqId", shipGroupSeqId); |
| |
| List<String> sort = UtilMisc.toList("-quantity"); |
| List<GenericValue> orderItems = this.getDelegator().findByAnd("OrderItemAndShipGroupAssoc", lookupMap, sort, false); |
| |
| String orderItemSeqId = null; |
| if (orderItems != null) { |
| for (GenericValue item: orderItems) { |
| // get the reservations for the item |
| Map<String, Object> invLookup = new HashMap<String, Object>(); |
| invLookup.put("orderId", orderId); |
| invLookup.put("orderItemSeqId", item.getString("orderItemSeqId")); |
| invLookup.put("shipGroupSeqId", shipGroupSeqId); |
| List<GenericValue> reservations = this.getDelegator().findByAnd("OrderItemShipGrpInvRes", invLookup, null, false); |
| for (GenericValue res: reservations) { |
| BigDecimal qty = res.getBigDecimal("quantity"); |
| if (quantity.compareTo(qty) <= 0) { |
| orderItemSeqId = item.getString("orderItemSeqId"); |
| break; |
| } |
| } |
| } |
| } |
| |
| if (orderItemSeqId != null) { |
| return orderItemSeqId; |
| } else { |
| throw new GeneralException("No valid order item found for product [" + productId + "] with quantity: " + quantity); |
| } |
| } |
| |
| protected int checkLineForAdd(GenericValue res, String orderId, String orderItemSeqId, String shipGroupSeqId, String productId, BigDecimal quantity, int packageSeqId, boolean update) { |
| // check to see if the reservation can hold the requested quantity amount |
| String invItemId = res.getString("inventoryItemId"); |
| BigDecimal resQty = res.getBigDecimal("quantity"); |
| |
| PackingSessionLine line = this.findLine(orderId, orderItemSeqId, shipGroupSeqId, productId, invItemId, packageSeqId); |
| BigDecimal packedQty = this.getPackedQuantity(orderId, orderItemSeqId, shipGroupSeqId, productId); |
| |
| Debug.logInfo("Packed quantity [" + packedQty + "] + [" + quantity + "]", module); |
| |
| if (line == null) { |
| Debug.logInfo("No current line found testing [" + invItemId + "] R: " + resQty + " / Q: " + quantity, module); |
| if (resQty.compareTo(quantity) < 0) { |
| return 0; |
| } else { |
| return 2; |
| } |
| } else { |
| BigDecimal newQty = update ? quantity : (line.getQuantity().add(quantity)); |
| Debug.logInfo("Existing line found testing [" + invItemId + "] R: " + resQty + " / Q: " + newQty, module); |
| if (resQty.compareTo(newQty) < 0) { |
| return 0; |
| } else { |
| line.setQuantity(newQty); |
| return 1; |
| } |
| } |
| } |
| |
| public void addItemInfo(List<GenericValue> infos) { |
| for (GenericValue v: infos) { |
| ItemDisplay newItem = new ItemDisplay(v); |
| int currentIdx = itemInfos.indexOf(newItem); |
| if (currentIdx != -1) { |
| ItemDisplay existingItem = itemInfos.get(currentIdx); |
| existingItem.quantity = existingItem.quantity.add(newItem.quantity); |
| } else { |
| itemInfos.add(newItem); |
| } |
| } |
| } |
| |
| public List<ItemDisplay> getItemInfos() { |
| return itemInfos; |
| } |
| |
| /** |
| * <p>Delivers all the packing lines grouped by package.</p> |
| * Output map: |
| * <ul> |
| * <li>packageMap - a Map of type {@code Map<Integer, List<PackingSessionLine>>} |
| * that maps package sequence ids to the lines that belong in |
| * that package</li> |
| * <li>sortedKeys - a List of type List<Integer> with the sorted package |
| * sequence numbers to index the packageMap</li> |
| * </ul> |
| * @return result Map with packageMap and sortedKeys |
| */ |
| public Map<Object, Object> getPackingSessionLinesByPackage() { |
| Map<Integer, List<PackingSessionLine>> packageMap = new HashMap<Integer, List<PackingSessionLine>>(); |
| for (PackingSessionLine line : packLines) { |
| int pSeq = line.getPackageSeq(); |
| List<PackingSessionLine> packageLineList = packageMap.get(pSeq); |
| if (packageLineList == null) { |
| packageLineList = new LinkedList<PackingSessionLine>(); |
| packageMap.put(pSeq, packageLineList); |
| } |
| packageLineList.add(line); |
| } |
| Object[] keys = packageMap.keySet().toArray(); |
| java.util.Arrays.sort(keys); |
| List<Object> sortedKeys = new LinkedList<Object>(); |
| for (Object key : keys) { |
| sortedKeys.add(key); |
| } |
| Map<Object, Object> result = new HashMap<Object, Object>(); |
| result.put("packageMap", packageMap); |
| result.put("sortedKeys", sortedKeys); |
| return result; |
| } |
| |
| public void clearItemInfos() { |
| itemInfos.clear(); |
| } |
| |
| public String getShipmentId() { |
| return this.shipmentId; |
| } |
| |
| public List<PackingSessionLine> getLines() { |
| return this.packLines; |
| } |
| |
| public int nextPackageSeq() { |
| return ++packageSeq; |
| } |
| |
| public int getCurrentPackageSeq() { |
| return packageSeq; |
| } |
| |
| public BigDecimal getPackedQuantity(String orderId, String orderItemSeqId, String shipGroupSeqId, String productId) { |
| return getPackedQuantity(orderId, orderItemSeqId, shipGroupSeqId, productId, null, -1); |
| } |
| |
| public BigDecimal getPackedQuantity(String orderId, String orderItemSeqId, String shipGroupSeqId, String productId, int packageSeq) { |
| return getPackedQuantity(orderId, orderItemSeqId, shipGroupSeqId, productId, null, packageSeq); |
| } |
| |
| public BigDecimal getPackedQuantity(String orderId, String orderItemSeqId, String shipGroupSeqId, String productId, String inventoryItemId, int packageSeq) { |
| BigDecimal total = BigDecimal.ZERO; |
| for (PackingSessionLine line: this.getLines()) { |
| if (orderId.equals(line.getOrderId()) && orderItemSeqId.equals(line.getOrderItemSeqId()) && |
| shipGroupSeqId.equals(line.getShipGroupSeqId()) && productId.equals(line.getProductId())) { |
| if (inventoryItemId == null || inventoryItemId.equals(line.getInventoryItemId())) { |
| if (packageSeq == -1 || packageSeq == line.getPackageSeq()) { |
| total = total.add(line.getQuantity()); |
| } |
| } |
| } |
| } |
| return total; |
| } |
| |
| public BigDecimal getPackedQuantity(String productId, int packageSeq) { |
| if (productId != null) { |
| try { |
| productId = ProductWorker.findProductId(this.getDelegator(), productId); |
| } catch (GenericEntityException e) { |
| Debug.logError(e, module); |
| } |
| } |
| |
| BigDecimal total = BigDecimal.ZERO; |
| if (productId != null) { |
| for (PackingSessionLine line: this.getLines()) { |
| if (productId.equals(line.getProductId())) { |
| if (packageSeq == -1 || packageSeq == line.getPackageSeq()) { |
| total = total.add(line.getQuantity()); |
| } |
| } |
| } |
| } |
| return total; |
| } |
| |
| public BigDecimal getPackedQuantity(int packageSeq) { |
| BigDecimal total = BigDecimal.ZERO; |
| for (PackingSessionLine line: this.getLines()) { |
| if (packageSeq == -1 || packageSeq == line.getPackageSeq()) { |
| total = total.add(line.getQuantity()); |
| } |
| } |
| return total; |
| } |
| |
| public BigDecimal getPackedQuantity(String productId) { |
| return getPackedQuantity(productId, -1); |
| } |
| |
| public BigDecimal getCurrentReservedQuantity(String orderId, String orderItemSeqId, String shipGroupSeqId, String productId) { |
| BigDecimal reserved = BigDecimal.ONE.negate(); |
| try { |
| GenericValue res = EntityUtil.getFirst(this.getDelegator().findByAnd("OrderItemAndShipGrpInvResAndItemSum", UtilMisc.toMap("orderId", orderId, |
| "orderItemSeqId", orderItemSeqId, "shipGroupSeqId", shipGroupSeqId, "inventoryProductId", productId), null, false)); |
| reserved = res.getBigDecimal("totQuantityAvailable"); |
| if (reserved == null) { |
| reserved = BigDecimal.ONE.negate(); |
| } |
| } catch (GenericEntityException e) { |
| Debug.logError(e, module); |
| } |
| return reserved; |
| } |
| |
| public BigDecimal getCurrentShippedQuantity(String orderId, String orderItemSeqId, String shipGroupSeqId) { |
| BigDecimal shipped = BigDecimal.ZERO; |
| List<GenericValue> issues = this.getItemIssuances(orderId, orderItemSeqId, shipGroupSeqId); |
| if (issues != null) { |
| for (GenericValue v: issues) { |
| BigDecimal qty = v.getBigDecimal("quantity"); |
| if (qty == null) qty = BigDecimal.ZERO; |
| shipped = shipped.add(qty); |
| } |
| } |
| |
| return shipped; |
| } |
| |
| public List<String> getCurrentShipmentIds(String orderId, String orderItemSeqId, String shipGroupSeqId) { |
| Set<String> shipmentIds = new HashSet<String>(); |
| List<GenericValue> issues = this.getItemIssuances(orderId, orderItemSeqId, shipGroupSeqId); |
| |
| if (issues != null) { |
| for (GenericValue v: issues) { |
| shipmentIds.add(v.getString("shipmentId")); |
| } |
| } |
| |
| List<String> retList = new LinkedList<String>(); |
| retList.addAll(shipmentIds); |
| return retList; |
| } |
| |
| public List<String> getCurrentShipmentIds(String orderId, String shipGroupSeqId) { |
| return this.getCurrentShipmentIds(orderId, null, shipGroupSeqId); |
| } |
| |
| public void registerEvent(PackingEvent event) { |
| this.packEvents.add(event); |
| this.runEvents(PackingEvent.EVENT_CODE_EREG); |
| } |
| |
| public LocalDispatcher getDispatcher() { |
| if (_dispatcher == null) { |
| _dispatcher = ServiceContainer.getLocalDispatcher(dispatcherName, this.getDelegator()); |
| } |
| return _dispatcher; |
| } |
| |
| public Delegator getDelegator() { |
| if (_delegator == null) { |
| _delegator = DelegatorFactory.getDelegator(delegatorName); |
| } |
| return _delegator; |
| } |
| |
| public GenericValue getUserLogin() { |
| return this.userLogin; |
| } |
| |
| public int getStatus() { |
| return this.status; |
| } |
| |
| public String getFacilityId() { |
| return this.facilityId; |
| } |
| |
| public void setFacilityId(String facilityId) { |
| this.facilityId = facilityId; |
| } |
| |
| public String getPrimaryOrderId() { |
| return this.primaryOrderId; |
| } |
| |
| public void setPrimaryOrderId(String orderId) { |
| this.primaryOrderId = orderId; |
| } |
| |
| public String getPrimaryShipGroupSeqId() { |
| return this.primaryShipGrp; |
| } |
| |
| public void setPrimaryShipGroupSeqId(String shipGroupSeqId) { |
| this.primaryShipGrp = shipGroupSeqId; |
| } |
| |
| public void setPicklistBinId(String binId) { |
| this.picklistBinId = binId; |
| } |
| |
| public String getPicklistBinId() { |
| return this.picklistBinId; |
| } |
| |
| public String getHandlingInstructions() { |
| return this.instructions; |
| } |
| |
| public void setHandlingInstructions(String instructions) { |
| this.instructions = instructions; |
| } |
| |
| public void setPickerPartyId(String partyId) { |
| this.pickerPartyId = partyId; |
| } |
| |
| public String getPickerPartyId() { |
| return this.pickerPartyId; |
| } |
| |
| public int clearLastPackage() { |
| if (packageSeq == 1) { |
| this.clear(); |
| return packageSeq; |
| } |
| |
| List<PackingSessionLine> currentLines = UtilMisc.makeListWritable(this.packLines); |
| for (PackingSessionLine line: currentLines) { |
| if (line.getPackageSeq() == packageSeq) { |
| this.clearLine(line); |
| } |
| } |
| //return --packageSeq; |
| return packageSeq; |
| } |
| |
| public void clearLine(PackingSessionLine line) { |
| this.packLines.remove(line); |
| BigDecimal packageWeight = this.packageWeights.get(line.packageSeq); |
| if (packageWeight != null) { |
| packageWeight = packageWeight.subtract(line.weight); |
| if (packageWeight.compareTo(BigDecimal.ZERO) < 0) { |
| packageWeight = BigDecimal.ZERO; |
| } |
| this.packageWeights.put(line.packageSeq, packageWeight); |
| } |
| if (line.packageSeq == packageSeq && packageSeq > 1) { |
| packageSeq--; |
| } |
| } |
| |
| public void clearAllLines() { |
| this.packLines.clear(); |
| this.packageWeights.clear(); |
| this.shipmentBoxTypes.clear(); |
| this.packageSeq = 1; |
| } |
| |
| public void clear() { |
| this.packLines.clear(); |
| this.instructions = null; |
| this.pickerPartyId = null; |
| this.picklistBinId = null; |
| this.primaryOrderId = null; |
| this.primaryShipGrp = null; |
| this.additionalShippingCharge = null; |
| if (this.packageWeights != null) this.packageWeights.clear(); |
| if (this.shipmentBoxTypes != null) this.shipmentBoxTypes.clear(); |
| this.weightUomId = null; |
| this.packageSeq = 1; |
| this.status = 1; |
| this.runEvents(PackingEvent.EVENT_CODE_CLEAR); |
| } |
| |
| public String complete(boolean force) throws GeneralException { |
| // check to see if there is anything to process |
| if (this.getLines().size() == 0) { |
| return "EMPTY"; |
| } |
| |
| // check for errors |
| this.checkReservations(force); |
| // set the status to 0 |
| this.status = 0; |
| // create the shipment |
| this.createShipment(); |
| // create the packages |
| this.createPackages(); |
| // issue the items |
| this.issueItemsToShipment(); |
| // assign items to packages |
| this.applyItemsToPackages(); |
| // update ShipmentRouteSegments with total weight and weightUomId |
| this.updateShipmentRouteSegments(); |
| // set the shipment to packed |
| this.setShipmentToPacked(); |
| // set role on picklist |
| this.setPickerOnPicklist(); |
| // run the complete events |
| this.runEvents(PackingEvent.EVENT_CODE_COMPLETE); |
| |
| return this.shipmentId; |
| } |
| |
| protected void checkReservations(boolean ignore) throws GeneralException { |
| List<String> errors = new LinkedList<String>(); |
| for (PackingSessionLine line: this.getLines()) { |
| BigDecimal reservedQty = this.getCurrentReservedQuantity(line.getOrderId(), line.getOrderItemSeqId(), line.getShipGroupSeqId(), line.getProductId()); |
| BigDecimal packedQty = this.getPackedQuantity(line.getOrderId(), line.getOrderItemSeqId(), line.getShipGroupSeqId(), line.getProductId()); |
| |
| if (packedQty.compareTo(reservedQty) != 0) { |
| errors.add("Packed amount does not match reserved amount for item (" + line.getProductId() + ") [" + packedQty + " / " + reservedQty + "]"); |
| } |
| } |
| |
| if (errors.size() > 0) { |
| if (!ignore) { |
| throw new GeneralException("Attempt to pack order failed.", errors); |
| } else { |
| Debug.logWarning("Packing warnings: " + errors, module); |
| } |
| } |
| } |
| |
| protected void checkEmptyLines() throws GeneralException { |
| List<PackingSessionLine> lines = new LinkedList<PackingSessionLine>(); |
| lines.addAll(this.getLines()); |
| for (PackingSessionLine l: lines) { |
| if (l.getQuantity().compareTo(BigDecimal.ZERO) == 0) { |
| this.packLines.remove(l); |
| } |
| } |
| } |
| |
| protected void runEvents(int eventCode) { |
| if (this.packEvents.size() > 0) { |
| for (PackingEvent event: this.packEvents) { |
| event.runEvent(this, eventCode); |
| } |
| } |
| } |
| |
| protected List<GenericValue> getItemIssuances(String orderId, String orderItemSeqId, String shipGroupSeqId) { |
| List<GenericValue> issues = null; |
| if (orderId == null) { |
| throw new IllegalArgumentException("Value for orderId is null"); |
| } |
| |
| Map<String, Object> lookupMap = new HashMap<String, Object>(); |
| lookupMap.put("orderId", orderId); |
| if (UtilValidate.isNotEmpty(orderItemSeqId)) { |
| lookupMap.put("orderItemSeqId", orderItemSeqId); |
| } |
| if (UtilValidate.isNotEmpty(shipGroupSeqId)) { |
| lookupMap.put("shipGroupSeqId", shipGroupSeqId); |
| } |
| try { |
| issues = this.getDelegator().findByAnd("ItemIssuance", lookupMap, null, false); |
| } catch (GenericEntityException e) { |
| Debug.logError(e, module); |
| } |
| |
| return issues; |
| } |
| |
| protected void createShipment() throws GeneralException { |
| // first create the shipment |
| Delegator delegator = this.getDelegator(); |
| Map<String, Object> newShipment = new HashMap<String, Object>(); |
| newShipment.put("originFacilityId", this.facilityId); |
| newShipment.put("primaryShipGroupSeqId", primaryShipGrp); |
| newShipment.put("primaryOrderId", primaryOrderId); |
| newShipment.put("shipmentTypeId", "OUTGOING_SHIPMENT"); |
| newShipment.put("statusId", "SHIPMENT_INPUT"); |
| newShipment.put("handlingInstructions", instructions); |
| newShipment.put("picklistBinId", picklistBinId); |
| newShipment.put("additionalShippingCharge", additionalShippingCharge); |
| newShipment.put("userLogin", userLogin); |
| GenericValue orderRoleShipTo = EntityQuery.use(delegator).from("OrderRole").where("orderId", primaryOrderId, "roleTypeId", "SHIP_TO_CUSTOMER").queryFirst(); |
| if (UtilValidate.isNotEmpty(orderRoleShipTo)) { |
| newShipment.put("partyIdTo", orderRoleShipTo.getString("partyId")); |
| } |
| String partyIdFrom = null; |
| if (primaryOrderId != null) { |
| GenericValue orderItemShipGroup = EntityQuery.use(delegator).from("OrderItemShipGroup").where("orderId", primaryOrderId, "shipGroupSeqId", primaryShipGrp).queryFirst(); |
| if (UtilValidate.isNotEmpty(orderItemShipGroup.getString("vendorPartyId"))) { |
| partyIdFrom = orderItemShipGroup.getString("vendorPartyId"); |
| } else if (UtilValidate.isNotEmpty(orderItemShipGroup.getString("facilityId"))) { |
| GenericValue facility = EntityQuery.use(delegator).from("Facility").where("facilityId", orderItemShipGroup.getString("facilityId")).queryOne(); |
| if (UtilValidate.isNotEmpty(facility.getString("ownerPartyId"))) { |
| partyIdFrom = facility.getString("ownerPartyId"); |
| } |
| } |
| if (UtilValidate.isEmpty(partyIdFrom)) { |
| GenericValue orderRoleShipFrom = EntityQuery.use(delegator).from("OrderRole").where("orderId", primaryOrderId, "roleTypeId", "SHIP_FROM_VENDOR").queryFirst(); |
| if (UtilValidate.isNotEmpty(orderRoleShipFrom)) { |
| partyIdFrom = orderRoleShipFrom.getString("partyId"); |
| } else { |
| orderRoleShipFrom = EntityQuery.use(delegator).from("OrderRole").where("orderId", primaryOrderId, "roleTypeId", "BILL_FROM_VENDOR").queryFirst(); |
| partyIdFrom = orderRoleShipFrom.getString("partyId"); |
| } |
| } |
| } else if (this.facilityId != null) { |
| GenericValue facility = EntityQuery.use(delegator).from("Facility").where("facilityId", this.facilityId).queryOne(); |
| if (UtilValidate.isNotEmpty(facility.getString("ownerPartyId"))) { |
| partyIdFrom = facility.getString("ownerPartyId"); |
| } |
| } |
| |
| newShipment.put("partyIdFrom", partyIdFrom); |
| Debug.logInfo("Creating new shipment with context: " + newShipment, module); |
| Map<String, Object> newShipResp = this.getDispatcher().runSync("createShipment", newShipment); |
| |
| if (ServiceUtil.isError(newShipResp)) { |
| throw new GeneralException(ServiceUtil.getErrorMessage(newShipResp)); |
| } |
| this.shipmentId = (String) newShipResp.get("shipmentId"); |
| } |
| |
| protected void issueItemsToShipment() throws GeneralException { |
| List<PackingSessionLine> processedLines = new LinkedList<PackingSessionLine>(); |
| for (PackingSessionLine line: this.getLines()) { |
| if (this.checkLine(processedLines, line)) { |
| BigDecimal totalPacked = this.getPackedQuantity(line.getOrderId(), line.getOrderItemSeqId(), |
| line.getShipGroupSeqId(), line.getProductId(), line.getInventoryItemId(), -1); |
| |
| line.issueItemToShipment(shipmentId, picklistBinId, userLogin, totalPacked, getDispatcher()); |
| processedLines.add(line); |
| } |
| } |
| } |
| |
| protected boolean checkLine(List<PackingSessionLine> processedLines, PackingSessionLine line) { |
| for (PackingSessionLine l: processedLines) { |
| if (line.isSameItem(l)) { |
| line.setShipmentItemSeqId(l.getShipmentItemSeqId()); |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| protected void createPackages() throws GeneralException { |
| for (int i = 0; i < packageSeq; i++) { |
| String shipmentPackageSeqId = UtilFormatOut.formatPaddedNumber(i+1, 5); |
| |
| Map<String, Object> pkgCtx = new HashMap<String, Object>(); |
| pkgCtx.put("shipmentId", shipmentId); |
| pkgCtx.put("shipmentPackageSeqId", shipmentPackageSeqId); |
| pkgCtx.put("shipmentBoxTypeId", getShipmentBoxType(i+1)); |
| pkgCtx.put("weight", getPackageWeight(i+1)); |
| pkgCtx.put("weightUomId", getWeightUomId()); |
| pkgCtx.put("userLogin", userLogin); |
| Map<String, Object> newPkgResp = this.getDispatcher().runSync("createShipmentPackage", pkgCtx); |
| |
| if (ServiceUtil.isError(newPkgResp)) { |
| throw new GeneralException(ServiceUtil.getErrorMessage(newPkgResp)); |
| } |
| } |
| } |
| |
| protected void applyItemsToPackages() throws GeneralException { |
| for (PackingSessionLine line: this.getLines()) { |
| line.applyLineToPackage(shipmentId, userLogin, getDispatcher()); |
| } |
| } |
| |
| protected void updateShipmentRouteSegments() throws GeneralException { |
| BigDecimal shipmentWeight = getTotalWeight(); |
| if (shipmentWeight.compareTo(BigDecimal.ZERO) <= 0) return; |
| List<GenericValue> shipmentRouteSegments = getDelegator().findByAnd("ShipmentRouteSegment", UtilMisc.toMap("shipmentId", this.getShipmentId()), null, false); |
| if (! UtilValidate.isEmpty(shipmentRouteSegments)) { |
| for (GenericValue shipmentRouteSegment: shipmentRouteSegments) { |
| shipmentRouteSegment.set("billingWeight", shipmentWeight); |
| shipmentRouteSegment.set("billingWeightUomId", getWeightUomId()); |
| } |
| getDelegator().storeAll(shipmentRouteSegments); |
| } |
| } |
| |
| protected void setShipmentToPacked() throws GeneralException { |
| Map<String, Object> packedCtx = UtilMisc.toMap("shipmentId", shipmentId, "statusId", "SHIPMENT_PACKED", "userLogin", userLogin); |
| Map<String, Object> packedResp = this.getDispatcher().runSync("updateShipment", packedCtx); |
| if (packedResp != null && ServiceUtil.isError(packedResp)) { |
| throw new GeneralException(ServiceUtil.getErrorMessage(packedResp)); |
| } |
| } |
| |
| protected void setPickerOnPicklist() throws GeneralException { |
| if (picklistBinId != null) { |
| // first find the picklist id |
| GenericValue bin = this.getDelegator().findOne("PicklistBin", UtilMisc.toMap("picklistBinId", picklistBinId), false); |
| if (bin != null) { |
| Map<String, Object> ctx = new HashMap<String, Object>(); |
| ctx.put("picklistId", bin.getString("picklistId")); |
| ctx.put("partyId", pickerPartyId); |
| ctx.put("roleTypeId", "PICKER"); |
| |
| // check if the role already exists and is valid |
| List<GenericValue> currentRoles = this.getDelegator().findByAnd("PicklistRole", ctx, null, false); |
| currentRoles = EntityUtil.filterByDate(currentRoles); |
| |
| // if not; create the role |
| if (UtilValidate.isNotEmpty(currentRoles)) { |
| ctx.put("userLogin", userLogin); |
| Map<String, Object> addRole = this.getDispatcher().runSync("createPicklistRole", ctx); |
| if (ServiceUtil.isError(addRole)) { |
| throw new GeneralException(ServiceUtil.getErrorMessage(addRole)); |
| } |
| } |
| } |
| } |
| } |
| |
| public BigDecimal getAdditionalShippingCharge() { |
| return additionalShippingCharge; |
| } |
| |
| public void setAdditionalShippingCharge(BigDecimal additionalShippingCharge) { |
| this.additionalShippingCharge = additionalShippingCharge; |
| } |
| |
| public BigDecimal getTotalWeight() { |
| BigDecimal total = BigDecimal.ZERO; |
| for (int i = 0; i < packageSeq; i++) { |
| BigDecimal packageWeight = getPackageWeight(i); |
| if (! UtilValidate.isEmpty(packageWeight)) { |
| total = total.add(packageWeight); |
| } |
| } |
| return total; |
| } |
| |
| public BigDecimal getShipmentCostEstimate(GenericValue orderItemShipGroup, String productStoreId, List<GenericValue> shippableItemInfo, BigDecimal shippableTotal, BigDecimal shippableWeight, BigDecimal shippableQuantity) { |
| return getShipmentCostEstimate(orderItemShipGroup.getString("contactMechId"), orderItemShipGroup.getString("shipmentMethodTypeId"), |
| orderItemShipGroup.getString("carrierPartyId"), orderItemShipGroup.getString("carrierRoleTypeId"), |
| productStoreId, shippableItemInfo, shippableTotal, shippableWeight, shippableQuantity); |
| } |
| |
| public BigDecimal getShipmentCostEstimate(GenericValue orderItemShipGroup, String productStoreId) { |
| return getShipmentCostEstimate(orderItemShipGroup.getString("contactMechId"), orderItemShipGroup.getString("shipmentMethodTypeId"), |
| orderItemShipGroup.getString("carrierPartyId"), orderItemShipGroup.getString("carrierRoleTypeId"), |
| productStoreId, null, null, null, null); |
| } |
| |
| public BigDecimal getShipmentCostEstimate(String shippingContactMechId, String shipmentMethodTypeId, String carrierPartyId, String carrierRoleTypeId, String productStoreId, List<GenericValue> shippableItemInfo, BigDecimal shippableTotal, BigDecimal shippableWeight, BigDecimal shippableQuantity) { |
| |
| BigDecimal shipmentCostEstimate = null; |
| Map<String, Object> serviceResult = null; |
| try { |
| Map<String, Object> serviceContext = new HashMap<String, Object>(); |
| serviceContext.put("shippingContactMechId", shippingContactMechId); |
| serviceContext.put("shipmentMethodTypeId", shipmentMethodTypeId); |
| serviceContext.put("carrierPartyId", carrierPartyId); |
| serviceContext.put("carrierRoleTypeId", carrierRoleTypeId); |
| serviceContext.put("productStoreId", productStoreId); |
| |
| if (UtilValidate.isEmpty(shippableItemInfo)) { |
| shippableItemInfo = new LinkedList<GenericValue>(); |
| for (PackingSessionLine line: getLines()) { |
| List<GenericValue> oiasgas = getDelegator().findByAnd("OrderItemAndShipGroupAssoc", UtilMisc.toMap("orderId", line.getOrderId(), "orderItemSeqId", line.getOrderItemSeqId(), "shipGroupSeqId", line.getShipGroupSeqId()), null, false); |
| shippableItemInfo.addAll(oiasgas); |
| } |
| } |
| serviceContext.put("shippableItemInfo", shippableItemInfo); |
| |
| if (UtilValidate.isEmpty(shippableWeight)) { |
| shippableWeight = getTotalWeight(); |
| } |
| serviceContext.put("shippableWeight", shippableWeight); |
| |
| if (UtilValidate.isEmpty(shippableQuantity)) { |
| shippableQuantity = getPackedQuantity(-1); |
| } |
| serviceContext.put("shippableQuantity", shippableQuantity); |
| |
| if (UtilValidate.isEmpty(shippableTotal)) { |
| shippableTotal = BigDecimal.ZERO; |
| } |
| serviceContext.put("shippableTotal", shippableTotal); |
| |
| serviceResult = getDispatcher().runSync("calcShipmentCostEstimate", serviceContext); |
| } catch (GenericEntityException e) { |
| Debug.logError(e, module); |
| } catch (GenericServiceException e) { |
| Debug.logError(e, module); |
| } |
| |
| if (UtilValidate.isNotEmpty(serviceResult)) { |
| shipmentCostEstimate = (BigDecimal) serviceResult.get("shippingEstimateAmount"); |
| } |
| |
| return shipmentCostEstimate; |
| |
| } |
| |
| public String getWeightUomId() { |
| return weightUomId; |
| } |
| |
| public void setWeightUomId(String weightUomId) { |
| this.weightUomId = weightUomId; |
| } |
| |
| public void setShipmentBoxTypeId(String shipmentBoxTypeId) { |
| this.shipmentBoxTypeId = shipmentBoxTypeId; |
| } |
| |
| public List<Integer> getPackageSeqIds() { |
| Set<Integer> packageSeqIds = new TreeSet<Integer>(); |
| if (! UtilValidate.isEmpty(this.getLines())) { |
| for (PackingSessionLine line: this.getLines()) { |
| packageSeqIds.add(Integer.valueOf(line.getPackageSeq())); |
| } |
| } |
| return UtilMisc.makeListWritable(packageSeqIds); |
| } |
| |
| public void setPackageWeight(int packageSeqId, BigDecimal packageWeight) { |
| if (UtilValidate.isEmpty(packageWeight)) { |
| packageWeights.remove(Integer.valueOf(packageSeqId)); |
| } else { |
| packageWeights.put(Integer.valueOf(packageSeqId), packageWeight); |
| } |
| } |
| |
| public BigDecimal getPackageWeight(int packageSeqId) { |
| if (this.packageWeights == null) return null; |
| BigDecimal packageWeight = null; |
| Object p = packageWeights.get(packageSeqId); |
| if (p != null) { |
| packageWeight = (BigDecimal) p; |
| } |
| return packageWeight; |
| } |
| |
| public void addToPackageWeight(int packageSeqId, BigDecimal weight) { |
| if (UtilValidate.isEmpty(weight)) return; |
| BigDecimal packageWeight = getPackageWeight(packageSeqId); |
| BigDecimal newPackageWeight = UtilValidate.isEmpty(packageWeight) ? weight : weight.add(packageWeight); |
| setPackageWeight(packageSeqId, newPackageWeight); |
| } |
| |
| // These methods (setShipmentBoxType and getShipmentBoxType) are added so that each package will have different box type. |
| public void setShipmentBoxType(int packageSeqId, String shipmentBoxType) { |
| if (UtilValidate.isEmpty(shipmentBoxType)) { |
| shipmentBoxTypes.remove(Integer.valueOf(packageSeqId)); |
| } else { |
| shipmentBoxTypes.put(Integer.valueOf(packageSeqId), shipmentBoxType); |
| } |
| } |
| |
| public String getShipmentBoxType(int packageSeqId) { |
| if (this.shipmentBoxTypes == null) return null; |
| String shipmentBoxType = null; |
| Object p = shipmentBoxTypes.get(packageSeqId); |
| if (p != null) { |
| shipmentBoxType = (String) p; |
| } |
| return shipmentBoxType; |
| } |
| |
| class ItemDisplay extends AbstractMap<Object, Object> { |
| |
| public GenericValue orderItem; |
| public BigDecimal quantity; |
| public String productId; |
| |
| public ItemDisplay(GenericValue v) { |
| if ("PicklistItem".equals(v.getEntityName())) { |
| quantity = v.getBigDecimal("quantity").setScale(2, BigDecimal.ROUND_HALF_UP); |
| try { |
| orderItem = v.getRelatedOne("OrderItem", false); |
| productId = v.getRelatedOne("InventoryItem", false).getString("productId"); |
| } catch (GenericEntityException e) { |
| Debug.logError(e, module); |
| } |
| } else { |
| // this is an OrderItemAndShipGrpInvResAndItemSum |
| orderItem = v; |
| productId = v.getString("inventoryProductId"); |
| quantity = v.getBigDecimal("totQuantityReserved").setScale(2, BigDecimal.ROUND_HALF_UP); |
| } |
| Debug.logInfo("created item display object quantity: " + quantity + " (" + productId + ")", module); |
| } |
| |
| public GenericValue getOrderItem() { |
| return orderItem; |
| } |
| |
| public BigDecimal getQuantity() { |
| return quantity; |
| } |
| |
| @Override |
| public Set<Entry<Object, Object>> entrySet() { |
| return null; |
| } |
| |
| @Override |
| public Object get(Object name) { |
| if ("orderItem".equals(name.toString())) { |
| return orderItem; |
| } else if ("quantity".equals(name.toString())) { |
| return quantity; |
| } else if ("productId".equals(name.toString())) { |
| return productId; |
| } |
| return null; |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (o instanceof ItemDisplay) { |
| ItemDisplay d = (ItemDisplay) o; |
| boolean sameOrderItemProduct = true; |
| if (d.getOrderItem().getString("productId") != null && orderItem.getString("productId") != null) { |
| sameOrderItemProduct = d.getOrderItem().getString("productId").equals(orderItem.getString("productId")); |
| } else if (d.getOrderItem().getString("productId") != null || orderItem.getString("productId") != null) { |
| sameOrderItemProduct = false; |
| } |
| return (d.productId.equals(productId) && |
| d.getOrderItem().getString("orderItemSeqId").equals(orderItem.getString("orderItemSeqId")) && |
| sameOrderItemProduct); |
| } else { |
| return false; |
| } |
| } |
| } |
| } |