/******************************************************************************* * 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)); } } } } }