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 <[hidden email]> 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 <[hidden email]> --- .../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> |
Free forum by Nabble | Edit this page |