| /******************************************************************************* |
| * 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.verify; |
| |
| import java.io.Serializable; |
| import java.math.BigDecimal; |
| import java.util.HashMap; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.Map; |
| |
| import org.apache.ofbiz.base.util.Debug; |
| import org.apache.ofbiz.base.util.GeneralException; |
| import org.apache.ofbiz.base.util.UtilMisc; |
| import org.apache.ofbiz.base.util.UtilProperties; |
| 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.service.LocalDispatcher; |
| import org.apache.ofbiz.service.ServiceContainer; |
| import org.apache.ofbiz.service.ServiceUtil; |
| |
| @SuppressWarnings("serial") |
| public class VerifyPickSession implements Serializable { |
| |
| public static final String module = VerifyPickSession.class.getName(); |
| |
| protected GenericValue userLogin = null; |
| protected String dispatcherName = null; |
| protected String delegatorName = null; |
| protected String picklistBinId = null; |
| protected String facilityId = null; |
| protected List<VerifyPickSessionRow> pickRows = null; |
| |
| private transient Delegator _delegator = null; |
| private transient LocalDispatcher _dispatcher = null; |
| private static BigDecimal ZERO = BigDecimal.ZERO; |
| |
| public VerifyPickSession() { |
| } |
| |
| public VerifyPickSession(LocalDispatcher dispatcher, GenericValue userLogin) { |
| this._dispatcher = dispatcher; |
| this.dispatcherName = dispatcher.getName(); |
| this._delegator = _dispatcher.getDelegator(); |
| this.delegatorName = _delegator.getDelegatorName(); |
| this.userLogin = userLogin; |
| this.pickRows = new LinkedList<VerifyPickSessionRow>(); |
| } |
| |
| 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 void createRow(String orderId, String orderItemSeqId, String shipGroupSeqId, String productId, String originGeoId, BigDecimal quantity, Locale locale) throws GeneralException { |
| |
| if (orderItemSeqId == null && productId != null) { |
| orderItemSeqId = this.findOrderItemSeqId(productId, orderId, shipGroupSeqId, quantity, locale); |
| } |
| |
| // get the reservations for the item |
| Map<String, Object> inventoryLookupMap = new HashMap<String, Object>(); |
| inventoryLookupMap.put("orderId", orderId); |
| inventoryLookupMap.put("orderItemSeqId", orderItemSeqId); |
| inventoryLookupMap.put("shipGroupSeqId", shipGroupSeqId); |
| List<GenericValue> reservations = this.getDelegator().findByAnd("OrderItemShipGrpInvRes", inventoryLookupMap, UtilMisc.toList("quantity DESC"), false); |
| |
| // no reservations we cannot add this item |
| if (UtilValidate.isEmpty(reservations)) { |
| throw new GeneralException(UtilProperties.getMessage("ProductErrorUiLabels", "ProductErrorNoInventoryReservationsAvailableCannotVerifyThisItem", locale)); |
| } |
| |
| if (reservations.size() == 1) { |
| GenericValue reservation = EntityUtil.getFirst(reservations); |
| int checkCode = this.checkRowForAdd(reservation, orderId, orderItemSeqId, shipGroupSeqId, productId, quantity); |
| this.createVerifyPickRow(checkCode, reservation, orderId, orderItemSeqId, shipGroupSeqId, productId, originGeoId, quantity, locale); |
| } else { |
| // more than one reservation found |
| Map<GenericValue, BigDecimal> reserveQtyMap = new HashMap<GenericValue, BigDecimal>(); |
| BigDecimal qtyRemain = quantity; |
| |
| for (GenericValue reservation : reservations) { |
| if (qtyRemain.compareTo(ZERO) > 0) { |
| if (!productId.equals(reservation.getRelatedOne("InventoryItem", false).getString("productId"))) { |
| continue; |
| } |
| BigDecimal reservedQty = reservation.getBigDecimal("quantity"); |
| BigDecimal resVerifiedQty = this.getVerifiedQuantity(orderId, orderItemSeqId, shipGroupSeqId, productId, reservation.getString("inventoryItemId")); |
| if (resVerifiedQty.compareTo(reservedQty) >= 0) { |
| continue; |
| } else { |
| reservedQty = reservedQty.subtract(resVerifiedQty); |
| } |
| BigDecimal thisQty = reservedQty.compareTo(qtyRemain) > 0 ? qtyRemain : reservedQty; |
| int thisCheck = this.checkRowForAdd(reservation, orderId, orderItemSeqId, shipGroupSeqId, productId, thisQty); |
| switch (thisCheck) { |
| case 2: |
| // new verify pick row will be created |
| reserveQtyMap.put(reservation, thisQty); |
| qtyRemain = qtyRemain.subtract(thisQty); |
| break; |
| case 1: |
| // existing verify pick row has been updated |
| qtyRemain = qtyRemain.subtract(thisQty); |
| break; |
| case 0: |
| //doing nothing |
| break; |
| } |
| } |
| } |
| if (qtyRemain.compareTo(ZERO) == 0) { |
| for (Map.Entry<GenericValue, BigDecimal> entry : reserveQtyMap.entrySet()) { |
| GenericValue reservation = entry.getKey(); |
| BigDecimal qty = entry.getValue(); |
| this.createVerifyPickRow(2, reservation, orderId, orderItemSeqId, shipGroupSeqId, productId, originGeoId, qty, locale); |
| } |
| } else { |
| throw new GeneralException(UtilProperties.getMessage("ProductErrorUiLabels", "ProductErrorNotEnoughInventoryReservationAvailableCannotVerifyTheItem", locale)); |
| } |
| } |
| } |
| |
| protected String findOrderItemSeqId(String productId, String orderId, String shipGroupSeqId, BigDecimal quantity, Locale locale) throws GeneralException { |
| |
| Map<String, Object> orderItemLookupMap = new HashMap<String, Object>(); |
| orderItemLookupMap.put("orderId", orderId); |
| orderItemLookupMap.put("productId", productId); |
| orderItemLookupMap.put("statusId", "ITEM_APPROVED"); |
| orderItemLookupMap.put("shipGroupSeqId", shipGroupSeqId); |
| |
| List<GenericValue> orderItems = this.getDelegator().findByAnd("OrderItemAndShipGroupAssoc", orderItemLookupMap, null, false); |
| |
| String orderItemSeqId = null; |
| if (orderItems != null) { |
| for (GenericValue orderItem : orderItems) { |
| // get the reservations for the item |
| Map<String, Object> inventoryLookupMap = new HashMap<String, Object>(); |
| inventoryLookupMap.put("orderId", orderId); |
| inventoryLookupMap.put("orderItemSeqId", orderItem.getString("orderItemSeqId")); |
| inventoryLookupMap.put("shipGroupSeqId", shipGroupSeqId); |
| List<GenericValue> reservations = this.getDelegator().findByAnd("OrderItemShipGrpInvRes", inventoryLookupMap, null, false); |
| for (GenericValue reservation : reservations) { |
| BigDecimal qty = reservation.getBigDecimal("quantity"); |
| if (quantity.compareTo(qty) <= 0) { |
| orderItemSeqId = orderItem.getString("orderItemSeqId"); |
| break; |
| } |
| } |
| } |
| } |
| |
| if (orderItemSeqId != null) { |
| return orderItemSeqId; |
| } else { |
| throw new GeneralException(UtilProperties.getMessage("ProductErrorUiLabels", "ProductErrorNoValidOrderItemFoundForProductWithEnteredQuantity", UtilMisc.toMap("productId", productId, "quantity", quantity), locale)); |
| } |
| } |
| |
| protected int checkRowForAdd(GenericValue reservation, String orderId, String orderItemSeqId, String shipGroupSeqId, String productId, BigDecimal quantity) { |
| // check to see if the reservation can hold the requested quantity amount |
| String inventoryItemId = reservation.getString("inventoryItemId"); |
| BigDecimal resQty = reservation.getBigDecimal("quantity"); |
| VerifyPickSessionRow pickRow = this.getPickRow(orderId, orderItemSeqId, shipGroupSeqId, productId, inventoryItemId); |
| |
| if (pickRow == null) { |
| if (resQty.compareTo(quantity) < 0) { |
| return 0; |
| } else { |
| return 2; |
| } |
| } else { |
| BigDecimal newQty = pickRow.getReadyToVerifyQty().add(quantity); |
| if (resQty.compareTo(newQty) < 0) { |
| return 0; |
| } else { |
| pickRow.setReadyToVerifyQty(newQty); |
| return 1; |
| } |
| } |
| } |
| |
| protected void createVerifyPickRow(int checkCode, GenericValue res, String orderId, String orderItemSeqId, String shipGroupSeqId, String productId, String originGeoId,BigDecimal quantity, Locale locale) throws GeneralException { |
| // process the result; add new item if necessary |
| switch (checkCode) { |
| case 0: |
| // not enough reserved |
| throw new GeneralException(UtilProperties.getMessage("ProductErrorUiLabels", "ProductErrorNotEnoughInventoryReservationAvailableCannotVerifyTheItem", locale)); |
| case 1: |
| // we're all good to go; quantity already updated |
| break; |
| case 2: |
| // need to create a new item |
| String inventoryItemId = res.getString("inventoryItemId"); |
| pickRows.add(new VerifyPickSessionRow(orderId, orderItemSeqId, shipGroupSeqId, productId, originGeoId, inventoryItemId, quantity)); |
| break; |
| } |
| } |
| |
| public GenericValue getUserLogin() { |
| return this.userLogin; |
| } |
| |
| public void setFacilityId(String facilityId) { |
| this.facilityId = facilityId; |
| } |
| |
| public String getFacilityId() { |
| return this.facilityId; |
| } |
| |
| public void setPicklistBinId(String setPicklistBinId) { |
| this.picklistBinId = setPicklistBinId; |
| } |
| |
| public String getPicklistBinId() { |
| return this.picklistBinId; |
| } |
| |
| public List<VerifyPickSessionRow> getPickRows() { |
| return this.pickRows; |
| } |
| |
| public List<VerifyPickSessionRow> getPickRows(String orderId) { |
| List<VerifyPickSessionRow> pickVerifyRows = new LinkedList<VerifyPickSessionRow>(); |
| for (VerifyPickSessionRow line: this.getPickRows()) { |
| if (orderId.equals(line.getOrderId())) { |
| pickVerifyRows.add(line); |
| } |
| } |
| return pickVerifyRows; |
| } |
| |
| public BigDecimal getReadyToVerifyQuantity(String orderId, String orderSeqId) throws GeneralException { |
| BigDecimal readyToVerifyQty = BigDecimal.ZERO; |
| for (VerifyPickSessionRow line: this.getPickRows()) { |
| if ((orderId.equals(line.getOrderId())) && (orderSeqId.equals(line.getOrderItemSeqId()))) { |
| readyToVerifyQty = readyToVerifyQty.add(line.getReadyToVerifyQty()); |
| } |
| } |
| return readyToVerifyQty; |
| } |
| |
| public VerifyPickSessionRow getPickRow(String orderId, String orderItemSeqId, String shipGroupSeqId, String productId, String inventoryItemId) { |
| for (VerifyPickSessionRow line : this.getPickRows(orderId)) { |
| if (orderItemSeqId.equals(line.getOrderItemSeqId()) && shipGroupSeqId.equals(line.getShipGroupSeqId()) |
| && productId.equals(line.getProductId()) && inventoryItemId.equals(line.getInventoryItemId())) { |
| return line; |
| } |
| } |
| return null; |
| } |
| |
| public BigDecimal getVerifiedQuantity(String orderId, String orderItemSeqId, String shipGroupSeqId, String productId, String inventoryItemId) { |
| BigDecimal total = ZERO; |
| for (VerifyPickSessionRow pickRow : this.getPickRows(orderId)) { |
| if (orderItemSeqId.equals(pickRow.getOrderItemSeqId()) && shipGroupSeqId.equals(pickRow.getShipGroupSeqId()) && productId.equals(pickRow.getProductId())) { |
| if (inventoryItemId == null || inventoryItemId.equals(pickRow.getInventoryItemId())) { |
| total = total.add(pickRow.getReadyToVerifyQty()); |
| } |
| } |
| } |
| return total; |
| } |
| |
| public void clearAllRows() { |
| this.pickRows.clear(); |
| } |
| |
| public String complete(String orderId, Locale locale) throws GeneralException { |
| this.checkVerifiedQty(orderId, locale); |
| // check reserved quantity, it should be equal to verified quantity |
| this.checkReservedQty(orderId, locale); |
| String shipmentId = this.createShipment((this.getPickRows(orderId)).get(0)); |
| |
| this.issueItemsToShipment(shipmentId, locale); |
| this.updateProduct(); |
| |
| // Update the shipment status to Picked, this will trigger createInvoicesFromShipment and finally a invoice will be created |
| Map<String, Object> updateShipmentCtx = new HashMap<String, Object>(); |
| updateShipmentCtx.put("shipmentId", shipmentId); |
| updateShipmentCtx.put("statusId", "SHIPMENT_PICKED"); |
| updateShipmentCtx.put("userLogin", this.getUserLogin()); |
| this.getDispatcher().runSync("updateShipment", updateShipmentCtx); |
| |
| return shipmentId; |
| } |
| |
| protected void checkReservedQty(String orderId, Locale locale) throws GeneralException { |
| List<String> errorList = new LinkedList<String>(); |
| for (VerifyPickSessionRow pickRow : this.getPickRows(orderId)) { |
| BigDecimal reservedQty = this.getReservedQty(pickRow.getOrderId(), pickRow.getOrderItemSeqId(), pickRow.getShipGroupSeqId()); |
| BigDecimal verifiedQty = this.getReadyToVerifyQuantity(pickRow.getOrderId(), pickRow.getOrderItemSeqId()); |
| if (verifiedQty.compareTo(reservedQty) != 0) { |
| errorList.add(UtilProperties.getMessage("ProductErrorUiLabels", "ProductErrorVerifiedQtyDoesNotMatchTheReservedQtyForItem", UtilMisc.toMap("productId", pickRow.getProductId(), "verifiedQty", pickRow.getReadyToVerifyQty(), "reservedQty", reservedQty), locale)); |
| } |
| } |
| |
| if (errorList.size() > 0) { |
| throw new GeneralException(UtilProperties.getMessage("OrderErrorUiLabels", "OrderErrorAttemptToVerifyOrderFailed", UtilMisc.toMap("orderId", orderId), locale), errorList); |
| } |
| } |
| |
| public BigDecimal getReservedQty(String orderId, String orderItemSeqId, String shipGroupSeqId) { |
| BigDecimal reservedQty = ZERO; |
| try { |
| GenericValue reservation = EntityUtil.getFirst(this.getDelegator().findByAnd("OrderItemAndShipGrpInvResAndItemSum", UtilMisc.toMap("orderId", orderId, "orderItemSeqId", orderItemSeqId, "shipGroupSeqId", shipGroupSeqId), null, false)); |
| reservedQty = reservation.getBigDecimal("totQuantityAvailable"); |
| } catch (GenericEntityException e) { |
| Debug.logError(e, module); |
| } |
| return reservedQty; |
| } |
| |
| protected void checkVerifiedQty(String orderId, Locale locale) throws GeneralException { |
| |
| BigDecimal verifiedQty = ZERO; |
| BigDecimal orderedQty = ZERO; |
| |
| List<GenericValue> orderItems = this.getDelegator().findByAnd("OrderItem", UtilMisc.toMap("orderId", orderId, "statusId", "ITEM_APPROVED"), null, false); |
| for (GenericValue orderItem : orderItems) { |
| orderedQty = orderedQty.add(orderItem.getBigDecimal("quantity")); |
| } |
| |
| for (VerifyPickSessionRow pickRow : this.getPickRows(orderId)) { |
| verifiedQty = verifiedQty.add(pickRow.getReadyToVerifyQty()); |
| } |
| |
| if (orderedQty.compareTo(verifiedQty) != 0) { |
| throw new GeneralException(UtilProperties.getMessage("ProductErrorUiLabels", "ProductErrorAllOrderItemsAreNotVerified", locale)); |
| } |
| } |
| |
| protected void issueItemsToShipment(String shipmentId, Locale locale) throws GeneralException { |
| List<VerifyPickSessionRow> processedRows = new LinkedList<VerifyPickSessionRow>(); |
| for (VerifyPickSessionRow pickRow : this.getPickRows()) { |
| if (this.checkLine(processedRows, pickRow)) { |
| BigDecimal totalVerifiedQty = this.getVerifiedQuantity(pickRow.getOrderId(), pickRow.getOrderItemSeqId(), pickRow.getShipGroupSeqId(), pickRow.getProductId(), pickRow.getInventoryItemId()); |
| pickRow.issueItemToShipment(shipmentId, picklistBinId, userLogin, totalVerifiedQty, getDispatcher(), locale); |
| processedRows.add(pickRow); |
| } |
| } |
| } |
| |
| protected boolean checkLine(List<VerifyPickSessionRow> processedRows, VerifyPickSessionRow pickrow) { |
| for (VerifyPickSessionRow processedRow : processedRows) { |
| if (pickrow.isSameItem(processedRow)) { |
| pickrow.setShipmentItemSeqId(processedRow.getShipmentItemSeqId()); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| protected String createShipment(VerifyPickSessionRow line) throws GeneralException { |
| Delegator delegator = this.getDelegator(); |
| String orderId = line.getOrderId(); |
| Map<String, Object> newShipment = new HashMap<String, Object>(); |
| newShipment.put("originFacilityId", facilityId); |
| newShipment.put("primaryShipGroupSeqId", line.getShipGroupSeqId()); |
| newShipment.put("primaryOrderId", orderId); |
| newShipment.put("shipmentTypeId", "OUTGOING_SHIPMENT"); |
| newShipment.put("statusId", "SHIPMENT_SCHEDULED"); |
| newShipment.put("userLogin", this.getUserLogin()); |
| GenericValue orderRoleShipTo = EntityQuery.use(delegator).from("OrderRole").where("orderId", orderId, "roleTypeId", "SHIP_TO_CUSTOMER").queryFirst(); |
| if (UtilValidate.isNotEmpty(orderRoleShipTo)) { |
| newShipment.put("partyIdTo", orderRoleShipTo.getString("partyId")); |
| } |
| String partyIdFrom = null; |
| GenericValue orderItemShipGroup = EntityQuery.use(delegator).from("OrderItemShipGroup").where("orderId", orderId, "shipGroupSeqId", line.getShipGroupSeqId()).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", orderId, "roleTypeId", "SHIP_FROM_VENDOR").queryFirst(); |
| if (UtilValidate.isNotEmpty(orderRoleShipFrom)) { |
| partyIdFrom = orderRoleShipFrom.getString("partyId"); |
| } else { |
| orderRoleShipFrom = EntityQuery.use(delegator).from("OrderRole").where("orderId", orderId, "roleTypeId", "BILL_FROM_VENDOR").queryFirst(); |
| partyIdFrom = orderRoleShipFrom.getString("partyId"); |
| } |
| } |
| newShipment.put("partyIdFrom", partyIdFrom); |
| Map<String, Object> newShipResp = this.getDispatcher().runSync("createShipment", newShipment); |
| if (ServiceUtil.isError(newShipResp)) { |
| throw new GeneralException(ServiceUtil.getErrorMessage(newShipResp)); |
| } |
| String shipmentId = (String) newShipResp.get("shipmentId"); |
| return shipmentId; |
| } |
| |
| protected void updateProduct() throws GeneralException { |
| for (VerifyPickSessionRow pickRow : this.getPickRows()) { |
| if (UtilValidate.isNotEmpty(pickRow.getOriginGeoId())) { |
| Map<String, Object> updateProductCtx = new HashMap<String, Object>(); |
| updateProductCtx.put("originGeoId", pickRow.getOriginGeoId()); |
| updateProductCtx.put("productId", pickRow.getProductId()); |
| updateProductCtx.put("userLogin", this.getUserLogin()); |
| Map<String, Object> result = this.getDispatcher().runSync("updateProduct", updateProductCtx); |
| if (ServiceUtil.isError(result)) { |
| throw new GeneralException(ServiceUtil.getErrorMessage(result)); |
| } |
| } |
| } |
| } |
| } |