| /* |
| * 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. |
| */ |
| |
| import org.apache.ofbiz.entity.condition.* |
| import org.apache.ofbiz.entity.util.* |
| import org.apache.ofbiz.service.ServiceUtil |
| import org.apache.ofbiz.base.util.* |
| |
| shipmentId = request.getParameter("shipmentId") |
| orderId = request.getParameter("purchaseOrderId") |
| shipGroupSeqId = request.getParameter("shipGroupSeqId") |
| context.shipmentId = shipmentId |
| context.shipGroupSeqId = shipGroupSeqId |
| |
| // Retrieve the map resident in session which stores order item quantities to receive |
| itemQuantitiesToReceive = session.getAttribute("purchaseOrderItemQuantitiesToReceive") |
| if (itemQuantitiesToReceive) { |
| sessionShipmentId = itemQuantitiesToReceive._shipmentId |
| sessionOrderId = itemQuantitiesToReceive._orderId |
| if ((sessionShipmentId && !sessionShipmentId.equals(shipmentId)) || |
| ((sessionOrderId && !sessionOrderId.equals(orderId))) || |
| "Y".equals(request.getParameter("clearAll"))) { |
| |
| // Clear the map if the shipmentId or orderId are different than the current ones, or |
| // if the clearAll parameter is present |
| itemQuantitiesToReceive.clear() |
| } |
| } |
| |
| shipment = from("Shipment").where("shipmentId", shipmentId).queryOne() |
| context.shipment = shipment |
| if (!shipment) { |
| return |
| } |
| |
| isPurchaseShipment = "PURCHASE_SHIPMENT".equals(shipment.shipmentTypeId) |
| context.isPurchaseShipment = isPurchaseShipment |
| if (!isPurchaseShipment) { |
| return |
| } |
| |
| facility = shipment.getRelatedOne("DestinationFacility", false) |
| context.facility = facility |
| context.facilityId = shipment.destinationFacilityId |
| context.now = UtilDateTime.nowTimestamp() |
| |
| if (!orderId) { |
| orderId = shipment.primaryOrderId |
| } |
| if (!shipGroupSeqId) { |
| shipGroupSeqId = shipment.primaryShipGroupSeqId |
| } |
| context.orderId = orderId |
| |
| if (!orderId) { |
| return |
| } |
| |
| orderHeader = from("OrderHeader").where("orderId", orderId).queryOne() |
| context.orderHeader = orderHeader |
| if (!orderHeader) { |
| return |
| } |
| |
| isPurchaseOrder = "PURCHASE_ORDER".equals(orderHeader.orderTypeId) |
| context.isPurchaseOrder = isPurchaseOrder |
| if (!isPurchaseOrder) { |
| return |
| } |
| |
| // Get the base currency from the facility owner, for currency conversions |
| baseCurrencyUomId = null |
| if (facility) { |
| owner = facility.getRelatedOne("OwnerParty", false) |
| if (owner) { |
| result = runService('getPartyAccountingPreferences', [organizationPartyId : owner.partyId, userLogin : request.getAttribute("userLogin")]) |
| if (!ServiceUtil.isError(result) && result.partyAccountingPreference) { |
| ownerAcctgPref = result.partyAccountingPreference |
| } |
| } |
| if (ownerAcctgPref) { |
| baseCurrencyUomId = ownerAcctgPref.baseCurrencyUomId |
| } |
| } |
| |
| inventoryItemTypes = from("InventoryItemType").queryList() |
| context.inventoryItemTypes = inventoryItemTypes |
| |
| // Populate the tracking map with shipment and order IDs |
| if (!itemQuantitiesToReceive) { |
| itemQuantitiesToReceive = [_shipmentId : shipmentId, _orderId : orderId] |
| } |
| |
| oiasgaLimitMap = null |
| if (shipGroupSeqId) { |
| oiasgaLimitMap = [shipGroupSeqId : shipGroupSeqId] |
| } |
| |
| orderItemDatas = [:] as TreeMap |
| totalAvailableToReceive = 0 |
| |
| // Populate the order item data for the FTL |
| orderItems = orderHeader.getRelated("OrderItemAndShipGroupAssoc", oiasgaLimitMap, ['shipGroupSeqId', 'orderItemSeqId'], false) |
| orderItems.each { orderItemAndShipGroupAssoc -> |
| product = orderItemAndShipGroupAssoc.getRelatedOne("Product", false) |
| |
| // Get the order item, since the orderItemAndShipGroupAssoc's quantity field is manipulated in some cases |
| orderItem = from("OrderItem").where("orderId", orderId, "orderItemSeqId", orderItemAndShipGroupAssoc.orderItemSeqId).queryOne() |
| orderItemData = [:] |
| |
| // Get the item's ordered quantity |
| totalOrdered = 0 |
| ordered = orderItem.getDouble("quantity") |
| if (ordered) { |
| totalOrdered += ordered.doubleValue() |
| } |
| cancelled = orderItem.getDouble("cancelQuantity") |
| if (cancelled) { |
| totalOrdered -= cancelled.doubleValue() |
| } |
| |
| // Get the item quantity received from all shipments via the ShipmentReceipt entity |
| totalReceived = 0.0 |
| receipts = from("ShipmentReceipt").where("orderId", orderId, "orderItemSeqId", orderItem.orderItemSeqId).queryList() |
| fulfilledReservations = [] as ArrayList |
| if (receipts) { |
| receipts.each { rec -> |
| accepted = rec.getDouble("quantityAccepted") |
| rejected = rec.getDouble("quantityRejected") |
| if (accepted) { |
| totalReceived += accepted.doubleValue() |
| } |
| if (rejected) { |
| totalReceived += rejected.doubleValue() |
| } |
| // Get the reservations related to this receipt |
| oisgirs = from("OrderItemShipGrpInvRes").where("inventoryItemId", rec.inventoryItemId).queryList() |
| if (oisgirs) { |
| fulfilledReservations.addAll(oisgirs) |
| } |
| } |
| } |
| orderItemData.fulfilledReservations = fulfilledReservations |
| |
| // Update the unit cost with the converted value, if any |
| if (baseCurrencyUomId && orderHeader.currencyUom) { |
| if (product) { |
| result = runService('convertUom', [uomId : orderHeader.currencyUom, uomIdTo : baseCurrencyUomId, originalValue : orderItem.unitPrice]) |
| if (!ServiceUtil.isError(result)) { |
| orderItem.unitPrice = result.convertedValue |
| } |
| } |
| } |
| |
| // Retrieve the backordered quantity |
| // TODO: limit to a facility? The shipment destination facility is not necessarily the same facility as the inventory |
| conditions = [EntityCondition.makeCondition("productId", EntityOperator.EQUALS, product.productId), |
| EntityCondition.makeCondition("availableToPromiseTotal", EntityOperator.LESS_THAN, BigDecimal.ZERO)] |
| negativeInventoryItems = from("InventoryItem").where(conditions).queryList() |
| backOrderedQuantity = 0 |
| negativeInventoryItems.each { negativeInventoryItem -> |
| backOrderedQuantity += negativeInventoryItem.getDouble("availableToPromiseTotal").doubleValue() |
| } |
| orderItemData.backOrderedQuantity = Math.abs(backOrderedQuantity) |
| |
| // Calculate how many units it should be possible to recieve for this purchase order |
| availableToReceive = totalOrdered - totalReceived |
| totalAvailableToReceive += availableToReceive |
| orderItemData.availableToReceive = availableToReceive |
| orderItemData.totalQuantityReceived = totalReceived |
| orderItemData.shipGroupSeqId = orderItemAndShipGroupAssoc.shipGroupSeqId |
| orderItemData.orderItem = orderItem |
| orderItemData.product = product |
| orderItemDatas.put(orderItem.orderItemSeqId, orderItemData) |
| } |
| context.orderItemDatas = orderItemDatas.values() |
| |
| // Handle any item product quantities to receive by adding to the map in session |
| productIdToReceive = request.getParameter("productId") |
| productQtyToReceive = request.getParameter("quantity") |
| context.newQuantity = productQtyToReceive |
| |
| if (productIdToReceive) { |
| List candidateOrderItems = EntityUtil.filterByAnd(orderItems, [productId : productIdToReceive]) |
| |
| // If the productId as given isn't found in the order, try any goodIdentifications and use the first match |
| if (!candidateOrderItems) { |
| goodIdentifications = from("GoodIdentification").where("idValue", productIdToReceive).queryList() |
| if (goodIdentifications) { |
| giit = goodIdentifications.iterator() |
| while (giit.hasNext()) { |
| goodIdentification = giit.next() |
| candidateOrderItems = EntityUtil.filterByAnd(orderItems, [productId : goodIdentification.productId]) |
| if (candidateOrderItems) { |
| productIdToReceive = goodIdentification.productId |
| break |
| } |
| } |
| } |
| } |
| |
| if (candidateOrderItems) { |
| quantity = 0 |
| if (productQtyToReceive) { |
| try { |
| quantity = Double.parseDouble(productQtyToReceive) |
| } catch (Exception e) { |
| // Ignore the quantity update if there's a problem parsing it |
| } |
| } |
| |
| totalQuantityUsed = 0 |
| totalQuantityToReceiveBefore = 0 |
| pqit = candidateOrderItems.iterator() |
| while (pqit.hasNext() && totalQuantityUsed < quantity) { |
| candidateOrderItem = pqit.next() |
| orderItemSeqId = candidateOrderItem.orderItemSeqId |
| qtyBefore = itemQuantitiesToReceive.containsKey(orderItemSeqId) ? itemQuantitiesToReceive.get(orderItemSeqId) : 0 |
| totalQuantityToReceiveBefore += qtyBefore |
| qtyMaxAvailable = orderItemDatas.get(orderItemSeqId).availableToReceive - qtyBefore |
| |
| if (qtyMaxAvailable <= 0) { |
| continue |
| } |
| |
| qtyUsedForItem = quantity - totalQuantityUsed >= qtyMaxAvailable ? qtyMaxAvailable : quantity - totalQuantityUsed |
| itemQuantitiesToReceive.put(orderItemSeqId, qtyUsedForItem + qtyBefore) |
| totalQuantityUsed += qtyUsedForItem |
| } |
| |
| // If there's any quantity to receive left after using as much as possible for every relevant order item, add an error message to the context |
| if (quantity > totalQuantityUsed) { |
| context.ProductReceiveInventoryAgainstPurchaseOrderQuantityExceedsAvailableToReceive = true |
| } |
| |
| // Notify if some or all of the quantity just entered for the product will go to a backorder |
| backOrderedQuantity = orderItemDatas.get(EntityUtil.getFirst(candidateOrderItems).orderItemSeqId).backOrderedQuantity - totalQuantityToReceiveBefore |
| |
| if (backOrderedQuantity > 0) { |
| totalQtyUsedForBackorders = backOrderedQuantity >= totalQuantityUsed ? totalQuantityUsed : backOrderedQuantity |
| if (totalQtyUsedForBackorders > 0) { |
| context.quantityToReceive = totalQuantityUsed |
| context.quantityToBackOrder = totalQtyUsedForBackorders |
| context.ProductReceiveInventoryAgainstPurchaseOrderQuantityGoesToBackOrder = true |
| } |
| } |
| } else { |
| |
| // Add an error message to the context if the productId doesn't exist in this purchase order |
| context.ProductReceiveInventoryAgainstPurchaseOrderProductNotFound = true |
| } |
| } |
| |
| // Put the tracking map back into the session, in case it has been reconstructed |
| session.setAttribute("purchaseOrderItemQuantitiesToReceive", itemQuantitiesToReceive) |
| context.itemQuantitiesToReceive = itemQuantitiesToReceive |
| context.totalAvailableToReceive = totalAvailableToReceive |