You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ofbiz.apache.org by jl...@apache.org on 2020/06/24 08:57:38 UTC
[ofbiz-framework] branch trunk updated: OFBIZ-11468 Improved:
Convert ShipmentReceiptServices.xml mini lang to groovy (#150)
This is an automated email from the ASF dual-hosted git repository.
jleroux pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ofbiz-framework.git
The following commit(s) were added to refs/heads/trunk by this push:
new 7335ce0 OFBIZ-11468 Improved: Convert ShipmentReceiptServices.xml mini lang to groovy (#150)
7335ce0 is described below
commit 7335ce021a3d6d241306087a4d0a97dcb8d08234
Author: wpaetzold <12...@users.noreply.github.com>
AuthorDate: Wed Jun 24 10:57:28 2020 +0200
OFBIZ-11468 Improved: Convert ShipmentReceiptServices.xml mini lang to groovy (#150)
* Improved: Convert ShipmentReceiptServices.xml mini lang to groovy
(OFBIZ-11468)
Also converted getTotalIssuedQuantityForOrderItem in IssuanceServices, because it is used in ShipmentReceiptServices and needed more return values.
* split to long lines
Co-authored-by: Wiebke Pätzold <Wi...@ecomify.de>
---
.../shipment/issuance/IssuanceServices.groovy | 44 ++
.../receipt/ShipmentReceiptServices.groovy | 500 ++++++++++++++++++
.../shipment/receipt/ShipmentReceiptServices.xml | 577 ---------------------
.../product/servicedef/services_shipment.xml | 38 +-
4 files changed, 570 insertions(+), 589 deletions(-)
diff --git a/applications/product/groovyScripts/shipment/issuance/IssuanceServices.groovy b/applications/product/groovyScripts/shipment/issuance/IssuanceServices.groovy
new file mode 100644
index 0000000..1c683e0
--- /dev/null
+++ b/applications/product/groovyScripts/shipment/issuance/IssuanceServices.groovy
@@ -0,0 +1,44 @@
+/*
+ * 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.GenericValue
+
+/**
+ * Computes the total quantity assigned to shipment for a purchase order item
+ * @return
+ */
+def getTotalIssuedQuantityForOrderItem() {
+ Map result = success()
+ GenericValue orderItem = parameters.orderItem
+ BigDecimal totalIssuedQuantity = 0.0
+ List orderShipments = from("OrderShipment").where(orderId: orderItem.orderId, orderItemSeqId: orderItem.orderItemSeqId).queryList()
+ if (orderShipments) {
+ for (GenericValue orderShipment : orderShipments) {
+ totalIssuedQuantity += orderShipment.quantity
+ }
+ } else {
+ // This is here for backward compatibility only: ItemIssuances are no more created for purchase orders
+ List allItemIssuances = from("ItemIssuance").where(orderId: orderItem.orderId, orderItemSeqId: orderItem.orderItemSeqId).queryList()
+ for (GenericValue itemIssuance : allItemIssuances) {
+ totalIssuedQuantity += itemIssuance.quantity
+ }
+ }
+ result.totalIssuedQuantity = totalIssuedQuantity
+ return result
+}
diff --git a/applications/product/groovyScripts/shipment/receipt/ShipmentReceiptServices.groovy b/applications/product/groovyScripts/shipment/receipt/ShipmentReceiptServices.groovy
new file mode 100644
index 0000000..2646ed7
--- /dev/null
+++ b/applications/product/groovyScripts/shipment/receipt/ShipmentReceiptServices.groovy
@@ -0,0 +1,500 @@
+/*
+ * 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 java.math.RoundingMode
+import java.sql.Timestamp
+
+import org.apache.ofbiz.base.util.UtilDateTime
+import org.apache.ofbiz.base.util.UtilProperties
+import org.apache.ofbiz.entity.GenericValue
+import org.apache.ofbiz.entity.condition.EntityCondition
+
+/**
+ * Create a ShipmentReceipt
+ * @return
+ */
+def createShipmentReceipt() {
+ Map result = success()
+ GenericValue newEntity = makeValue("ShipmentReceipt")
+ newEntity.setNonPKFields(parameters)
+
+ String receiptId = delegator.getNextSeqId("ShipmentReceipt").toString()
+ newEntity.receiptId = receiptId
+ result.receiptId = receiptId
+
+ if (!newEntity.datetimeReceived) {
+ newEntity.datetimeReceived = UtilDateTime.nowTimestamp()
+ }
+ newEntity.receivedByUserLoginId = userLogin.userLoginId
+ newEntity.create()
+
+ if (parameters.inventoryItemDetailSeqId) {
+ GenericValue invDet = from("InventoryItemDetail")
+ .where(inventoryItemDetailSeqId: parameters.inventoryItemDetailSeqId, inventoryItemId: parameters.inventoryItemId)
+ .queryOne()
+ invDet.receiptId = receiptId
+ invDet.store()
+ }
+ Boolean affectAccounting = true
+
+ GenericValue product = from("Product").where(parameters).queryOne()
+ if (product.productTypeId == "SERVICE_PRODUCT"
+ || product.productTypeId == "ASSET_USAGE_OUT_IN"
+ || product.productTypeId == "AGGREGATEDSERV_CONF") {
+ affectAccounting = false
+ }
+ result.affectAccounting = affectAccounting
+ return result
+}
+
+/**
+ * Receive Inventory in new Inventory Item(s)
+ * @return success, inventoryItemId, successMessageList
+ */
+def receiveInventoryProduct () {
+ /*
+ * NOTES
+ *
+ * - for serialized items with a serial number passed in: the quantityAccepted _should_ always be 1
+ * - if the type is SERIALIZED_INV_ITEM but there is not serial number (which is weird...) we'll create a bunch of individual InventoryItems
+ * - DEJ20070822: something to consider for the future:
+ * maybe instead of this funny looping maybe for serialized items we should only allow a quantity of 1, ie return an error if it is not 1
+ */
+ Map result = success()
+ List successMessageList =[]
+ String currentInventoryItemId
+ Double loops = 1.0
+ if (parameters.inventoryItemTypeId == "SERIALIZED_INV_ITEM") {
+ // if we are serialized and either a serialNumber or inventoyItemId is passed in and the quantityAccepted is greater than 1 then complain
+ if ((parameters.serialNumber || parameters.currentInventoryItemId) && (parameters.quantityAccepted > (BigDecimal) 1)) {
+ Map errorLog = [parameters: parameters]
+ return error(UtilProperties.getMessage("ProductUiLabels", "FacilityReceiveInventoryProduct", errorLog, parameters.locale))
+ // before getting going, see if there are any validation issues so far
+ }
+ loops = parameters.quantityAccepted
+ parameters.quantityAccepted = (BigDecimal) 1
+ }
+ parameters.quantityOnHandDiff = parameters.quantityAccepted
+ parameters.availableToPromiseDiff = parameters.quantityAccepted
+
+ //Status for Non serialized and Serialized inventory are different, lets make sure correct status is stored in database
+ if (parameters.inventoryItemTypeId == "NON_SERIAL_INV_ITEM") {
+ if (parameters.statusId == "INV_DEFECTIVE") {
+ // This status may come from the Receive Return Screen
+ parameters.statusId = "INV_NS_DEFECTIVE"
+ } else if (parameters.statusId == "INV_ON_HOLD") {
+ parameters.statusId = "INV_NS_ON_HOLD"
+ } else if (parameters.statusId == "INV_RETURNED") {
+ parameters.statusId = "INV_NS_RETURNED"
+ } else {
+ // Any other status should be just set to null, if it is not a valid status for Non Serialized inventory
+ parameters.statusId = null
+ }
+ }
+
+ for (Double currentLoop = 0; currentLoop <= loops; currentLoop++) {
+ logInfo("receiveInventoryProduct Looping and creating inventory info - ${currentLoop}")
+
+ // if there is an inventoryItemId, update it (this will happen when receiving serialized inventory already in the system, like for returns); if not create one
+ Map serviceInMap = [:]
+ currentInventoryItemId = null
+
+ // Set supplier partyId, if inventory received by purchase order
+ if (parameters.orderId) {
+ GenericValue orderRole = from("OrderRole")
+ .where(orderId: parameters.orderId, roleTypeId: "SUPPLIER_AGENT")
+ .queryFirst()
+ if (orderRole) {
+ parameters.partyId = orderRole.partyId
+ }
+ }
+ if (!parameters.currentInventoryItemId) {
+ Map serviceResult = run service:"createInventoryItem", with: parameters
+ currentInventoryItemId = serviceResult.inventoryItemId
+ } else {
+ if (parameters.currentInventoryItemId) {
+ parameters.inventoryItemId = parameters.currentInventoryItemId
+ }
+ run service:"updateInventoryItem", with: parameters
+ currentInventoryItemId = parameters.currentInventoryItemId
+ }
+
+ // do this only for non-serialized inventory
+ if (parameters.inventoryItemTypeId != "SERIALIZED_INV_ITEM") {
+ serviceInMap = [:]
+ serviceInMap = parameters
+ serviceInMap.inventoryItemId = currentInventoryItemId
+ Map serviceCIID = run service:"createInventoryItemDetail", with: serviceInMap
+ parameters.inventoryItemDetailSeqId = serviceCIID.inventoryItemDetailSeqId
+ }
+ serviceInMap = [:]
+ serviceInMap = parameters
+ serviceInMap.inventoryItemId = currentInventoryItemId
+ run service:"createShipmentReceipt", with: serviceInMap
+
+ //update serialized items to AVAILABLE (only if this is not a return), which then triggers other SECA chains
+ if (parameters.inventoryItemTypeId == "SERIALIZED_INV_ITEM" && !parameters.returnId) {
+ // Retrieve the new inventoryItem
+ GenericValue inventoryItem = from("InventoryItem").where(inventoryItemId: currentInventoryItemId).queryOne()
+
+ // Don't reset the status if it's already set to INV_PROMISED or INV_ON_HOLD
+ if (inventoryItem.statusId != "INV_PROMISED" && inventoryItem.statusId != "INV_ON_HOLD") {
+ serviceInMap = [:]
+ serviceInMap.inventoryItemId = currentInventoryItemId
+ serviceInMap.statusId = "INV_AVAILABLE" // XXX set to returned instead
+ run service:"updateInventoryItem", with: serviceInMap
+ }
+ }
+ serviceInMap = [:]
+ serviceInMap = parameters
+ serviceInMap.inventoryItemId = currentInventoryItemId
+ run service:"balanceInventoryItems", with: serviceInMap
+
+ successMessageList << "Received ${parameters.quantityAccepted} of ${parameters.productId} in inventory item ${currentInventoryItemId}"
+ }
+ // return the last inventory item received
+ result.inventoryItemId = currentInventoryItemId
+ result.successMessageList = successMessageList
+
+ return result
+}
+
+/**
+ * Quick Receive Entire Return
+ * @return
+ */
+def quickReceiveReturn() {
+ Map result = success()
+ GenericValue returnHeader = from("ReturnHeader").where(returnId: parameters.returnId).queryOne()
+ if (returnHeader.needsInventoryReceive == "Y") {
+ // before receiving inventory, check to see if there is inventory information in this database
+ Long iiCount = from("InventoryItem").where(facilityId: returnHeader.destinationFacilityId).queryCount()
+
+ if (iiCount > (Integer) 0) {
+ // create a return shipment for this return
+ Map shipmentCtx = [returnId: parameters.returnId]
+ Map serviceCSFR = run service:"createShipmentForReturn", with: shipmentCtx
+ String shipmentId = serviceCSFR.shipmentId
+ logInfo("Created new shipment ${shipmentId}")
+
+ List returnItems = from("ReturnItem").where(returnId: returnHeader.returnId).queryList()
+
+ // if no inventory item type specified, get default from facility
+ if(!parameters.inventoryItemTypeId) {
+ GenericValue facility = delegator.getRelatedOne("Facility", returnHeader, false)
+ parameters.inventoryItemTypeId = facility.defaultInventoryItemTypeId ?: "NON_SERIAL_INV_ITEM"
+ }
+ Timestamp nowTimestamp = UtilDateTime.nowTimestamp()
+
+ Long returnItemCount = from("ReturnItem").where(returnId: returnHeader.returnId).queryCount()
+ Long nonProductItems = (Long) 0
+
+ for (GenericValue returnItem : returnItems) {
+ // record this return item on the return shipment as well. not sure if this is actually necessary...
+ Map shipItemCtx = [shipmentId: shipmentId, productId: returnItem.productId, quantity: returnItem.returnQuantity]
+ logInfo("calling create shipment item with ${shipItemCtx}")
+ Map serviceCSI = run service:"createShipmentItem", with: shipItemCtx
+ String shipmentItemSeqId = serviceCSI.shipmentItemSeqId
+ }
+ for (GenericValue returnItem : returnItems) {
+ Map receiveCtx = [:]
+ if (!returnItem.expectedItemStatus) {
+ returnItem.expectedItemStatus = "INV_RETURNED"
+ }
+ GenericValue orderItem = delegator.getRelatedOne("OrderItem", returnItem, false)
+ if (orderItem?.productId) {
+ Map costCtx = [returnItemSeqId: returnItem.returnItemSeqId, returnId: returnItem.returnId]
+ Map serviceGRIIC = run service:"getReturnItemInitialCost", with: costCtx
+ receiveCtx.unitCost = serviceGRIIC.initialItemCost
+
+ // check if the items already have SERIALIZED inventory. If so, it still puts them back as SERIALIZED with status "Accepted."
+ Long serializedItemCount = from("InventoryItem")
+ .where(productId: returnItem.productId, facilityId: returnHeader.destinationFacilityId, inventoryItemTypeId: "SERIALIZED_INV_ITEM")
+ .queryCount()
+ Boolean setNonSerial = false
+ if (parameters.inventoryItemTypeId == "NON_SERIAL_INV_ITEM") {
+ if (serializedItemCount == 0) {
+ parameters.inventoryItemTypeId = "NON_SERIAL_INV_ITEM"
+ setNonSerial = true
+ }
+ }
+ if (!setNonSerial) {
+ parameters.inventoryItemTypeId = "SERIALIZED_INV_ITEM"
+ returnItem.returnQuantity = (BigDecimal) 1
+ }
+ receiveCtx = [inventoryItemTypeId: parameters.inventoryItemTypeId,
+ statusId: returnItem.expectedItemStatus,
+ productId: returnItem.productId,
+ returnItemSeqId: returnItem.returnItemSeqId,
+ returnId: returnItem.returnId,
+ quantityAccepted: returnItem.returnQuantity,
+ facilityId: returnHeader.destinationFacilityId,
+ shipmentId: shipmentId, // important: associate ShipmentReceipt with return shipment created
+ comments: "Returned Item RA# ${returnItem.returnId}",
+ datetimeReceived: nowTimestamp,
+ quantityRejected: (BigDecimal) 0
+ ]
+ Map serviceResult = run service:"receiveInventoryProduct", with: receiveCtx
+ result.successMessageList = serviceResult.successMessageList
+ } else {
+ nonProductItems += (Long) 1
+ }
+ }
+ // now that the receive is done; set the need flag to N
+ returnHeader.refresh()
+ returnHeader.needsInventoryReceive = "N"
+ returnHeader.store()
+
+ // always check/update the ReturnHeader status, even though it might have been from the receiving above, just make sure
+ if (returnHeader.statusId != "RETURN_RECEIVED") {
+ Map retStCtx = [returnId: returnHeader.returnId, statusId: "RETURN_RECEIVED"]
+ run service:"updateReturnHeader", with: retStCtx
+ }
+ } else {
+ logInfo("Not receiving inventory for returnId ${returnHeader.returnId}, no inventory information available.")
+ }
+ }
+ return result
+}
+
+/**
+ * Issues order item quantity specified to the shipment, then receives inventory for that item and quantity
+ * @return
+ */
+def issueOrderItemToShipmentAndReceiveAgainstPO() {
+ Map result = success()
+ String shipmentItemSeqId
+ GenericValue shipmentItem
+ // get orderItem
+ GenericValue orderItem = from("OrderItem").where(parameters).queryOne()
+ // get orderItemShipGroupAssoc
+ GenericValue orderItemShipGroupAssoc = from("OrderItemShipGroupAssoc").where(parameters).queryOne()
+ // get shipment
+ GenericValue shipment = from("Shipment").where(parameters).queryOne()
+
+ // try to find an existing shipmentItem and attach to it, if none found create a new shipmentItem
+ // if there is NO productId on the orderItem, ALWAYS create a new shipmentItem
+ if (orderItem?.productId) {
+ EntityCondition condition = EntityCondition.makeCondition([
+ EntityCondition.makeCondition(productId: orderItem.productId),
+ EntityCondition.makeCondition(shipmentId: shipment.shipmentId)
+ ])
+ if (parameters.shipmentItemSeqId) {
+ condition = EntityCondition.makeCondition([
+ EntityCondition.makeCondition(shipmentItemSeqId: parameters.shipmentItemSeqId),
+ condition
+ ])
+ }
+ shipmentItem = from("ShipmentItem")
+ .where(condition)
+ .orderBy("shipmentItemSeqId")
+ .queryFirst()
+ }
+ if (!shipmentItem) {
+ Map shipmentItemCreate = [productId: orderItem.productId, shipmentId: parameters.shipmentId, quantity: parameters.quantity]
+ Map serviceResult = run service:"createShipmentItem", with: shipmentItemCreate
+ Map shipmentItemLookupPk = [shipmentItemSeqId: serviceResult.shipmentItemSeqId, shipmentId: parameters.shipmentId]
+ shipmentItem = from("ShipmentItem").where(shipmentItemLookupPk).queryOne()
+
+ // Create OrderShipment for this ShipmentItem
+ Map orderShipmentCreate =[quantity: parameters.quantity,
+ shipmentId: shipmentItem.shipmentId,
+ shipmentItemSeqId: shipmentItem.shipmentItemSeqId,
+ orderId: orderItem.orderId,
+ orderItemSeqId: orderItem.orderItemSeqId]
+ if (orderItemShipGroupAssoc) {
+ // If we have a ShipGroup Assoc for this Item to focus on, set that; this is mostly the case for purchase orders and such
+ orderShipmentCreate.shipGroupSeqId = orderItemShipGroupAssoc.shipGroupSeqId
+ }
+ run service:"createOrderShipment", with: orderShipmentCreate
+ } else {
+ Map inputMap = parameters
+ inputMap.orderItem = orderItem
+ Map serviceResult = run service:"getTotalIssuedQuantityForOrderItem", with: inputMap
+ BigDecimal totalIssuedQuantity = serviceResult.totalIssuedQuantity
+ BigDecimal receivedQuantity = getReceivedQuantityForOrderItem(orderItem)
+ receivedQuantity += parameters.quantity
+ GenericValue orderShipment = from("OrderShipment")
+ .where(orderId: orderItem.orderId,
+ orderItemSeqId: orderItem.orderItemSeqId,
+ shipmentId: shipmentItem.shipmentId,
+ shipmentItemSeqId: shipmentItem.shipmentItemSeqId,
+ shipGroupSeqId: orderItemShipGroupAssoc.shipGroupSeqId)
+ .queryFirst()
+ if (totalIssuedQuantity < receivedQuantity) {
+ BigDecimal quantityToAdd = receivedQuantity - totalIssuedQuantity
+ shipmentItem.quantity += quantityToAdd
+ shipmentItem.store()
+ shipmentItemSeqId = shipmentItem.shipmentItemSeqId
+
+ orderShipment.quantity = orderShipment.quantity + quantityToAdd
+ orderShipment.store()
+ }
+ }
+ // TODO: if we want to record the role of the facility operation we have to re-implement this using ShipmentReceiptRole
+ // <call-simple-method method-name="associateIssueRoles" xml-resource="component://product/minilang/shipment/issuance/IssuanceServices.xml"/>
+
+ Map receiveInventoryProductCtx = parameters
+ receiveInventoryProductCtx.shipmentItemSeqId = shipmentItemSeqId
+ Map serviceResult = run service:"receiveInventoryProduct", with:receiveInventoryProductCtx
+ result.inventoryItemId = serviceResult.inventoryItemId
+ result.successMessageList = serviceResult.successMessageList
+
+ return result
+}
+
+/**
+ * Computes the till now received quantity from all ShipmentReceipts
+ * @return
+ */
+def getReceivedQuantityForOrderItem (GenericValue orderItem) {
+ BigDecimal receivedQuantity = 0
+ List shipmentReceipts = from("ShipmentReceipt").where(orderId: orderItem.orderId, orderItemSeqId: orderItem.orderItemSeqId).queryList()
+ for (GenericValue shipmentReceipt : shipmentReceipts) {
+ receivedQuantity += shipmentReceipt.quantityAccepted
+ }
+ return receivedQuantity
+}
+
+/**
+ * Update issuance, shipment and order items if quantity received is higher than quantity on purchase order
+ * @return
+ */
+def updateIssuanceShipmentAndPoOnReceiveInventory() {
+ GenericValue orderItem = from("OrderItem").where(parameters).queryOne()
+ if (parameters.orderCurrencyUnitPrice) {
+ if (parameters.orderCurrencyUnitPrice != orderItem.unitPrice) {
+ orderItem.unitPrice = new BigDecimal (parameters.orderCurrencyUnitPrice)
+ orderItem.store()
+ }
+ } else {
+ if (parameters.unitCost != orderItem.unitPrice) {
+ orderItem.unitPrice = parameters.unitCost
+ orderItem.store()
+ }
+ }
+ BigDecimal receivedQuantity = getReceivedQuantityForOrderItem(orderItem)
+ if (orderItem.quantity < receivedQuantity) {
+ GenericValue orderItemShipGroupAssoc = from("OrderItemShipGroupAssoc")
+ .where(orderId: orderItem.orderId, orderItemSeqId: orderItem.orderItemSeqId)
+ .queryFirst()
+ BigDecimal quantityVariance = (receivedQuantity - orderItem.quantity).setScale(2, RoundingMode.HALF_UP)
+ BigDecimal oisgaQuantity = (orderItemShipGroupAssoc.quantity + quantityVariance).setScale(2, RoundingMode.HALF_UP)
+ orderItemShipGroupAssoc.quantity = oisgaQuantity
+ orderItem.quantity = receivedQuantity
+ orderItemShipGroupAssoc.store()
+ orderItem.store()
+ }
+ if (parameters.shipmentId) {
+ if (orderItem.productId) {
+ Map inputMap = parameters
+ inputMap.orderItem = orderItem
+ Map serviceResult = run service:"getTotalIssuedQuantityForOrderItem", with: inputMap
+ BigDecimal totalIssuedQuantity = serviceResult.totalIssuedQuantity
+ if (totalIssuedQuantity < receivedQuantity) {
+ BigDecimal quantityToAdd = receivedQuantity - totalIssuedQuantity
+ EntityCondition condition = EntityCondition.makeCondition([
+ EntityCondition.makeCondition(productId: orderItem.productId),
+ EntityCondition.makeCondition(shipmentId: parameters.shipmentId)
+ ])
+ if (parameters.shipmentItemSeqId) {
+ condition = EntityCondition.makeCondition([
+ EntityCondition.makeCondition(shipmentItemSeqId: parameters.shipmentItemSeqId),
+ condition
+ ])
+ }
+ GenericValue shipmentItem = from("ShipmentItem").where(condition).orderBy("shipmentItemSeqId").queryFirst()
+ if (shipmentItem) {
+ shipmentItem.quantity += quantityToAdd
+ shipmentItem.store()
+ GenericValue orderShipment = from("OrderShipment")
+ .where(orderId: parameters.orderId, orderItemSeqId: parameters.orderItemSeqId,
+ shipmentId: parameters.shipmentId, shipmentItemSeqId: shipmentItem.shipmentItemSeqId)
+ .queryFirst()
+ if (orderShipment) {
+ orderShipment.quantity += quantityToAdd
+ orderShipment.store()
+ }
+ }
+
+ // TODO: if we want to record the role of the facility operation we have to re-implement this using ShipmentReceiptRole
+ // <set field="itemIssuanceId" from-field="itemIssuance.itemIssuanceId"/>
+ // <call-simple-method method-name="associateIssueRoles" xml-resource="component://product/minilang/shipment/issuance/IssuanceServices.xml"/>
+ }
+ }
+ }
+ return success()
+}
+
+
+/**
+ * Cancel Received Items against a purchase order if received something incorrectly
+ * @return
+ */
+def cancelReceivedItems() {
+ // TODO: When items are received against a Purchase Order, service listed below changes certain things in the system. Changes done by these
+ // services also need to be reverted and missing logic can be added later.
+ // 1. addProductsBackToCategory
+ // 2. setUnitPriceAsLastPrice
+ // 3. createAcctgTransForShipmentReceipt
+ // 4. updateProductIfAvailableFromShipment
+
+ // update the accepted and received quantity to zero in ShipmentReceipt entity
+ GenericValue shipmentReceipt = from("ShipmentReceipt").where(parameters).queryOne()
+ shipmentReceipt.quantityAccepted = 0.0
+ shipmentReceipt.quantityRejected = 0.0
+ shipmentReceipt.store()
+
+ // create record for InventoryItemDetail entity
+ GenericValue inventoryItem = delegator.getRelatedOne("InventoryItem", shipmentReceipt, false)
+ Map inventoryItemDetailMap = [inventoryItemId: inventoryItem.inventoryItemId]
+ inventoryItemDetailMap.quantityOnHandDiff = inventoryItem.quantityOnHandTotal * (-1)
+ inventoryItemDetailMap.availableToPromiseDiff = inventoryItem.availableToPromiseTotal *(-1)
+ run service:"createInventoryItemDetail", with: inventoryItemDetailMap
+
+ // Balance the inventory item
+ Map balanceInventoryItemMap = [inventoryItemId: inventoryItem.inventoryItemId,
+ priorityOrderId: shipmentReceipt.orderId, priorityOrderItemSeqId: shipmentReceipt.orderItemSeqId]
+ run service:"balanceInventoryItems", with: balanceInventoryItemMap
+
+ // update the shipment status, if shipment was received
+ GenericValue shipment = delegator.getRelatedOne("Shipment", shipmentReceipt, false)
+ if (shipment?.statusId == "PURCH_SHIP_RECEIVED") {
+ Map shipmentStatusMap = [shipmentId : shipment.shipmentId, statusId: "PURCH_SHIP_SHIPPED"]
+ run service:"updateShipment", with: shipmentStatusMap
+ }
+ // change order item and order status
+ GenericValue orderItem = delegator.getRelatedOne("OrderItem", shipmentReceipt, false)
+ if (orderItem?.statusId == "ITEM_COMPLETED") {
+ // update the order item status
+ orderItem.statusId = "ITEM_APPROVED"
+ Map orderItemCtx = [:]
+ orderItemCtx << orderItem
+ orderItemCtx.fromStatusId = "ITEM_COMPLETED"
+ run service:"changeOrderItemStatus", with: orderItemCtx
+ GenericValue orderHeader = delegator.getRelatedOne("OrderHeader", orderItem, false)
+ // cancel the invoice
+ GenericValue orderItemBilling = from("OrderItemBilling").where(orderId: orderItem.orderId).queryFirst()
+ if (orderItemBilling) {
+ Map invoiceStatusMap = [invoiceId: orderItemBilling.invoiceId, statusId: "INVOICE_CANCELLED"]
+ run service:"setInvoiceStatus", with: invoiceStatusMap
+ }
+ }
+ return success()
+}
diff --git a/applications/product/minilang/shipment/receipt/ShipmentReceiptServices.xml b/applications/product/minilang/shipment/receipt/ShipmentReceiptServices.xml
deleted file mode 100644
index 4334dc5..0000000
--- a/applications/product/minilang/shipment/receipt/ShipmentReceiptServices.xml
+++ /dev/null
@@ -1,577 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
-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.
--->
-
-<simple-methods xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns="http://ofbiz.apache.org/Simple-Method" xsi:schemaLocation="http://ofbiz.apache.org/Simple-Method http://ofbiz.apache.org/dtds/simple-methods.xsd">
- <simple-method method-name="createShipmentReceipt" short-description="Create a ShipmentReceipt">
- <make-value entity-name="ShipmentReceipt" value-field="newEntity"/>
- <set-nonpk-fields map="parameters" value-field="newEntity"/>
-
- <sequenced-id sequence-name="ShipmentReceipt" field="receiptId"/>
- <to-string field="receiptId"/>
- <set field="newEntity.receiptId" from-field="receiptId"/>
- <field-to-result field="receiptId" result-name="receiptId"/>
-
- <if-empty field="newEntity.datetimeReceived">
- <now-timestamp field="nowTimestamp"/>
- <set field="newEntity.datetimeReceived" from-field="nowTimestamp"/>
- </if-empty>
-
- <set field="newEntity.receivedByUserLoginId" from-field="userLogin.userLoginId"/>
- <create-value value-field="newEntity"/>
-
- <if-not-empty field="parameters.inventoryItemDetailSeqId">
- <entity-one entity-name="InventoryItemDetail" value-field="invDet">
- <field-map field-name="inventoryItemDetailSeqId" from-field="parameters.inventoryItemDetailSeqId"/>
- <field-map field-name="inventoryItemId" from-field="parameters.inventoryItemId"/>
- </entity-one>
- <set field="invDet.receiptId" from-field="receiptId"/>
- <store-value value-field="invDet"/>
- </if-not-empty>
- <set field="affectAccounting" type="Boolean" value="true"/>
-
- <entity-one entity-name="Product" value-field="product"/>
- <if>
- <condition>
- <or>
- <if-compare field="product.productTypeId" operator="equals" value="SERVICE_PRODUCT"/>
- <if-compare field="product.productTypeId" operator="equals" value="ASSET_USAGE_OUT_IN"/>
- <if-compare field="product.productTypeId" operator="equals" value="AGGREGATEDSERV_CONF"/>
- </or>
- </condition>
- <then>
- <set field="affectAccounting" type="Boolean" value="false"/>
- </then>
- </if>
- <field-to-result field="affectAccounting" result-name="affectAccounting"/>
- </simple-method>
-
- <simple-method method-name="receiveInventoryProduct" short-description="Receive Inventory in new Inventory Item(s)">
- <!-- NOTES
- - for serialized items with a serial number passed in: the quantityAccepted _should_ always be 1
- - if the type is SERIALIZED_INV_ITEM but there is not serial number (which is weird...) we'll create a bunch of individual InventoryItems
- - DEJ20070822: something to consider for the future: maybe instead of this funny looping maybe for serialized items we should only allow a quantity of 1, ie return an error if it is not 1
- -->
- <set field="loops" value="1" type="Double"/>
- <if-compare value="SERIALIZED_INV_ITEM" operator="equals" field="parameters.inventoryItemTypeId">
- <!-- if we are serialized and either a serialNumber or inventoyItemId is passed in and the quantityAccepted is greater than 1 then complain -->
- <if>
- <condition>
- <and>
- <or>
- <not><if-empty field="parameters.serialNumber"/></not>
- <not><if-empty field="parameters.currentInventoryItemId"/></not>
- </or>
- <if-compare field="parameters.quantityAccepted" operator="greater" value="1" type="BigDecimal"/>
- </and>
- </condition>
- <then>
- <add-error>
- <fail-property resource="ProductUiLabels" property="FacilityReceiveInventoryProduct"/>
- </add-error>
- </then>
- </if>
-
- <set field="loops" from-field="parameters.quantityAccepted"/>
- <set field="parameters.quantityAccepted" value="1" type="BigDecimal"/>
- </if-compare>
-
- <set field="parameters.quantityOnHandDiff" from-field="parameters.quantityAccepted"/>
- <set field="parameters.availableToPromiseDiff" from-field="parameters.quantityAccepted"/>
-
- <!-- before getting going, see if there are any validation issues so far -->
- <check-errors/>
-
- <!-- Status for Non serialized and Serialized inventory are different, lets make sure correct status is stored in database -->
- <if-compare field="parameters.inventoryItemTypeId" operator="equals" value="NON_SERIAL_INV_ITEM">
- <if-compare field="parameters.statusId" operator="equals" value="INV_DEFECTIVE"><!-- This status may come from the Receive Return Screen -->
- <set field="parameters.statusId" value="INV_NS_DEFECTIVE"/>
- <else>
- <if-compare field="parameters.statusId" operator="equals" value="INV_ON_HOLD">
- <set field="parameters.statusId" value="INV_NS_ON_HOLD"/>
- <else>
- <if-compare field="parameters.statusId" operator="equals" value="INV_RETURNED">
- <set field="parameters.statusId" value="INV_NS_RETURNED"/>
- </if-compare>
- </else>
- </if-compare>
- </else>
- </if-compare>
- <!-- Any other status should be just set to null, if it is not a valid status for Non Serialized inventory -->
- <if>
- <condition>
- <and>
- <not><if-compare field="parameters.statusId" operator="equals" value="INV_NS_DEFECTIVE"/></not>
- <not><if-compare field="parameters.statusId" operator="equals" value="INV_NS_ON_HOLD"/></not>
- <not><if-compare field="parameters.statusId" operator="equals" value="INV_NS_RETURNED"/></not>
- </and>
- </condition>
- <then>
- <set field="parameters.statusId" from-field="nullField"/>
- </then>
- </if>
- </if-compare>
-
- <loop count="${loops}" field="currentLoop">
- <log level="info" message="receiveInventoryProduct Looping and creating inventory info - ${currentLoop}"/>
-
- <!-- if there is an inventoryItemId, update it (this will happen when receiving serialized inventory already in the system, like for returns); if not create one -->
- <clear-field field="serviceInMap"/>
- <clear-field field="currentInventoryItemId"/>
-
- <!-- Set supplier partyId, if inventory received by purchase order -->
- <if-not-empty field="parameters.orderId">
- <entity-and entity-name="OrderRole" list="orderRoles">
- <field-map field-name="orderId" from-field="parameters.orderId"/>
- <field-map field-name="roleTypeId" value="SUPPLIER_AGENT"/>
- </entity-and>
- <if-not-empty field="orderRoles">
- <first-from-list list="orderRoles" entry="orderRole"/>
- <set field="parameters.partyId" from-field="orderRole.partyId"/>
- </if-not-empty>
- </if-not-empty>
-
- <if-empty field="parameters.currentInventoryItemId">
- <set-service-fields service-name="createInventoryItem" map="parameters" to-map="serviceInMap"/>
- <call-service service-name="createInventoryItem" in-map-name="serviceInMap">
- <result-to-field result-name="inventoryItemId" field="currentInventoryItemId"/>
- </call-service>
-
- <else>
- <if-not-empty field="parameters.currentInventoryItemId">
- <set field="parameters.inventoryItemId" from-field="parameters.currentInventoryItemId"/>
- </if-not-empty>
- <set-service-fields service-name="updateInventoryItem" map="parameters" to-map="serviceInMap"/>
- <call-service service-name="updateInventoryItem" in-map-name="serviceInMap"/>
- <set field="currentInventoryItemId" from-field="parameters.currentInventoryItemId"/>
- </else>
- </if-empty>
-
- <!-- do this only for non-serialized inventory -->
- <if-compare value="SERIALIZED_INV_ITEM" operator="not-equals" field="parameters.inventoryItemTypeId">
- <clear-field field="serviceInMap"/>
- <set-service-fields service-name="createInventoryItemDetail" map="parameters" to-map="serviceInMap"/>
- <set field="serviceInMap.inventoryItemId" from-field="currentInventoryItemId"/>
- <call-service service-name="createInventoryItemDetail" in-map-name="serviceInMap">
- <result-to-field result-name="inventoryItemDetailSeqId" field="parameters.inventoryItemDetailSeqId"/>
- </call-service>
- </if-compare>
-
- <clear-field field="serviceInMap"/>
- <set-service-fields service-name="createShipmentReceipt" map="parameters" to-map="serviceInMap"/>
- <set field="serviceInMap.inventoryItemId" from-field="currentInventoryItemId"/>
- <call-service service-name="createShipmentReceipt" in-map-name="serviceInMap"/>
-
- <!-- update serialized items to AVAILABLE (only if this is not a return), which then triggers other SECA chains -->
- <if>
- <condition>
- <and>
- <if-compare value="SERIALIZED_INV_ITEM" operator="equals" field="parameters.inventoryItemTypeId"/>
- <if-empty field="parameters.returnId"/>
- </and>
- </condition>
- <then>
- <!-- Retrieve the new inventoryItem -->
- <entity-one entity-name="InventoryItem" value-field="inventoryItem">
- <field-map field-name="inventoryItemId" from-field="currentInventoryItemId"/>
- </entity-one>
-
- <!-- Don't reset the status if it's already set to INV_PROMISED or INV_ON_HOLD -->
- <if>
- <condition>
- <and>
- <if-compare value="INV_PROMISED" operator="not-equals" field="inventoryItem.statusId"/>
- <if-compare value="INV_ON_HOLD" operator="not-equals" field="inventoryItem.statusId"/>
- </and>
- </condition>
- <then>
- <clear-field field="serviceInMap"/>
- <set field="serviceInMap.inventoryItemId" from-field="currentInventoryItemId"/>
- <set field="serviceInMap.statusId" value="INV_AVAILABLE"/> <!-- XXX set to returned instead -->
- <call-service service-name="updateInventoryItem" in-map-name="serviceInMap"/>
- </then>
- </if>
- </then>
- </if>
-
- <clear-field field="serviceInMap"/>
- <set-service-fields service-name="balanceInventoryItems" map="parameters" to-map="serviceInMap"/>
- <set field="serviceInMap.inventoryItemId" from-field="currentInventoryItemId"/>
- <call-service service-name="balanceInventoryItems" in-map-name="serviceInMap"/>
-
- <set field="successMessageList[]" value="Received ${parameters.quantityAccepted} of ${parameters.productId} in inventory item ${currentInventoryItemId}"/>
- </loop>
- <!-- return the last inventory item received -->
- <field-to-result field="currentInventoryItemId" result-name="inventoryItemId"/>
- </simple-method>
-
- <simple-method method-name="quickReceiveReturn" short-description="Quick Receive Entire Return">
- <entity-one entity-name="ReturnHeader" value-field="returnHeader">
- <field-map field-name="returnId" from-field="parameters.returnId"/>
- </entity-one>
-
- <if-compare field="returnHeader.needsInventoryReceive" operator="equals" value="Y">
- <!-- before receiving inventory, check to see if there is inventory information in this database -->
- <entity-count entity-name="InventoryItem" count-field="iiCount">
- <condition-expr field-name="facilityId" operator="equals" from-field="returnHeader.destinationFacilityId"/>
- </entity-count>
-
- <if-compare field="iiCount" operator="greater" value="0" type="Integer">
- <!-- create a return shipment for this return -->
- <set field="shipmentCtx.returnId" from-field="parameters.returnId"/>
- <call-service service-name="createShipmentForReturn" in-map-name="shipmentCtx">
- <result-to-field result-name="shipmentId"/>
- </call-service>
- <log level="info" message="Created new shipment ${shipmentId}"/>
-
- <entity-condition entity-name="ReturnItem" list="returnItems">
- <condition-expr field-name="returnId" operator="equals" from-field="returnHeader.returnId"/>
- </entity-condition>
-
- <!-- if no inventory item type specified, get default from facility -->
- <if-empty field="parameters.inventoryItemTypeId">
- <get-related-one value-field="returnHeader" relation-name="Facility" to-value-field="facility"/>
- <set field="parameters.inventoryItemTypeId" from-field="facility.defaultInventoryItemTypeId" default-value="NON_SERIAL_INV_ITEM"/>
- </if-empty>
-
- <now-timestamp field="nowTimestamp"/>
-
- <entity-count entity-name="ReturnItem" count-field="returnItemCount">
- <condition-expr field-name="returnId" operator="equals" from-field="returnHeader.returnId"/>
- </entity-count>
- <set field="nonProductItems" type="Long" value="0"/>
-
- <iterate list="returnItems" entry="returnItem">
- <!-- record this return item on the return shipment as well. not sure if this is actually necessary... -->
- <clear-field field="shipItemCtx"/>
- <set from-field="shipmentId" field="shipItemCtx.shipmentId"/>
- <set from-field="returnItem.productId" field="shipItemCtx.productId"/>
- <set from-field="returnItem.returnQuantity" field="shipItemCtx.quantity"/>
- <log level="info" message="calling create shipment item with ${shipItemCtx}"/>
- <call-service service-name="createShipmentItem" in-map-name="shipItemCtx">
- <result-to-field result-name="shipmentItemSeqId"/>
- </call-service>
- </iterate>
- <iterate list="returnItems" entry="returnItem">
- <clear-field field="receiveCtx"/>
-
- <if-empty field="returnItem.expectedItemStatus">
- <set value="INV_RETURNED" field="returnItem.expectedItemStatus" type="String"/>
- </if-empty>
- <get-related-one value-field="returnItem" relation-name="OrderItem" to-value-field="orderItem"/>
- <if-not-empty field="orderItem.productId">
- <set field="costCtx.returnItemSeqId" from-field="returnItem.returnItemSeqId"/>
- <set field="costCtx.returnId" from-field="returnItem.returnId"/>
- <call-service service-name="getReturnItemInitialCost" in-map-name="costCtx">
- <result-to-field result-name="initialItemCost" field="receiveCtx.unitCost"/>
- </call-service>
- <!--check if the items already have SERIALIZED inventory. If so, it still puts them back as SERIALIZED with status "Accepted."-->
- <entity-count entity-name="InventoryItem" count-field="serializedItemCount">
- <condition-list combine="and">
- <condition-expr field-name="productId" operator="equals" from-field="returnItem.productId"/>
- <condition-expr field-name="facilityId" operator="equals" from-field="returnHeader.destinationFacilityId"/>
- <condition-expr field-name="inventoryItemTypeId" operator="equals" value="SERIALIZED_INV_ITEM"/>
- </condition-list>
- </entity-count>
- <set field="setNonSerial" value="false"/>
- <if-compare field="parameters.inventoryItemTypeId" value="NON_SERIAL_INV_ITEM" operator="equals">
- <if-compare field="serializedItemCount" value="0" operator="equals">
- <set field="parameters.inventoryItemTypeId" value="NON_SERIAL_INV_ITEM"/>
- <set field="setNonSerial" value="true"/>
- </if-compare>
- </if-compare>
- <if-compare field="setNonSerial" value="false" operator="equals">
- <set field="parameters.inventoryItemTypeId" value="SERIALIZED_INV_ITEM"/>
- <set field="returnItem.returnQuantity" value="1" type="BigDecimal"/>
- </if-compare>
-
- <set from-field="parameters.inventoryItemTypeId" field="receiveCtx.inventoryItemTypeId"/>
- <set from-field="returnItem.expectedItemStatus" field="receiveCtx.statusId"/>
- <set from-field="returnItem.productId" field="receiveCtx.productId"/>
- <set from-field="returnItem.returnItemSeqId" field="receiveCtx.returnItemSeqId"/>
- <set from-field="returnItem.returnId" field="receiveCtx.returnId"/>
- <set from-field="returnItem.returnQuantity" field="receiveCtx.quantityAccepted"/>
- <set from-field="returnHeader.destinationFacilityId" field="receiveCtx.facilityId"/>
- <!-- important: associate ShipmentReceipt with return shipment created -->
- <set field="receiveCtx.shipmentId" from-field="shipmentId"/>
-
- <set field="receiveCtx.comments" value="Returned Item RA# ${returnItem.returnId}"/>
- <set field="receiveCtx.datetimeReceived" from-field="nowTimestamp"/>
- <set field="receiveCtx.quantityRejected" value="0" type="BigDecimal"/>
-
- <call-service service-name="receiveInventoryProduct" in-map-name="receiveCtx"/>
- <else>
- <calculate field="nonProductItems" type="Long">
- <calcop operator="add">
- <number value="1"/>
- </calcop>
- </calculate>
- </else>
- </if-not-empty>
- </iterate>
-
- <!-- now that the receive is done; set the need flag to N -->
- <refresh-value value-field="returnHeader"/>
- <set field="returnHeader.needsInventoryReceive" value="N"/>
- <store-value value-field="returnHeader"/>
-
- <!-- always check/update the ReturnHeader status, even though it might have been from the receiving above, just make sure -->
- <if-compare field="returnHeader.statusId" operator="not-equals" value="RETURN_RECEIVED">
- <set field="retStCtx.returnId" from-field="returnHeader.returnId"/>
- <set field="retStCtx.statusId" value="RETURN_RECEIVED"/>
- <call-service service-name="updateReturnHeader" in-map-name="retStCtx"/>
- </if-compare>
- <else>
- <log level="info" message="Not receiving inventory for returnId ${returnHeader.returnId}, no inventory information available."/>
- </else>
- </if-compare>
- </if-compare>
- </simple-method>
-
- <simple-method method-name="issueOrderItemToShipmentAndReceiveAgainstPO" short-description="Issues order item quantity specified to the shipment, then receives inventory for that item and quantity">
-
- <!-- get orderItem -->
- <entity-one entity-name="OrderItem" value-field="orderItem" auto-field-map="true"/>
- <!-- get orderItemShipGroupAssoc -->
- <entity-one entity-name="OrderItemShipGroupAssoc" value-field="orderItemShipGroupAssoc" auto-field-map="true"/>
- <!-- get shipment -->
- <entity-one entity-name="Shipment" value-field="shipment" auto-field-map="true"/>
-
- <!-- try to find an existing shipmentItem and attach to it, if none found create a new shipmentItem -->
- <!-- if there is NO productId on the orderItem, ALWAYS create a new shipmentItem -->
- <if-not-empty field="orderItem.productId">
- <entity-condition entity-name="ShipmentItem" list="shipmentItems">
- <condition-list combine="and">
- <condition-expr field-name="productId" from-field="orderItem.productId"/>
- <condition-expr field-name="shipmentId" from-field="shipment.shipmentId"/>
- <condition-expr field-name="shipmentItemSeqId" from-field="parameters.shipmentItemSeqId" ignore-if-empty="true"/>
- </condition-list>
- <order-by field-name="shipmentItemSeqId"/>
- </entity-condition>
- <first-from-list list="shipmentItems" entry="shipmentItem"/>
- </if-not-empty>
-
- <if-empty field="shipmentItem">
- <set from-field="orderItem.productId" field="shipmentItemCreate.productId"/>
- <set from-field="parameters.shipmentId" field="shipmentItemCreate.shipmentId"/>
- <set from-field="parameters.quantity" field="shipmentItemCreate.quantity"/>
- <call-service service-name="createShipmentItem" in-map-name="shipmentItemCreate">
- <result-to-field result-name="shipmentItemSeqId" field="shipmentItemLookupPk.shipmentItemSeqId"/>
- </call-service>
- <set from-field="parameters.shipmentId" field="shipmentItemLookupPk.shipmentId"/>
- <find-by-primary-key entity-name="ShipmentItem" map="shipmentItemLookupPk" value-field="shipmentItem"/>
-
- <!-- Create OrderShipment for this ShipmentItem -->
- <set from-field="parameters.quantity" field="orderShipmentCreate.quantity"/>
- <set from-field="shipmentItem.shipmentId" field="orderShipmentCreate.shipmentId"/>
- <set from-field="shipmentItem.shipmentItemSeqId" field="orderShipmentCreate.shipmentItemSeqId"/>
- <set from-field="orderItem.orderId" field="orderShipmentCreate.orderId"/>
- <set from-field="orderItem.orderItemSeqId" field="orderShipmentCreate.orderItemSeqId"/>
-
- <if-not-empty field="orderItemShipGroupAssoc">
- <!-- If we have a ShipGroup Assoc for this Item to focus on, set that; this is mostly the case for purchase orders and such -->
- <set from-field="orderItemShipGroupAssoc.shipGroupSeqId" field="orderShipmentCreate.shipGroupSeqId"/>
- </if-not-empty>
- <call-service service-name="createOrderShipment" in-map-name="orderShipmentCreate"/>
- <else>
- <call-simple-method method-name="getTotalIssuedQuantityForOrderItem" xml-resource="component://product/minilang/shipment/issuance/IssuanceServices.xml"/>
- <call-simple-method method-name="getReceivedQuantityForOrderItem"/>
- <set field="receivedQuantity" value="${receivedQuantity$bigDecimal + parameters.quantity$bigDecimal}" type="BigDecimal"/>
- <entity-and entity-name="OrderShipment" list="orderShipments">
- <field-map field-name="orderId" from-field="orderItem.orderId"/>
- <field-map field-name="orderItemSeqId" from-field="orderItem.orderItemSeqId"/>
- <field-map field-name="shipmentId" from-field="shipmentItem.shipmentId"/>
- <field-map field-name="shipmentItemSeqId" from-field="shipmentItem.shipmentItemSeqId"/>
- <field-map field-name="shipGroupSeqId" from-field="orderItemShipGroupAssoc.shipGroupSeqId"/>
- </entity-and>
- <first-from-list list="orderShipments" entry="orderShipment"/>
- <if-compare-field field="totalIssuedQuantity" operator="less" to-field="receivedQuantity" type="BigDecimal">
- <set field="quantityToAdd" value="${receivedQuantity$bigDecimal - totalIssuedQuantity$bigDecimal}" type="BigDecimal"/>
- <set field="shipmentItem.quantity" value="${shipmentItem.quantity$bigDecimal + quantityToAdd$bigDecimal}" type="BigDecimal"/>
- <store-value value-field="shipmentItem"/>
- <set field="shipmentItemSeqId" from-field="shipmentItem.shipmentItemSeqId"/>
-
- <set field="orderShipment.quantity" value="${orderShipment.quantity$bigDecimal + quantityToAdd$bigDecimal}" type="BigDecimal"/>
- <store-value value-field="orderShipment"/>
- </if-compare-field>
- </else>
- </if-empty>
- <!--
- TODO: if we want to record the role of the facility operation we have to re-implement this using ShipmentReceiptRole
- <call-simple-method method-name="associateIssueRoles" xml-resource="component://product/minilang/shipment/issuance/IssuanceServices.xml"/>
- -->
-
- <set-service-fields service-name="receiveInventoryProduct" map="parameters" to-map="receiveInventoryProductCtx"/>
- <set field="receiveInventoryProductCtx.shipmentItemSeqId" from-field="shipmentItemSeqId"/>
- <call-service service-name="receiveInventoryProduct" in-map-name="receiveInventoryProductCtx">
- <result-to-result result-name="inventoryItemId"/>
- </call-service>
- </simple-method>
-
- <simple-method method-name="getReceivedQuantityForOrderItem" short-description="Computes the till now received quantity from all ShipmentReceipts">
- <set field="receivedQuantity" type="BigDecimal" value="0"/>
- <entity-and entity-name="ShipmentReceipt" list="shipmentReceipts">
- <field-map field-name="orderId" from-field="orderItem.orderId"/>
- <field-map field-name="orderItemSeqId" from-field="orderItem.orderItemSeqId"/>
- </entity-and>
- <iterate list="shipmentReceipts" entry="shipmentReceipt">
- <set field="receivedQuantity" value="${receivedQuantity$bigDecimal + shipmentReceipt.quantityAccepted$bigDecimal}" type="BigDecimal"/>
- </iterate>
- </simple-method>
-
- <simple-method method-name="updateIssuanceShipmentAndPoOnReceiveInventory" short-description="Update issuance, shipment and order items if quantity received is higher than quantity on purchase order">
- <entity-one entity-name="OrderItem" value-field="orderItem"/>
- <if-not-empty field="parameters.orderCurrencyUnitPrice">
- <if-compare-field field="parameters.orderCurrencyUnitPrice" operator="not-equals" to-field="orderItem.unitPrice" type="BigDecimal">
- <set field="orderItem.unitPrice" from-field="parameters.orderCurrencyUnitPrice" type="BigDecimal"/>
- <store-value value-field="orderItem"/>
- </if-compare-field>
- <else>
- <if-compare-field field="parameters.unitCost" operator="not-equals" to-field="orderItem.unitPrice" type="BigDecimal">
- <set field="orderItem.unitPrice" from-field="parameters.unitCost" type="BigDecimal"/>
- <store-value value-field="orderItem"/>
- </if-compare-field>
- </else>
- </if-not-empty>
- <call-simple-method method-name="getReceivedQuantityForOrderItem"/>
- <if-compare-field field="orderItem.quantity" operator="less" to-field="receivedQuantity" type="BigDecimal">
- <entity-and entity-name="OrderItemShipGroupAssoc" list="orderItemShipGroupAssocs">
- <field-map field-name="orderId" from-field="orderItem.orderId"/>
- <field-map field-name="orderItemSeqId" from-field="orderItem.orderItemSeqId"/>
- </entity-and>
- <calculate field="quantityVariance" type="BigDecimal" decimal-scale="2" rounding-mode="HalfUp">
- <calcop operator="subtract">
- <calcop operator="get" field="receivedQuantity"/>
- <calcop operator="get" field="orderItem.quantity"/>
- </calcop>
- </calculate>
- <first-from-list list="orderItemShipGroupAssocs" entry="orderItemShipGroupAssoc"/>
- <calculate field="oisgaQuantity" type="BigDecimal" decimal-scale="2" rounding-mode="HalfUp">
- <calcop operator="add">
- <calcop operator="get" field="orderItemShipGroupAssoc.quantity"/>
- <calcop operator="get" field="quantityVariance"/>
- </calcop>
- </calculate>
- <set field="orderItemShipGroupAssoc.quantity" from-field="oisgaQuantity"/>
- <set field="orderItem.quantity" from-field="receivedQuantity"/>
- <store-value value-field="orderItemShipGroupAssoc"/>
- <store-value value-field="orderItem"/>
- </if-compare-field>
- <if-not-empty field="parameters.shipmentId">
- <if-not-empty field="orderItem.productId">
- <call-simple-method method-name="getTotalIssuedQuantityForOrderItem" xml-resource="component://product/minilang/shipment/issuance/IssuanceServices.xml"/>
- <if-compare-field field="totalIssuedQuantity" operator="less" to-field="receivedQuantity" type="BigDecimal">
- <set field="quantityToAdd" value="${receivedQuantity$bigDecimal - totalIssuedQuantity$bigDecimal}" type="BigDecimal"/>
- <entity-condition entity-name="ShipmentItem" list="shipmentItems">
- <condition-list combine="and">
- <condition-expr field-name="productId" from-field="orderItem.productId"/>
- <condition-expr field-name="shipmentId" from-field="parameters.shipmentId"/>
- <condition-expr field-name="shipmentItemSeqId" from-field="parameters.shipmentItemSeqId" ignore-if-empty="true"/>
- </condition-list>
- <order-by field-name="shipmentItemSeqId"/>
- </entity-condition>
- <first-from-list list="shipmentItems" entry="shipmentItem"/>
- <set field="shipmentItem.quantity" value="${shipmentItem.quantity$bigDecimal + quantityToAdd$bigDecimal}" type="BigDecimal"/>
- <store-value value-field="shipmentItem"/>
-
- <entity-and entity-name="OrderShipment" list="orderShipments">
- <field-map field-name="orderId" from-field="parameters.orderId"/>
- <field-map field-name="orderItemSeqId" from-field="parameters.orderItemSeqId"/>
- <field-map field-name="shipmentId" from-field="parameters.shipmentId"/>
- <field-map field-name="shipmentItemSeqId" from-field="shipmentItem.shipmentItemSeqId"/>
- </entity-and>
- <first-from-list list="orderShipments" entry="orderShipment"/>
- <set field="orderShipment.quantity" value="${orderShipment.quantity$bigDecimal + quantityToAdd$bigDecimal}" type="BigDecimal"/>
- <store-value value-field="orderShipment"/>
- <!--
- TODO: if we want to record the role of the facility operation we have to re-implement this using ShipmentReceiptRole
- <set field="itemIssuanceId" from-field="itemIssuance.itemIssuanceId"/>
- <call-simple-method method-name="associateIssueRoles" xml-resource="component://product/minilang/shipment/issuance/IssuanceServices.xml"/>
- -->
- </if-compare-field>
- </if-not-empty>
- </if-not-empty>
- </simple-method>
-
- <simple-method method-name="cancelReceivedItems" short-description="Cancel Received Items against a purchase order if received something incorrectly">
- <!-- TODO: When items are received against a Purchase Order, service listed below changes certain things in the system. Changes done by these
- services also need to be reverted and missing logic can be added later.
- 1. addProductsBackToCategory
- 2. setUnitPriceAsLastPrice
- 3. createAcctgTransForShipmentReceipt
- 4. updateProductIfAvailableFromShipment
- -->
- <!-- update the accepted and received quantity to zero in ShipmentReceipt entity -->
- <entity-one entity-name="ShipmentReceipt" value-field="shipmentReceipt"/>
- <set field="shipmentReceipt.quantityAccepted" value="0" type="BigDecimal"/>
- <set field="shipmentReceipt.quantityRejected" value="0" type="BigDecimal"/>
- <store-value value-field="shipmentReceipt"/>
-
- <!-- create record for InventoryItemDetail entity -->
- <get-related-one value-field="shipmentReceipt" relation-name="InventoryItem" to-value-field="inventoryItem"/>
- <set field="inventoryItemDetailMap.inventoryItemId" from-field="inventoryItem.inventoryItemId"/>
- <calculate field="inventoryItemDetailMap.quantityOnHandDiff">
- <calcop operator="multiply" field="inventoryItem.quantityOnHandTotal">
- <number value="-1"/>
- </calcop>
- </calculate>
- <calculate field="inventoryItemDetailMap.availableToPromiseDiff">
- <calcop operator="multiply" field="inventoryItem.availableToPromiseTotal">
- <number value="-1"/>
- </calcop>
- </calculate>
- <call-service service-name="createInventoryItemDetail" in-map-name="inventoryItemDetailMap"/>
-
- <!-- Balance the inventory item -->
- <set field="balanceInventoryItemMap.inventoryItemId" from-field="inventoryItem.inventoryItemId"/>
- <set field="balanceInventoryItemMap.priorityOrderId" from-field="shipmentReceipt.orderId"/>
- <set field="balanceInventoryItemMap.priorityOrderItemSeqId" from-field="shipmentReceipt.orderItemSeqId"/>
- <call-service service-name="balanceInventoryItems" in-map-name="balanceInventoryItemMap"/>
-
- <!-- update the shipment status, if shipment was received -->
- <get-related-one value-field="shipmentReceipt" relation-name="Shipment" to-value-field="shipment"/>
- <if-compare field="shipment.statusId" operator="equals" value="PURCH_SHIP_RECEIVED">
- <set field="shipmentStatusMap.shipmentId" from-field="shipment.shipmentId"/>
- <set field="shipmentStatusMap.statusId" value="PURCH_SHIP_SHIPPED"/>
- <call-service service-name="updateShipment" in-map-name="shipmentStatusMap"/>
- </if-compare>
-
- <!-- change order item and order status -->
- <get-related-one value-field="shipmentReceipt" relation-name="OrderItem" to-value-field="orderItem"/>
- <if-compare field="orderItem.statusId" operator="equals" value="ITEM_COMPLETED">
- <!-- update the order item status -->
- <set field="orderItem.statusId" value="ITEM_APPROVED"/>
- <set-service-fields service-name="changeOrderItemStatus" map="orderItem" to-map="orderItemCtx"/>
- <set field="orderItemCtx.fromStatusId" value="ITEM_COMPLETED"/>
- <call-service service-name="changeOrderItemStatus" in-map-name="orderItemCtx"/>
- <get-related-one value-field="orderItem" relation-name="OrderHeader" to-value-field="orderHeader"/>
- <!-- cancel the invoice -->
- <entity-and entity-name="OrderItemBilling" list="orderItemBillings">
- <field-map field-name="orderId" from-field="orderItem.orderId"/>
- </entity-and>
- <if-not-empty field="orderItemBillings">
- <first-from-list list="orderItemBillings" entry="orderItemBilling"/>
- <set field="invoiceStatusMap.invoiceId" from-field="orderItemBilling.invoiceId"/>
- <set field="invoiceStatusMap.statusId" value="INVOICE_CANCELLED"/>
- <call-service service-name="setInvoiceStatus" in-map-name="invoiceStatusMap"/>
- </if-not-empty>
- </if-compare>
- </simple-method>
-</simple-methods>
diff --git a/applications/product/servicedef/services_shipment.xml b/applications/product/servicedef/services_shipment.xml
index 799b377..89bea13 100644
--- a/applications/product/servicedef/services_shipment.xml
+++ b/applications/product/servicedef/services_shipment.xml
@@ -606,6 +606,13 @@ under the License.
</attribute>
</service>
+ <service name="getTotalIssuedQuantityForOrderItem" engine="groovy"
+ location="component://product/groovyScripts/shipment/issuance/IssuanceServices.groovy" invoke="getTotalIssuedQuantityForOrderItem" auth="true">
+ <description>Computes the total quantity assigned to shipment for a purchase order item</description>
+ <attribute name="orderItem" type="GenericValue" mode="IN" optional="false"/>
+ <attribute name="totalIssuedQuantity" type="BigDecimal" mode="OUT" optional="false"/>
+ </service>
+
<!-- Pick Verify Services -->
<service name="verifySingleItem" engine="java"
location="org.apache.ofbiz.shipment.verify.VerifyPickServices" invoke="verifySingleItem" auth="true">
@@ -846,8 +853,8 @@ under the License.
<override name="quantityAccepted" optional="false"/>
<override name="quantityRejected" optional="false"/>
</service>
- <service name="createShipmentReceipt" engine="simple"
- location="component://product/minilang/shipment/receipt/ShipmentReceiptServices.xml" invoke="createShipmentReceipt" auth="true">
+ <service name="createShipmentReceipt" engine="groovy"
+ location="component://product/groovyScripts/shipment/receipt/ShipmentReceiptServices.groovy" invoke="createShipmentReceipt" auth="true">
<description>Creates a ShipmentReceipt Record</description>
<permission-service service-name="facilityGenericPermission" main-action="CREATE"/>
<implements service="interfaceShipmentReceipt"/>
@@ -870,8 +877,8 @@ under the License.
<attribute name="shipmentId" type="String" mode="IN" optional="false"/>
</service>
- <service name="receiveInventoryProduct" engine="simple" transaction-timeout="600"
- location="component://product/minilang/shipment/receipt/ShipmentReceiptServices.xml" invoke="receiveInventoryProduct" auth="true">
+ <service name="receiveInventoryProduct" engine="groovy" transaction-timeout="600"
+ location="component://product/groovyScripts/shipment/receipt/ShipmentReceiptServices.groovy" invoke="receiveInventoryProduct" auth="true">
<description>Receive Inventory In Warehouse</description>
<permission-service service-name="facilityGenericPermission" main-action="CREATE"/>
<auto-attributes entity-name="InventoryItem" include="nonpk" mode="IN" optional="true">
@@ -893,8 +900,8 @@ under the License.
<override name="facilityId" optional="false"/>
</service>
- <service name="issueOrderItemToShipmentAndReceiveAgainstPO" engine="simple" transaction-timeout="600"
- location="component://product/minilang/shipment/receipt/ShipmentReceiptServices.xml" invoke="issueOrderItemToShipmentAndReceiveAgainstPO" auth="true">
+ <service name="issueOrderItemToShipmentAndReceiveAgainstPO" engine="groovy" transaction-timeout="600"
+ location="component://product/groovyScripts/shipment/receipt/ShipmentReceiptServices.groovy" invoke="issueOrderItemToShipmentAndReceiveAgainstPO" auth="true">
<description>Issues order item quantity specified to the shipment, then receives inventory for that item and quantity</description>
<required-permissions join-type="AND">
<permission-service service-name="checkCanChangeShipmentStatusPacked" main-action="CREATE"/>
@@ -904,14 +911,21 @@ under the License.
<implements service="receiveInventoryProduct"/>
</service>
- <service name="quickReceiveReturn" engine="simple"
- location="component://product/minilang/shipment/receipt/ShipmentReceiptServices.xml" invoke="quickReceiveReturn" auth="true">
+ <service name="quickReceiveReturn" engine="groovy"
+ location="component://product/groovyScripts/shipment/receipt/ShipmentReceiptServices.groovy" invoke="quickReceiveReturn" auth="true">
<permission-service service-name="facilityGenericPermission" main-action="CREATE"/>
<attribute name="returnId" type="String" mode="IN" optional="false"/>
<attribute name="inventoryItemTypeId" type="String" mode="IN" optional="true"/>
<attribute name="statusId" type="String" mode="IN" optional="true"/>
</service>
+ <service name="interfaceShipmentReceiptRole" engine="interface" location="" invoke="">
+ <description>Interface for ShipmentReceiptRole</description>
+ <attribute name="receiptId" type="String" mode="IN" optional="false"/>
+ <attribute name="partyId" type="String" mode="IN" optional="false"/>
+ <attribute name="roleTypeId" type="String" mode="IN" optional="false"/>
+ </service>
+
<!-- Shipment Receipt Role Services -->
<service name="createShipmentReceiptRole" default-entity-name="ShipmentReceiptRole" engine="entity-auto" invoke="create" auth="true">
<description>Create a ShipmentReceipt Role entry</description>
@@ -997,8 +1011,8 @@ under the License.
<implements service="calcShipmentEstimateInterface"/>
</service>
- <service name="cancelReceivedItems" engine="simple"
- location="component://product/minilang/shipment/receipt/ShipmentReceiptServices.xml" invoke="cancelReceivedItems" auth="true">
+ <service name="cancelReceivedItems" engine="groovy"
+ location="component://product/groovyScripts/shipment/receipt/ShipmentReceiptServices.groovy" invoke="cancelReceivedItems" auth="true">
<description>Cancel Received Items against a purchase order if received something incorrectly</description>
<attribute name="receiptId" type="String" mode="IN" optional="false">
<type-validate>
@@ -1105,8 +1119,8 @@ under the License.
<attribute name="messageWrapper" type="org.apache.ofbiz.service.mail.MimeMessageWrapper" mode="OUT" optional="true"/>
<attribute name="communicationEventId" type="String" mode="OUT" optional="true"/>
</service>
- <service name="updateIssuanceShipmentAndPoOnReceiveInventory" engine="simple"
- location="component://product/minilang/shipment/receipt/ShipmentReceiptServices.xml" invoke="updateIssuanceShipmentAndPoOnReceiveInventory">
+ <service name="updateIssuanceShipmentAndPoOnReceiveInventory" engine="groovy"
+ location="component://product/groovyScripts/shipment/receipt/ShipmentReceiptServices.groovy" invoke="updateIssuanceShipmentAndPoOnReceiveInventory">
<description>Update issuance, shipment and order items if quantity received is higher than quantity on purchase order</description>
<attribute name="orderId" type="String" mode="IN" optional="false">
<type-validate>