blob: 39199524d5c0ccd9eeaa398c0eb18c7903b79af8 [file] [log] [blame]
<?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"
xsi:noNamespaceSchemaLocation="http://ofbiz.apache.org/dtds/simple-methods-v2.xsd">
<!-- =================================================================== -->
<!-- ======================= Stock Move Services ======================= -->
<!-- =================================================================== -->
<simple-method method-name="findStockMovesNeeded" short-description="Find all Stock Moves that need to be done">
<!-- TODO: make this method aware of serialized inventory in addition to non-serialized inventory -->
<check-permission permission="FACILITY" action="_VIEW">
<fail-property resource="ProductUiLabels" property="ProductFacilityViewPermissionError"/>
</check-permission>
<check-errors/>
<!-- First find all OrderItemShipGrpInvRes that are in a FLT_BULK location in the current Facility -->
<entity-and entity-name="OrderItemShipGrpInvResAndItemLocation" list="orderItemShipGrpInvResAndItemLocationList">
<field-map field-name="locationTypeEnumId" value="FLT_BULK"/>
<field-map field-name="orderItemStatusId" value="ITEM_APPROVED"/>
<field-map field-name="facilityId" from-field="parameters.facilityId"/>
</entity-and>
<!--
Consolidate into a List of infos return as moveByOisgirInfoList where each info Map contains:
- product
- facilityLocationFrom
- facilityLocationTo
- targetProductFacilityLocation
- quantityOnHandTotalFrom
- quantityOnHandTotalTo
- availableToPromiseTotalFrom
- availableToPromiseTotalTo
- totalQuantity
- orderItemShipGrpInvResInfoList (to be done later if we need it)
-->
<!-- start by making a Map where the locationSeqId is the key and the value is a List of orderItemShipGrpInvResAndItemLocation -->
<iterate entry="orderItemShipGrpInvResAndItemLocation" list="orderItemShipGrpInvResAndItemLocationList">
<field-to-list field="orderItemShipGrpInvResAndItemLocation" list="oiirailByLocMap[orderItemShipGrpInvResAndItemLocation.locationSeqId]"/>
</iterate>
<!-- now get all info for each origin location -->
<iterate-map key="locationSeqId" value="perLocationOiirailList" map="oiirailByLocMap">
<!-- now for more fun, split up by productId; this should generally not happen, but we'll make sure here -->
<clear-field field="oiirailByProdMap"/>
<iterate entry="orderItemShipGrpInvResAndItemLocation" list="perLocationOiirailList">
<field-to-list field="orderItemShipGrpInvResAndItemLocation" list="oiirailByProdMap[orderItemShipGrpInvResAndItemLocation.productId]"/>
</iterate>
<clear-field field="perProductOiirailList"/>
<clear-field field="productId"/>
<iterate-map key="productId" value="perProductOiirailList" map="oiirailByProdMap">
<!-- get product -->
<entity-one entity-name="Product" value-field="moveInfo.product"/>
<!-- get facilityLocationFrom -->
<entity-one entity-name="FacilityLocation" value-field="moveInfo.facilityLocationFrom">
<field-map field-name="facilityId" from-field="parameters.facilityId"/>
<field-map field-name="locationSeqId" from-field="locationSeqId"/>
</entity-one>
<!-- get facilityLocationTo: a little more tricky, find the first FLT_PICKLOC -->
<entity-and entity-name="ProductFacilityLocationView" list="productFacilityLocationViewList">
<field-map field-name="productId" from-field="productId"/>
<field-map field-name="facilityId" from-field="parameters.facilityId"/>
<field-map field-name="locationTypeEnumId" value="FLT_PICKLOC"/>
</entity-and>
<if-empty field="productFacilityLocationViewList">
<string-to-list string="Error in stock move, could not find a pick/primary location for facility [${parameters.facilityId}] and product [${productId}]" list="warningMessageList"/>
<else>
<!-- should generally only be one pick/primary location, just choose the first... -->
<first-from-list entry="productFacilityLocationView" list="productFacilityLocationViewList"/>
<get-related-one value-field="productFacilityLocationView" relation-name="FacilityLocation" to-value-field="moveInfo.facilityLocationTo"/>
<get-related-one value-field="productFacilityLocationView" relation-name="ProductFacilityLocation" to-value-field="moveInfo.targetProductFacilityLocation"/>
<!-- get totalQuantity: iterate through perProductOiirailList and add up quantity (from OrderItemShipGrpInvRes) -->
<calculate field="moveInfo.totalQuantity"><number value="0"/></calculate>
<iterate entry="perProductOiirail" list="perProductOiirailList">
<calculate field="moveInfo.totalQuantity">
<calcop field="moveInfo.totalQuantity" operator="add"><calcop field="perProductOiirail.quantity" operator="get"/></calcop>
</calculate>
</iterate>
<!-- if moveInfo.totalQuantity is greater than the total quantity in the given location, show an error and do something (hopefully) intelligent -->
<entity-and entity-name="InventoryItem" list="inventoryItemList">
<field-map field-name="productId" from-field="productId"/>
<field-map field-name="facilityId" from-field="parameters.facilityId"/>
<field-map field-name="locationSeqId" from-field="locationSeqId"/>
</entity-and>
<calculate field="totalQuantityOnHand"><number value="0"/></calculate>
<calculate field="totalAvailableToPromise"><number value="0"/></calculate>
<iterate entry="inventoryItem" list="inventoryItemList">
<calculate field="totalQuantityOnHand">
<calcop field="totalQuantityOnHand" operator="get"/>
<calcop field="inventoryItem.quantityOnHandTotal" operator="get"/>
</calculate>
<calculate field="totalAvailableToPromise">
<calcop field="totalAvailableToPromise" operator="get"/>
<calcop field="inventoryItem.availableToPromiseTotal" operator="get"/>
</calculate>
</iterate>
<set from-field="totalQuantityOnHand" field="moveInfo.quantityOnHandTotalFrom"/>
<set from-field="totalAvailableToPromise" field="moveInfo.availableToPromiseTotalFrom"/>
<if-compare-field field="totalQuantityOnHand" to-field="moveInfo.totalQuantity" operator="less" type="BigDecimal">
<!-- not enough on hand for move: add warning message, set moveInfo.totalQuantity to totalQuantityOnHand, and don't even bother looking for pre-emptive replenishment needs for this location right now -->
<string-to-list string="Warning in stock move: for facility [${parameters.facilityId}] and product [${productId}] going from location [${productFacilityLocation.locationSeqId}] to location [${moveInfo.targetProductFacilityLocation.locationSeqId}] a quantity of [${moveInfo.totalQuantity}] was needed but there are only [${totalQuantityOnHand}] on hand (this will be in the pick list with the full quantity on hand, but note that this will not be enough to prepare for all orders reserved against this location)" list="warningMessageList"/>
<set from-field="totalQuantityOnHand" field="moveInfo.totalQuantity"/>
<else>
<!-- check ProductFacilityLocation for where this is going and see if we should do a pre-emptive transfer too... -->
<!-- get all InventoryItems and total the availableToPromise (and quantityOnHand) for the target location -->
<get-related value-field="moveInfo.targetProductFacilityLocation" relation-name="InventoryItem" list="targetInventoryItemList"/>
<calculate field="targetTotalAvailableToPromise"><number value="0"/></calculate>
<calculate field="targetTotalQuantityOnHand"><number value="0"/></calculate>
<iterate entry="inventoryItem" list="targetInventoryItemList">
<calculate field="targetTotalAvailableToPromise">
<calcop field="targetTotalAvailableToPromise" operator="add"><calcop field="inventoryItem.availableToPromiseTotal" operator="get"/></calcop>
</calculate>
<calculate field="targetTotalQuantityOnHand">
<calcop field="targetTotalQuantityOnHand" operator="add"><calcop field="inventoryItem.quantityOnHandTotal" operator="get"/></calcop>
</calculate>
</iterate>
<set from-field="targetTotalAvailableToPromise" field="moveInfo.availableToPromiseTotalTo"/>
<set from-field="targetTotalQuantityOnHand" field="moveInfo.quantityOnHandTotalTo"/>
<!--
now if there is enough left and there is less than the minimum for the
ProductFacilityLocation, move the restock quantity from ProductFacilityLocation
-->
<if-compare-field field="targetTotalAvailableToPromise" to-field="moveInfo.targetProductFacilityLocation.minimumStock" operator="less" type="BigDecimal">
<if-empty field="targetLocationSimpleMoveQuantity[moveInfo.targetProductFacilityLocation.locationSeqId]">
<!-- if targetTotalAvailableToPromise is less than productFacilityLocation.minimumStock, move over the moveInfo.targetProductFacilityLocation.moveQuantity -->
<!-- if trying to move more from the location than is there, find the difference and put it in targetLocationSimpleMoveQuantity[moveInfo.targetProductFacilityLocation.locationSeqId] -->
<calculate field="moveInfo.totalQuantity">
<calcop field="moveInfo.totalQuantity" operator="add"><calcop field="moveInfo.targetProductFacilityLocation.moveQuantity" operator="get"/></calcop>
</calculate>
<else>
<!--
see if there is enough left for the full amount in
targetLocationSimpleMoveQuantity[moveInfo.targetProductFacilityLocation.locationSeqId],
if not do whatever is available and put difference back in
targetLocationSimpleMoveQuantity[moveInfo.targetProductFacilityLocation.locationSeqId]
-->
<calculate field="moveInfo.totalQuantity">
<calcop field="moveInfo.totalQuantity" operator="add"><calcop field="targetLocationSimpleMoveQuantity[moveInfo.targetProductFacilityLocation.locationSeqId]" operator="get"/></calcop>
</calculate>
</else>
</if-empty>
<if-compare-field field="totalQuantityOnHand" to-field="moveInfo.totalQuantity" operator="less" type="BigDecimal">
<calculate field="targetLocationSimpleMoveQuantity[moveInfo.targetProductFacilityLocation.locationSeqId]">
<calcop field="moveInfo.totalQuantity" operator="subtract"><calcop field="totalQuantityOnHand" operator="get"/></calcop>
</calculate>
<set from-field="totalQuantityOnHand" field="moveInfo.totalQuantity"/>
</if-compare-field>
<!-- add it to the stockMoveHandled Map to keep track of minimumStock based transfers already done so they are not duplicated below -->
<set value="Y" field="stockMoveHandled[moveInfo.targetProductFacilityLocation.locationSeqId]"/>
</if-compare-field>
</else>
</if-compare-field>
<!-- add the moveInfo to the master list -->
<field-to-list field="moveInfo" list="moveByOisgirInfoList"/>
<clear-field field="moveInfo"/>
</else>
</if-empty>
</iterate-map>
</iterate-map>
<field-to-result field="moveByOisgirInfoList"/>
<field-to-result field="stockMoveHandled"/>
<!-- TODO: go through targetLocationSimpleMoveQuantity Map and if any item is not zero, add a warning message -->
<field-to-result field="warningMessageList"/>
</simple-method>
<simple-method method-name="findStockMovesRecommended" short-description="Find all Stock Moves recommended to be done based on ProductFacilityLocation settings">
<!-- TODO: make this method aware of serialized inventory in addition to non-serialized inventory -->
<check-permission permission="FACILITY" action="_VIEW">
<fail-property resource="ProductUiLabels" property="ProductFacilityViewPermissionError"/>
</check-permission>
<check-errors/>
<set field="stockMoveHandled" from-field="parameters.stockMoveHandled"/>
<set field="productSave.productId" value=""/>
<!--
The will involve finding all stock moves for ProductFacilityLocation based pre-emptive moves.
These will go in a separate list returned as moveByPflInfoList where each info Map contains:
- product
- facilityLocationFrom
- facilityLocationTo
- targetProductFacilityLocation
- quantityOnHandTotalFrom
- quantityOnHandTotalTo
- availableToPromiseTotalFrom
- availableToPromiseTotalTo
- totalQuantity
-->
<!-- start by finding all Xxx where locationTypeEnumId=FLT_PICKLOC and total of InventoryItems in the loc are less than minimumStock (ProductFacilityLocation, FacilityLocation, InventoryItem) -->
<entity-and entity-name="ProductFacilityLocationQuantityTest" list="productFacilityLocationQuantityTestList">
<field-map field-name="locationTypeEnumId" value="FLT_PICKLOC"/>
<field-map field-name="facilityId" from-field="parameters.facilityId"/>
<order-by field-name="productId"/>
</entity-and>
<iterate entry="productFacilityLocationQuantityTest" list="productFacilityLocationQuantityTestList">
<!-- TODO: this comparison could be done by the database and be more efficient, but since we don't have field to field comparisons in the entity engine or EntityCondition operations in simple-methods, some work needs to be done before that can happen -->
<set field="minimumStock" from-field="productFacilityLocationQuantityTest.minimumStock" default-value="0" type="BigDecimal"/>
<if>
<condition>
<and>
<not><if-empty field="productFacilityLocationQuantityTest.moveQuantity"/></not>
<if-compare field="productFacilityLocationQuantityTest.moveQuantity" operator="greater" value="0" type="BigDecimal"/>
<or>
<and>
<if-empty field="productFacilityLocationQuantityTest.availableToPromiseTotal"/>
<if-compare field="minimumStock" operator="greater" value="0" type="BigDecimal"></if-compare>
</and>
<and>
<not><if-empty field="productFacilityLocationQuantityTest.availableToPromiseTotal"/></not>
<if-compare-field field="productFacilityLocationQuantityTest.availableToPromiseTotal" to-field="minimumStock" operator="less" type="BigDecimal"/>
</and>
</or>
</and>
</condition>
<then>
<if>
<condition>
<or>
<if-empty field="stockMoveHandled[productFacilityLocationQuantityTest.locationSeqId]"/>
<if-compare field="stockMoveHandled[productFacilityLocationQuantityTest.locationSeqId]" operator="not-equals" value="Y"/>
</or>
</condition>
<then>
<if-compare-field field="productFacilityLocationQuantityTest.productId" to-field="productSave.productId" operator="not-equals">
<get-related-one value-field="productFacilityLocationQuantityTest" relation-name="Product" to-value-field="productSave"/>
<clear-field field="fromLocationTotalAvailableToPromise"/>
</if-compare-field>
<get-related-one value-field="productFacilityLocationQuantityTest" relation-name="FacilityLocation" to-value-field="targetFacilityLocationSave"/>
<!-- create a moveInfo for each from location with a corresponding quantity -->
<entity-and entity-name="InventoryItemAndLocation" list="inventoryItemAndLocationList">
<field-map field-name="productId" from-field="productFacilityLocationQuantityTest.productId"/>
<field-map field-name="facilityId" from-field="productFacilityLocationQuantityTest.facilityId"/>
<field-map field-name="locationTypeEnumId" value="FLT_BULK"/>
</entity-and>
<if-empty field="inventoryItemAndLocationList">
<string-to-list string="Error in stock move, could not find a bulk location for facility [${productFacilityLocationQuantityTest.facilityId}] and product [${productFacilityLocationQuantityTest.productId}]" list="warningMessageList"/>
<else>
<set from-field="productFacilityLocationQuantityTest.moveQuantity" field="targetLocationMoveQuantity"/>
<!-- start by making a Map where the locationSeqId is the key and the value is a List of InventoryItemAndLocation -->
<clear-field field="InventoryItemAndLocationByLocMap"/>
<iterate entry="InventoryItemAndLocation" list="inventoryItemAndLocationList">
<field-to-list field="InventoryItemAndLocation" list="InventoryItemAndLocationByLocMap[InventoryItemAndLocation.locationSeqId]"/>
</iterate>
<clear-field field="locationSeqId"/>
<clear-field field="perLocationInventoryItemAndLocList"/>
<iterate-map key="locationSeqId" value="perLocationInventoryItemAndLocList" map="InventoryItemAndLocationByLocMap">
<if-empty field="fromLocationTotalAvailableToPromise[locationSeqId]">
<calculate field="totalQuantityOnHand"><number value="0"/></calculate>
<calculate field="totalAvailableToPromise"><number value="0"/></calculate>
<iterate entry="inventoryItem" list="perLocationInventoryItemAndLocList">
<calculate field="totalQuantityOnHand">
<calcop field="totalQuantityOnHand" operator="add"><calcop field="inventoryItem.quantityOnHandTotal" operator="get"/></calcop>
</calculate>
<calculate field="totalAvailableToPromise">
<calcop field="totalAvailableToPromise" operator="add"><calcop field="inventoryItem.availableToPromiseTotal" operator="get"/></calcop>
</calculate>
</iterate>
<else>
<set from-field="fromLocationTotalAvailableToPromise[locationSeqId]" field="totalAvailableToPromise"/>
</else>
</if-empty>
<if>
<condition>
<and>
<if-compare field="totalAvailableToPromise" operator="greater" value="0" type="BigDecimal"/>
<if-compare field="targetLocationMoveQuantity" operator="greater" value="0" type="BigDecimal"/>
</and>
</condition>
<then>
<set from-field="productSave" field="moveInfo.product"/>
<set from-field="targetFacilityLocationSave" field="moveInfo.facilityLocationTo"/>
<first-from-list entry="InventoryItemAndLocation" list="perLocationInventoryItemAndLocList"/>
<get-related-one value-field="InventoryItemAndLocation" relation-name="FacilityLocation" to-value-field="moveInfo.facilityLocationFrom"/>
<get-related-one value-field="productFacilityLocationQuantityTest" relation-name="ProductFacilityLocation" to-value-field="moveInfo.targetProductFacilityLocation"/>
<set from-field="productFacilityLocationQuantityTest.availableToPromiseTotal" field="moveInfo.availableToPromiseTotalTo"/>
<set from-field="productFacilityLocationQuantityTest.quantityOnHandTotal" field="moveInfo.quantityOnHandTotalTo"/>
<set from-field="totalAvailableToPromise" field="moveInfo.availableToPromiseTotalFrom"/>
<set from-field="totalQuantityOnHand" field="moveInfo.quantityOnHandTotalFrom"/>
<!-- see if there is enough left for the full amount in targetLocationMoveQuantity,
if not do whatever is available and put difference back in targetLocationMoveQuantity,
trying to move more from the location than is there, find the difference and put it in targetLocationMoveQuantity -->
<if-compare-field field="totalAvailableToPromise" to-field="targetLocationMoveQuantity" operator="less" type="BigDecimal">
<calculate field="targetLocationMoveQuantity">
<calcop field="targetLocationMoveQuantity" operator="subtract"><calcop field="totalAvailableToPromise" operator="get"/></calcop>
</calculate>
<set from-field="totalAvailableToPromise" field="moveInfo.totalQuantity"/>
<calculate field="fromLocationTotalAvailableToPromise[locationSeqId]"><number value="0"/></calculate>
<else>
<set from-field="targetLocationMoveQuantity" field="moveInfo.totalQuantity"/>
<calculate field="fromLocationTotalAvailableToPromise[locationSeqId]">
<calcop field="totalAvailableToPromise" operator="subtract"><calcop field="targetLocationMoveQuantity" operator="get"/></calcop>
</calculate>
<calculate field="targetLocationMoveQuantity"><number value="0"/></calculate>
</else>
</if-compare-field>
<!-- add the moveInfo to the master list -->
<field-to-list field="moveInfo" list="moveByPflInfoList"/>
<clear-field field="moveInfo"/>
</then>
</if>
</iterate-map>
<!-- TODO: add a warning message if there are not BULK location(s) with sufficient quantity -->
</else>
</if-empty>
</then>
</if>
</then>
</if>
</iterate>
<field-to-result field="moveByPflInfoList"/>
<field-to-result field="warningMessageList"/>
</simple-method>
<simple-method method-name="processPhysicalStockMove" short-description="Process a Physical Stock Move from one FacilityLocation to another, in the same Facility">
<check-permission permission="FACILITY" action="_CREATE">
<fail-property resource="ProductUiLabels" property="ProductFacilityCreatePermissionError"/>
</check-permission>
<check-permission permission="FACILITY" action="_UPDATE">
<fail-property resource="ProductUiLabels" property="ProductFacilityUpdatePermissionError"/>
</check-permission>
<check-errors/>
<set from-field="parameters.quantityMoved" field="quantityLeftToProcess"/>
<!-- move this over just in case a list was passed in that should be appended to; used when calling multiple times in succession -->
<set from-field="parameters.warningMessageList" field="warningMessageList"/>
<!-- Start by processing all OrderItemShipGrpInvRes from the origin FacilityLocation, and transfer to target, OrderItem.statusId (approved), orderBy reservedDatetime -->
<set value="ITEM_APPROVED" field="orderItemStatusId"/>
<call-simple-method method-name="processOisgirMoveByStatusInline"/>
<!-- should only need to pay attention to two statuses: ITEM_APPROVED, ITEM_CREATED, then the rest can go to non-reserved -->
<set value="ITEM_CREATED" field="orderItemStatusId"/>
<call-simple-method method-name="processOisgirMoveByStatusInline"/>
<!-- Now for any remaining quantity, do a simple, ie non-OISGIR, stock move -->
<if-compare field="quantityLeftToProcess" operator="greater" value="0" type="BigDecimal">
<!-- find InventoryItems in the origin location with some availableToPromise to move -->
<entity-and entity-name="InventoryItem" list="inventoryItemList">
<field-map field-name="productId" from-field="parameters.productId"/>
<field-map field-name="facilityId" from-field="parameters.facilityId"/>
<field-map field-name="locationSeqId" from-field="parameters.locationSeqId"/>
<order-by field-name="datetimeReceived"/>
</entity-and>
<iterate entry="inventoryItem" list="inventoryItemList">
<!-- for each inventoryItem only process if quantityLeftToProcess > 0 and inventoryItem.availableToPromiseTotal > 0 -->
<if-compare field="quantityLeftToProcess" operator="greater" value="0" type="BigDecimal">
<if-compare field="inventoryItem.availableToPromiseTotal" operator="greater" value="0" type="BigDecimal">
<!-- set the currentQuantityToMove to the lower of these two values to ensure we don't move too much -->
<if-compare-field field="quantityLeftToProcess" to-field="inventoryItem.availableToPromiseTotal" operator="greater" type="BigDecimal">
<set from-field="inventoryItem.availableToPromiseTotal" field="currentQuantityToMove"/>
<else>
<set from-field="quantityLeftToProcess" field="currentQuantityToMove"/>
</else>
</if-compare-field>
<!-- create a new InventoryItem for the targetInventoryItem -->
<make-value entity-name="InventoryItem" map="inventoryItem" value-field="targetInventoryItem"/>
<set from-field="parameters.targetLocationSeqId" field="targetInventoryItem.locationSeqId"/>
<!-- now that targetInventoryItem is populated with quantities, etc, call createInventoryItem service -->
<clear-field field="createNonOisgirTargetDetailMap"/>
<set-service-fields map="targetInventoryItem" service-name="createInventoryItem" to-map="createInventoryItemMap"/>
<call-service service-name="createInventoryItem" in-map-name="createInventoryItemMap">
<result-to-field result-name="inventoryItemId" field="createNonOisgirTargetDetailMap.inventoryItemId"/>
</call-service>
<!-- instead of updating InventoryItem, add an InventoryItemDetail for new Target InventoryItem -->
<set from-field="currentQuantityToMove" field="createNonOisgirTargetDetailMap.availableToPromiseDiff"/>
<set from-field="currentQuantityToMove" field="createNonOisgirTargetDetailMap.quantityOnHandDiff"/>
<call-service service-name="createInventoryItemDetail" in-map-name="createNonOisgirTargetDetailMap"/>
<!-- okay, move it over, calculate new ATP, QOH -->
<!-- instead of updating InventoryItem, add an InventoryItemDetail -->
<clear-field field="createNonOisgirDetailMap"/>
<set from-field="inventoryItem.inventoryItemId" field="createNonOisgirDetailMap.inventoryItemId"/>
<calculate field="createNonOisgirDetailMap.availableToPromiseDiff">
<calcop field="currentQuantityToMove" operator="negative"/>
</calculate>
<calculate field="createNonOisgirDetailMap.quantityOnHandDiff">
<calcop field="currentQuantityToMove" operator="negative"/>
</calculate>
<call-service service-name="createInventoryItemDetail" in-map-name="createNonOisgirDetailMap"/>
<refresh-value value-field="targetInventoryItem"/>
<log level="info" message="Just created new targetInventoryItem from non-OISGIR (ie location level based) [${targetInventoryItem}]"/>
</if-compare>
</if-compare>
</iterate>
</if-compare>
<if-compare field="quantityLeftToProcess" operator="greater" value="0" type="BigDecimal">
<!-- this really isn't good, that means more was moved than we found to move, how did that happen? -->
<string-to-list string="ERROR: Not enough available inventory found in location [${parameters.locationSeqId}] in facility [${parameters.facilityId}], did not reallocate ${quantityLeftToProcess} of the ${parameters.quantityMoved} reported as physically moved." list="warningMessageList"/>
</if-compare>
<field-to-result field="warningMessageList"/>
</simple-method>
<simple-method method-name="processOisgirMoveByStatusInline" short-description="Inline method to process OISGIR stock move for a specific OrderItem.statusId">
<entity-and entity-name="OrderItemShipGrpInvResAndItemLocation" list="orderItemShipGrpInvResAndItemLocationList">
<field-map field-name="productId" from-field="parameters.productId"/>
<field-map field-name="facilityId" from-field="parameters.facilityId"/>
<field-map field-name="locationSeqId" from-field="parameters.locationSeqId"/>
<field-map field-name="orderItemStatusId" from-field="orderItemStatusId"/>
<order-by field-name="reservedDatetime"/>
</entity-and>
<clear-field field="oiirailByInvItemMap"/>
<log level="info" message="In processOisgirMoveByStatusInline orderItemShipGrpInvResAndItemLocationList=${orderItemShipGrpInvResAndItemLocationList}"/>
<iterate entry="orderItemShipGrpInvResAndItemLocation" list="orderItemShipGrpInvResAndItemLocationList">
<field-to-list field="orderItemShipGrpInvResAndItemLocation" list="oiirailByInvItemMap[orderItemShipGrpInvResAndItemLocation.inventoryItemId]"/>
</iterate>
<iterate-map key="inventoryItemId" value="orderItemShipGrpInvResAndItemLocationList" map="oiirailByInvItemMap">
<entity-one entity-name="InventoryItem" value-field="inventoryItem"/>
<!-- if this is a serialized InventoryItem, then just update the location -->
<if-compare field="inventoryItem.inventoryItemTypeId" operator="equals" value="SERIALIZED_INV_ITEM">
<set from-field="parameters.targetLocationSeqId" field="inventoryItem.locationSeqId"/>
<store-value value-field="inventoryItem"/>
</if-compare>
<!--
if this is a non-serialized InventoryItem, create a new targetInventoryItem in the
target location and start moving all OrderItemShipGrpInvRes over, plus min move quantity from ProductFacilityLocation
-->
<if-compare field="inventoryItem.inventoryItemTypeId" operator="equals" value="NON_SERIAL_INV_ITEM">
<!-- create a new InventoryItem for the targetInventoryItem -->
<make-value entity-name="InventoryItem" map="inventoryItem" value-field="targetInventoryItem"/>
<set from-field="parameters.targetLocationSeqId" field="targetInventoryItem.locationSeqId"/>
<!-- now that targetInventoryItem is populated with initial values, call createInventoryItem service -->
<set-service-fields map="targetInventoryItem" service-name="createInventoryItem" to-map="createInventoryItemMap"/>
<call-service service-name="createInventoryItem" in-map-name="createInventoryItemMap">
<result-to-field result-name="inventoryItemId" field="targetInventoryItem.inventoryItemId"/>
</call-service>
<!-- refresh the value object now that the create has been done -->
<refresh-value value-field="targetInventoryItem"/>
<!-- if we don't end up setting any detail for this inventory item, then we'll delete it after -->
<set value="N" field="haveSetIiDetail"/>
<!-- <log level="info" message="Just created new targetInventoryItem from OISGIR [${targetInventoryItem}]"/> -->
<!--
go through OrderItemShipGrpInvRes and move each one over, starting with the older making
sure to stay within quantityOnHand
-->
<set from-field="inventoryItem.quantityOnHandTotal" field="remainingQuantityOnHand"/>
<iterate entry="orderItemShipGrpInvResAndItemLocation" list="orderItemShipGrpInvResAndItemLocationList">
<get-related-one value-field="orderItemShipGrpInvResAndItemLocation" relation-name="OrderItemShipGrpInvRes" to-value-field="orderItemShipGrpInvRes"/>
<!-- see if there is enough physically moved over to move this reservation -->
<!-- note that we need to check this because if there was a reservation against an
InventoryItem then the full quantity will be against that item for the given
OISGIR (reservation), so we would have to split it up
-->
<clear-field field="reservedQuantityLeftOver"/>
<clear-field field="currentQuantityToMove"/>
<clear-field field="quantityNotAvailableToMove"/>
<if-compare field="quantityLeftToProcess" operator="greater" value="0" type="Double">
<if-compare-field field="quantityLeftToProcess" to-field="orderItemShipGrpInvRes.quantity" operator="less" type="BigDecimal">
<!-- the complicated part: can split up the OISGIR (reservation) into two parts, and then transfer the inventory, somewhat complicated... -->
<set from-field="quantityLeftToProcess" field="currentQuantityToMove"/>
<!-- NOTE: we are now handling this scenario, so we'll not log this message
<set value="WARNING: In process stock move: quantityLeftToProcess [${quantityLeftToProcess}] was &lt; orderItemShipGrpInvRes.quantity [${orderItemShipGrpInvResAndItemLocation.quantity}] for orderItemShipGrpInvRes [${orderItemShipGrpInvRes}], which shouldn't happen from the inventory reservation process and must has happened from a later inventory change; the reservation or other information may need be manually corrected for this stock move to go through." field="quantityErrorMessage"/>
<log level="warning" message="${quantityErrorMessage}"/>
-->
<else>
<set from-field="orderItemShipGrpInvRes.quantity" field="currentQuantityToMove"/>
</else>
</if-compare-field>
<!-- now make sure that the currentQuantityToMove we decided on is not greater than remainingQuantityOnHand... -->
<if-compare-field field="currentQuantityToMove" to-field="remainingQuantityOnHand" operator="greater" type="BigDecimal">
<set from-field="remainingQuantityOnHand" field="currentQuantityToMove"/>
</if-compare-field>
<if-empty field="orderItemShipGrpInvRes.quantityNotAvailable">
<set value="0" field="orderItemShipGrpInvRes.quantityNotAvailable" type="BigDecimal" />
</if-empty>
<calculate field="reservedQuantityLeftOver" type="BigDecimal">
<calcop field="orderItemShipGrpInvRes.quantity" operator="get"/>
<calcop field="orderItemShipGrpInvRes.quantityNotAvailable" operator="negative"/>
<calcop field="currentQuantityToMove" operator="negative"/>
</calculate>
<!-- now one other little trick: reservedQuantityLeftOver is not empty and if orderItemShipGrpInvRes.quantityNotAvailable is greater than reservedQuantityLeftOver, then the difference should be moved -->
<if>
<condition>
<and>
<not><if-empty field="reservedQuantityLeftOver"/></not>
<if-compare field="reservedQuantityLeftOver" value="0" operator="greater" type="BigDecimal"/>
</and>
</condition>
<then>
<set value="0" field="quantityNotAvailableToMove" type="BigDecimal" />
</then>
<else>
<set from-field="orderItemShipGrpInvRes.quantityNotAvailable" field="quantityNotAvailableToMove"/>
</else>
</if>
<!-- make a new OISGIR value object -->
<make-value entity-name="OrderItemShipGrpInvRes" map="orderItemShipGrpInvRes" value-field="targetOrderItemShipGrpInvRes"/>
<set from-field="targetInventoryItem.inventoryItemId" field="targetOrderItemShipGrpInvRes.inventoryItemId"/>
<!-- okay, move it over, start by calculating new QOHs; note that don't need to modify the ATP since with an OISGIR that has already been subtracted out, so it shouldn't be subtracted from the source or added to the target -->
<!-- instead of updating InventoryItem, add an InventoryItemDetail -->
<clear-field field="createOisgirDetailMap"/>
<clear-field field="createOisgirTargetDetailMap"/>
<set from-field="inventoryItem.inventoryItemId" field="createOisgirDetailMap.inventoryItemId"/>
<set from-field="targetInventoryItem.inventoryItemId" field="createOisgirTargetDetailMap.inventoryItemId"/>
<calculate field="createOisgirDetailMap.quantityOnHandDiff">
<calcop field="currentQuantityToMove" operator="negative"/>
</calculate>
<calculate field="createOisgirTargetDetailMap.quantityOnHandDiff">
<calcop field="currentQuantityToMove" operator="get"/>
</calculate>
<!--
note that nothing needs to be done with the OISGIR.quantity because if all is
balanced the new one should have a zero availableToPromise, of course that is different when a
ProductFacilityLocation based pre-emptive move is done
-->
<!-- if the OISGIR has a quantityNotAvailable, move that over by adding it to the origin, and subtracting it from the target -->
<if-not-empty field="orderItemShipGrpInvRes.quantityNotAvailable">
<if-compare field="orderItemShipGrpInvRes.quantityNotAvailable" operator="greater" value="0" type="BigDecimal">
<calculate field="createOisgirDetailMap.availableToPromiseDiff">
<calcop field="quantityNotAvailableToMove" operator="get"/>
</calculate>
<calculate field="createOisgirTargetDetailMap.availableToPromiseDiff">
<calcop field="quantityNotAvailableToMove" operator="negative"/>
</calculate>
</if-compare>
</if-not-empty>
<call-service service-name="createInventoryItemDetail" in-map-name="createOisgirDetailMap"/>
<call-service service-name="createInventoryItemDetail" in-map-name="createOisgirTargetDetailMap"/>
<!-- create the new and remove or update the old OISGIRs -->
<calculate field="targetOrderItemShipGrpInvRes.quantity" type="BigDecimal">
<calcop field="currentQuantityToMove" operator="get"/>
<calcop field="quantityNotAvailableToMove" operator="get"/>
</calculate>
<calculate field="targetOrderItemShipGrpInvRes.quantityNotAvailable" type="BigDecimal">
<calcop field="quantityNotAvailableToMove" operator="get"/>
</calculate>
<create-value value-field="targetOrderItemShipGrpInvRes"/>
<calculate field="orderItemShipGrpInvRes.quantity">
<calcop field="orderItemShipGrpInvRes.quantity" operator="get"/>
<calcop field="currentQuantityToMove" operator="negative"/>
<calcop field="quantityNotAvailableToMove" operator="negative"/>
</calculate>
<calculate field="orderItemShipGrpInvRes.quantityNotAvailable">
<calcop field="orderItemShipGrpInvRes.quantityNotAvailable" operator="get"/>
<calcop field="quantityNotAvailableToMove" operator="negative"/>
</calculate>
<store-value value-field="orderItemShipGrpInvRes"/>
<if>
<condition>
<and>
<if-compare field="orderItemShipGrpInvRes.quantity" operator="equals" value="0" type="BigDecimal" />
<if-compare field="orderItemShipGrpInvRes.quantityNotAvailable" operator="equals" value="0" type="BigDecimal" />
</and>
</condition>
<then>
<remove-value value-field="orderItemShipGrpInvRes"/>
</then>
<else>
<store-value value-field="orderItemShipGrpInvRes"/>
</else>
</if>
<set value="Y" field="haveSetIiDetail"/>
<!-- deduct the orderItemShipGrpInvRes.quantity from quantityLeftToProcess -->
<calculate field="quantityLeftToProcess">
<calcop field="quantityLeftToProcess" operator="get"/>
<calcop field="currentQuantityToMove" operator="negative"/>
</calculate>
<calculate field="remainingQuantityOnHand">
<calcop field="remainingQuantityOnHand" operator="subtract"/>
<calcop field="currentQuantityToMove" operator="negative"/>
</calculate>
<!-- part of the following log data prep: <refresh-value value-field="targetInventoryItem"/> -->
<!-- <log level="info" message="Just update targetInventoryItem from OISGIR [${targetInventoryItem}]"/> -->
</if-compare>
</iterate>
<if-compare field="haveSetIiDetail" operator="equals" value="N">
<log level="info" message="Did not end up finding an OISGIR that we could move inventory with, so removing targetInventoryItem: ${targetInventoryItem}"/>
<remove-value value-field="targetInventoryItem"/>
</if-compare>
</if-compare>
</iterate-map>
</simple-method>
<simple-method method-name="findProductInventorylocations" short-description="Find Product's inventory locations from facility">
<entity-condition entity-name="InventoryItemAndLocation" list="LocationList">
<condition-list>
<condition-expr field-name="facilityId" operator="equals" from-field="parameters.facilityId"/>
<condition-expr field-name="productId" operator="equals" from-field="parameters.productId"/>
<condition-expr field-name="locationSeqId" operator="not-equals" from-field="nullField"/>
</condition-list>
</entity-condition>
<field-to-result field="LocationList"/>
</simple-method>
</simple-methods>