| <?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"> |
| <!-- CostComponent services --> |
| <simple-method method-name="cancelCostComponents" short-description="Cancels CostComponents"> |
| <set from-field="parameters.costComponentId" field="costsAndMap.costComponentId"/> |
| <set from-field="parameters.productId" field="costsAndMap.productId"/> |
| <set from-field="parameters.costUomId" field="costsAndMap.costUomId"/> |
| <set from-field="parameters.costComponentTypeId" field="costsAndMap.costComponentTypeId"/> |
| <find-by-and entity-name="CostComponent" map="costsAndMap" list="existingCosts"/> |
| <filter-list-by-date list="existingCosts"/> |
| <iterate list="existingCosts" entry="existingCost"> |
| <now-timestamp field="existingCost.thruDate"/> |
| <store-value value-field="existingCost"/> |
| </iterate> |
| </simple-method> |
| <simple-method method-name="recreateCostComponent" short-description="Create a CostComponent and cancel the existing ones"> |
| <!-- The existing costs of the same type are expired --> |
| <set from-field="parameters.productId" field="costsAndMap.productId"/> |
| <set from-field="parameters.costUomId" field="costsAndMap.costUomId"/> |
| <set from-field="parameters.costComponentTypeId" field="costsAndMap.costComponentTypeId"/> |
| <find-by-and entity-name="CostComponent" map="costsAndMap" list="existingCosts"/> |
| <filter-list-by-date list="existingCosts"/> |
| <iterate list="existingCosts" entry="existingCost"> |
| <now-timestamp field="existingCost.thruDate"/> |
| <store-value value-field="existingCost"/> |
| </iterate> |
| <!-- The new cost is created --> |
| <make-value value-field="newEntity" entity-name="CostComponent"/> |
| <set-nonpk-fields map="parameters" value-field="newEntity"/> |
| <sequenced-id sequence-name="CostComponent" field="newEntity.costComponentId"/> |
| <if-empty field="newEntity.fromDate"> |
| <now-timestamp field="newEntity.fromDate"/> |
| </if-empty> |
| <create-value value-field="newEntity"/> |
| <field-to-result field="newEntity.costComponentId" result-name="costComponentId"/> |
| </simple-method> |
| |
| <!-- Services to get the product and tasks costs --> |
| <simple-method method-name="getProductCost" short-description="Gets the product's costs (from CostComponent or ProductPrice)"> |
| <entity-condition entity-name="CostComponent" list="costComponents" filter-by-date="true"> |
| <condition-list> |
| <condition-expr field-name="productId" operator="equals" from-field="parameters.productId"/> |
| <condition-expr field-name="costUomId" operator="equals" from-field="parameters.currencyUomId"/> |
| <condition-expr field-name="costComponentTypeId" operator="like" value="${parameters.costComponentTypePrefix}_%"/> |
| </condition-list> |
| </entity-condition> |
| <set field="productCost" value="0" type="BigDecimal"/> |
| <iterate list="costComponents" entry="costComponent"> |
| <calculate field="productCost" decimal-scale="6"> |
| <calcop operator="add" field="costComponent.cost"> |
| <calcop operator="get" field="productCost"/> |
| </calcop> |
| </calculate> |
| <!--set field="productCost" value="${costComponent.cost + productCost}" type="BigDecimal"/--> |
| </iterate> |
| <!-- if the cost is zero, and the product is a variant, get the cost of the virtual --> |
| <if-compare field="productCost" operator="equals" value="0" type="BigDecimal"> |
| <entity-one entity-name="Product" value-field="product"/> |
| <set from-field="product.productId" field="assocAndMap.productIdTo"/> |
| <set value="PRODUCT_VARIANT" field="assocAndMap.productAssocTypeId"/> |
| <find-by-and entity-name="ProductAssoc" map="assocAndMap" list="virtualAssocs"/> |
| <filter-list-by-date list="virtualAssocs"/> |
| <first-from-list list="virtualAssocs" entry="virtualAssoc"/> |
| <if-not-empty field="virtualAssoc"> |
| <set from-field="virtualAssoc.productId" field="inputMap.productId"/> |
| <set from-field="parameters.currencyUomId" field="inputMap.currencyUomId"/> |
| <set from-field="parameters.costComponentTypePrefix" field="inputMap.costComponentTypePrefix"/> |
| <call-service service-name="getProductCost" in-map-name="inputMap"> |
| <result-to-field result-name="productCost"/> |
| </call-service> |
| </if-not-empty> |
| </if-compare> |
| <!-- if the cost is zero, get the purchase cost from the SupplierProduct --> |
| <if-compare field="productCost" operator="equals" value="0" type="BigDecimal"> |
| <set field="orderByList[]" value="+supplierPrefOrderId"/> |
| <set field="orderByList[]" value="+lastPrice"/> |
| <clear-field field="costsAndMap"/> |
| <set from-field="parameters.productId" field="costsAndMap.productId"/> |
| <set from-field="parameters.currencyUomId" field="costsAndMap.currencyUomId"/> |
| <find-by-and entity-name="SupplierProduct" map="costsAndMap" list="priceCosts" order-by-list="orderByList"/> |
| <filter-list-by-date list="priceCosts" from-field-name="availableFromDate" thru-field-name="availableThruDate"/> |
| <first-from-list list="priceCosts" entry="priceCost"/> |
| <if-not-empty field="priceCost.lastPrice"> |
| <set from-field="priceCost.lastPrice" field="productCost"/> |
| </if-not-empty> |
| <!-- if the cost is zero, get the purchase cost from the SupplierProduct |
| in a different currency and try to convert |
| --> |
| <if-compare field="productCost" operator="equals" value="0" type="BigDecimal"> |
| <clear-field field="costsAndMap"/> |
| <set from-field="parameters.productId" field="costsAndMap.productId"/> |
| <set from-field="parameters.productPriceTypeId" field="costsAndMap.productPriceTypeId"/> |
| <find-by-and entity-name="SupplierProduct" map="costsAndMap" list="priceCosts" order-by-list="orderByList"/> |
| <filter-list-by-date list="priceCosts" from-field-name="availableFromDate" thru-field-name="availableThruDate"/> |
| <first-from-list list="priceCosts" entry="priceCost"/> |
| <if-not-empty field="priceCost.lastPrice"> |
| <!-- we try to convert the lastPrice to the desired currency --> |
| <clear-field field="inputMap"/> |
| <set from-field="priceCost.lastPrice" field="inputMap.originalValue"/> |
| <set from-field="priceCost.currencyUomId" field="inputMap.uomId"/> |
| <set from-field="parameters.currencyUomId" field="inputMap.uomIdTo"/> |
| |
| <call-service service-name="convertUom" in-map-name="inputMap" require-new-transaction="true" break-on-error="false"> |
| <result-to-field result-name="convertedValue" field="productCost"/> |
| </call-service> |
| |
| <!-- if currency conversion fails then a 0 cost will be returned --> |
| <if-empty field="productCost"> |
| <log level="warning" message="Currency conversion failed for ProductCost lookup; unable to convert from ${priceCost.currencyUomId} to ${parameters.currencyUomId}"/> |
| <set field="productCost" value="0" type="BigDecimal"/> |
| </if-empty> |
| </if-not-empty> |
| </if-compare> |
| </if-compare> |
| <!-- |
| <if-compare field="productCost" operator="equals" value="0" type="BigDecimal"> |
| <clear-field field="costsAndMap"/> |
| <set from-field="parameters.productId" field="costsAndMap.productId"/> |
| <set from-field="parameters.currencyUomId" field="costsAndMap.currencyUomId"/> |
| <set from-field="parameters.productPriceTypeId" field="costsAndMap.productPriceTypeId"/> |
| <find-by-and entity-name="ProductPrice" map="costsAndMap" list="priceCosts"/> |
| <filter-list-by-date list="priceCosts"/> |
| <first-from-list list="priceCosts" entry="priceCost"/> |
| <if-not-empty field="priceCost.price"> |
| <set from-field="priceCost.price" field="productCost"/> |
| </if-not-empty> |
| </if-compare> |
| --> |
| <field-to-result field="productCost"/> |
| </simple-method> |
| <simple-method method-name="getTaskCost" short-description="Gets the production run task's costs"> |
| <!-- First of all, the estimated task time is computed --> |
| <set-service-fields service-name="getEstimatedTaskTime" map="parameters" to-map="inputMap"/> |
| <set from-field="parameters.workEffortId" field="inputMap.taskId"/> |
| <call-service service-name="getEstimatedTaskTime" in-map-name="inputMap"> |
| <result-to-field result-name="estimatedTaskTime" field="totalEstimatedTaskTime"/> |
| <result-to-field result-name="setupTime"/> |
| </call-service> |
| |
| <calculate field="estimatedTaskTime" decimal-scale="6"> |
| <calcop operator="subtract" field="totalEstimatedTaskTime"> |
| <calcop operator="get" field="setupTime"/> |
| </calcop> |
| </calculate> |
| |
| <entity-one entity-name="WorkEffort" value-field="task"/> |
| <if-not-empty field="task"> |
| <get-related-one value-field="task" relation-name="FixedAsset" to-value-field="fixedAsset"/> |
| <set from-field="parameters.currencyUomId" field="costsAndMap.amountUomId"/> |
| <set value="SETUP_COST" field="costsAndMap.fixedAssetStdCostTypeId"/> |
| <get-related value-field="fixedAsset" relation-name="FixedAssetStdCost" map="costsAndMap" list="setupCosts"/> |
| <filter-list-by-date list="setupCosts"/> |
| <!--<filter-list-by-and list-name="costs" map-name="costsAndMap"/>--> |
| <first-from-list list="setupCosts" entry="setupCost"/> |
| <set value="USAGE_COST" field="costsAndMap.fixedAssetStdCostTypeId"/> |
| <get-related value-field="fixedAsset" relation-name="FixedAssetStdCost" map="costsAndMap" list="usageCosts"/> |
| <filter-list-by-date list="usageCosts"/> |
| <first-from-list list="usageCosts" entry="usageCost"/> |
| </if-not-empty> |
| <calculate field="taskCost" decimal-scale="6"> |
| <calcop operator="add"> |
| <calcop operator="multiply" field="estimatedTaskTime"> |
| <calcop operator="get" field="usageCost.amount"/> |
| </calcop> |
| <calcop operator="multiply" field="setupTime"> |
| <calcop operator="get" field="setupCost.amount"/> |
| </calcop> |
| </calcop> |
| </calculate> |
| |
| <!-- Time is converted from milliseconds to hours --> |
| <calculate field="taskCost" decimal-scale="6"> |
| <calcop operator="divide" field="taskCost"> |
| <number value="3600000"/> |
| </calcop> |
| </calculate> |
| |
| <!-- Now compute the costs derived from CostComponentCalc records associated with the task --> |
| <get-related relation-name="WorkEffortCostCalc" list="weccs" value-field="task"/> |
| <filter-list-by-date list="weccs"/> |
| <iterate list="weccs" entry="wecc"> |
| <clear-field field="totalCostComponentCost"/> |
| <clear-field field="totalCostComponentTime"/> |
| <get-related-one relation-name="CostComponentCalc" to-value-field="costComponentCalc" value-field="wecc"/> |
| <get-related-one relation-name="CustomMethod" to-value-field="customMethod" value-field="costComponentCalc"/> |
| <if-empty field="customMethod"> |
| <if-not-empty field="costComponentCalc.perMilliSecond"> |
| <if-compare operator="not-equals" value="0" field="costComponentCalc.perMilliSecond" type="BigDecimal"> |
| <calculate field="totalCostComponentTime" decimal-scale="6"> |
| <calcop operator="divide" field="totalEstimatedTaskTime"> |
| <calcop operator="get" field="costComponentCalc.perMilliSecond"/> |
| </calcop> |
| </calculate> |
| <calculate field="totalCostComponentCost" decimal-scale="6"> |
| <calcop operator="multiply" field="totalCostComponentTime"> |
| <calcop operator="get" field="costComponentCalc.variableCost"/> |
| </calcop> |
| </calculate> |
| <calculate field="totalCostComponentCost" decimal-scale="6"> |
| <calcop operator="add" field="totalCostComponentCost"> |
| <calcop operator="get" field="costComponentCalc.fixedCost"/> |
| </calcop> |
| </calculate> |
| <set field="costsByType.${wecc.costComponentTypeId}" from-field="totalCostComponentCost"/> |
| </if-compare> |
| </if-not-empty> |
| <else> |
| <!-- FIXME: formulas are still not supported for standard costs --> |
| </else> |
| </if-empty> |
| </iterate> |
| <field-to-result field="taskCost"/> |
| <field-to-result field="costsByType"/> |
| </simple-method> |
| |
| <!-- services to automatically generate cost information --> |
| <simple-method method-name="calculateAllProductsCosts" short-description="Calculates estimated costs for all the products"> |
| <!--filter-by-date="true"--> |
| <entity-condition entity-name="Product" list="products"> |
| <select-field field-name="productId"/> |
| <order-by field-name="-billOfMaterialLevel"/> |
| </entity-condition> |
| <set from-field="parameters.currencyUomId" field="inMap.currencyUomId"/> |
| <set from-field="parameters.costComponentTypePrefix" field="inMap.costComponentTypePrefix"/> |
| <iterate list="products" entry="product"> |
| <set from-field="product.productId" field="inMap.productId"/> |
| <call-service service-name="calculateProductCosts" in-map-name="inMap"/> |
| </iterate> |
| </simple-method> |
| <simple-method method-name="calculateProductCosts" short-description="Calculates the product's cost"> |
| <!-- the existing costs are expired --> |
| <set value="${parameters.costComponentTypePrefix}_ROUTE_COST" field="cancelMap.costComponentTypeId"/> |
| <set from-field="parameters.productId" field="cancelMap.productId"/> |
| <set from-field="parameters.currencyUomId" field="cancelMap.costUomId"/> |
| <call-service service-name="cancelCostComponents" in-map-name="cancelMap"/> |
| <set value="${parameters.costComponentTypePrefix}_MAT_COST" field="cancelMap.costComponentTypeId"/> |
| <call-service service-name="cancelCostComponents" in-map-name="cancelMap"/> |
| <!-- calculate the total materials' cost --> |
| <set from-field="parameters.productId" field="callSvcMap.productId"/> |
| <call-service service-name="getManufacturingComponents" in-map-name="callSvcMap"> |
| <result-to-field result-name="componentsMap"/> |
| </call-service> |
| <if-not-empty field="componentsMap"> |
| <iterate list="componentsMap" entry="componentMap"> |
| <clear-field field="inputMap"/> |
| <set field="product" from-field="componentMap.product"/> |
| <set field="inputMap.productId" from-field="product.productId"/> |
| <set field="inputMap.currencyUomId" from-field="parameters.currencyUomId"/> |
| <set field="inputMap.costComponentTypePrefix" from-field="parameters.costComponentTypePrefix"/> |
| <call-service service-name="getProductCost" in-map-name="inputMap"> |
| <result-to-field result-name="productCost"/> |
| </call-service> |
| <calculate field="totalProductsCost" decimal-scale="6"> |
| <calcop operator="add" field="totalProductsCost"> |
| <calcop operator="multiply" field="componentMap.quantity"> |
| <calcop operator="get" field="productCost"/> |
| </calcop> |
| </calcop> |
| </calculate> |
| </iterate> |
| <else> |
| <clear-field field="inputMap"/> |
| <set field="inputMap.productId" from-field="parameters.productId"/> |
| <set field="inputMap.currencyUomId" from-field="parameters.currencyUomId"/> |
| <set field="inputMap.costComponentTypePrefix" from-field="parameters.costComponentTypePrefix"/> |
| <call-service service-name="getProductCost" in-map-name="inputMap"> |
| <result-to-field result-name="productCost"/> |
| </call-service> |
| <calculate field="totalProductsCost" decimal-scale="6"> |
| <calcop operator="get" field="productCost"/> |
| </calculate> |
| </else> |
| </if-not-empty> |
| <!-- calculate the total tasks' cost --> |
| <set field="callSvcMap.ignoreDefaultRouting" value="Y"/> |
| <call-service service-name="getProductRouting" in-map-name="callSvcMap"> |
| <result-to-field result-name="tasks"/> |
| <result-to-field result-name="routing"/> |
| </call-service> |
| <iterate list="tasks" entry="task"> |
| <clear-field field="callSvcMap"/> |
| <set from-field="task.workEffortIdTo" field="callSvcMap.workEffortId"/> |
| <set from-field="parameters.currencyUomId" field="callSvcMap.currencyUomId"/> |
| <set from-field="parameters.productId" field="callSvcMap.productId"/> |
| <set from-field="routing.workEffortId" field="callSvcMap.routingId"/> |
| <call-service service-name="getTaskCost" in-map-name="callSvcMap"> |
| <result-to-field result-name="taskCost" field="taskCost"/> |
| <result-to-field result-name="costsByType" field="costsByType"/> |
| </call-service> |
| <calculate field="totalTaskCost" decimal-scale="6"> |
| <calcop operator="add" field="totalTaskCost"> |
| <calcop operator="get" field="taskCost"/> |
| </calcop> |
| </calculate> |
| <iterate-map map="costsByType" key="costType" value="costAmount"> |
| <if-not-empty field="totalCostsByType.${costType}"> |
| <calculate field="totalCostsByType.${costType}" decimal-scale="6"> |
| <calcop operator="add" field="costAmount"> |
| <calcop operator="get" field="totalCostsByType.${costType}"/> |
| </calcop> |
| </calculate> |
| <else> |
| <set field="totalCostsByType.${costType}" from-field="costAmount"/> |
| </else> |
| </if-not-empty> |
| <calculate field="totalOtherTaskCost" decimal-scale="6"> |
| <calcop operator="add" field="totalOtherTaskCost"> |
| <calcop operator="get" field="costAmount"/> |
| </calcop> |
| </calculate> |
| </iterate-map> |
| </iterate> |
| |
| <calculate field="totalCost" decimal-scale="6"> |
| <calcop operator="add" field="totalTaskCost"> |
| <calcop operator="get" field="totalProductsCost"/> |
| <calcop operator="get" field="totalOtherTaskCost"/> |
| </calcop> |
| </calculate> |
| |
| <!-- The CostComponent records are created. --> |
| <if-not-empty field="totalTaskCost"> |
| <if-compare field="totalTaskCost" operator="greater" value="0" type="BigDecimal"> |
| <clear-field field="callSvcMap"/> |
| <set value="${parameters.costComponentTypePrefix}_ROUTE_COST" field="callSvcMap.costComponentTypeId"/> |
| <set from-field="parameters.productId" field="callSvcMap.productId"/> |
| <set from-field="parameters.currencyUomId" field="callSvcMap.costUomId"/> |
| <set from-field="totalTaskCost" field="callSvcMap.cost"/> |
| <call-service service-name="recreateCostComponent" in-map-name="callSvcMap"/> |
| </if-compare> |
| </if-not-empty> |
| <if-not-empty field="totalProductsCost"> |
| <if-compare field="totalProductsCost" operator="greater" value="0" type="BigDecimal"> |
| <clear-field field="callSvcMap"/> |
| <set value="${parameters.costComponentTypePrefix}_MAT_COST" field="callSvcMap.costComponentTypeId"/> |
| <set from-field="parameters.productId" field="callSvcMap.productId"/> |
| <set from-field="parameters.currencyUomId" field="callSvcMap.costUomId"/> |
| <set from-field="totalProductsCost" field="callSvcMap.cost"/> |
| <call-service service-name="recreateCostComponent" in-map-name="callSvcMap"/> |
| </if-compare> |
| </if-not-empty> |
| <iterate-map map="totalCostsByType" key="costType" value="totalCostAmount"> |
| <clear-field field="callSvcMap"/> |
| <set value="${parameters.costComponentTypePrefix}_${costType}" field="callSvcMap.costComponentTypeId"/> |
| <set from-field="parameters.productId" field="callSvcMap.productId"/> |
| <set from-field="parameters.currencyUomId" field="callSvcMap.costUomId"/> |
| <set from-field="totalCostAmount" field="callSvcMap.cost"/> |
| <call-service service-name="recreateCostComponent" in-map-name="callSvcMap"/> |
| </iterate-map> |
| |
| <!-- Now compute the costs derived from CostComponentCalc records associated with the product --> |
| <entity-condition entity-name="ProductCostComponentCalc" list="productCostComponentCalcs" filter-by-date="true"> |
| <condition-expr field-name="productId" from-field="parameters.productId"/> |
| <order-by field-name="sequenceNum"/> |
| </entity-condition> |
| <iterate list="productCostComponentCalcs" entry="productCostComponentCalc"> |
| <get-related-one relation-name="CostComponentCalc" to-value-field="costComponentCalc" value-field="productCostComponentCalc"/> |
| <get-related-one relation-name="CustomMethod" to-value-field="customMethod" value-field="costComponentCalc"/> |
| <if-empty field="customMethod"> |
| <!-- TODO: not supported for CostComponentCalc entries directly associated to a product --> |
| <log level="warning" message="Unable to create cost component for cost component calc with id [${costComponentCalc.costComponentCalcId}] because customMethod is not set"/> |
| <else> |
| <clear-field field="customMethodParameters"/> |
| <set field="customMethodParameters.productCostComponentCalc" from-field="productCostComponentCalc"/> |
| <set field="customMethodParameters.costComponentCalc" from-field="costComponentCalc"/> |
| <set from-field="parameters.currencyUomId" field="customMethodParameters.currencyUomId"/> |
| <set from-field="parameters.costComponentTypePrefix" field="customMethodParameters.costComponentTypePrefix"/> |
| <set from-field="totalCost" field="customMethodParameters.baseCost"/> |
| <call-service service-name="${customMethod.customMethodName}" in-map-name="customMethodParameters"> |
| <result-to-field result-name="productCostAdjustment"/> |
| </call-service> |
| <clear-field field="callSvcMap"/> |
| <set value="${parameters.costComponentTypePrefix}_${productCostComponentCalc.costComponentTypeId}" field="callSvcMap.costComponentTypeId"/> |
| <set from-field="productCostComponentCalc.productId" field="callSvcMap.productId"/> |
| <set from-field="parameters.currencyUomId" field="callSvcMap.costUomId"/> |
| <set from-field="productCostAdjustment" field="callSvcMap.cost"/> |
| <call-service service-name="recreateCostComponent" in-map-name="callSvcMap"/> |
| <!--set field="totalCost" value="${totalCost + productCostAdjustment}" type="BigDecimal"/--> |
| <calculate field="totalCost" decimal-scale="6"> |
| <calcop operator="add" field="totalCost"> |
| <calcop operator="get" field="productCostAdjustment"/> |
| </calcop> |
| </calculate> |
| </else> |
| </if-empty> |
| </iterate> |
| |
| <field-to-result field="totalCost"/> |
| </simple-method> |
| <simple-method method-name="calculateProductAverageCost" short-description="Calculate inventory average cost for a product"> |
| <entity-condition entity-name="InventoryItem" list="inventoryItems"> |
| <condition-list> |
| <condition-expr field-name="productId" from-field="parameters.productId"/> |
| <condition-expr field-name="facilityId" from-field="parameters.facilityId" ignore-if-empty="true"/> |
| <condition-expr field-name="ownerPartyId" from-field="parameters.ownerPartyId" ignore-if-empty="true"/> |
| <condition-expr field-name="unitCost" operator="not-equals" from-field="nullField"/> |
| </condition-list> |
| <select-field field-name="quantityOnHandTotal"/> |
| <select-field field-name="unitCost"/> |
| <select-field field-name="currencyUomId"/> |
| </entity-condition> |
| <set field="totalQuantityOnHand" type="BigDecimal" value="0"/> |
| <set field="totalInventoryCost" type="BigDecimal" value="0"/> |
| <set field="absValOfTotalQOH" type="BigDecimal" value="0"/> |
| <set field="absValOfTotalInvCost" type="BigDecimal" value="0"/> |
| <set field="differentCurrencies" type="Boolean" value="false"/> |
| <iterate list="inventoryItems" entry="inventoryItem"> |
| <calculate field="totalQuantityOnHand"> |
| <calcop operator="add" > |
| <calcop operator="get" field="totalQuantityOnHand"/> |
| <calcop operator="get" field="inventoryItem.quantityOnHandTotal"/> |
| </calcop> |
| </calculate> |
| |
| <if-empty field="currencyUomId"> |
| <set field="currencyUomId" from-field="inventoryItem.currencyUomId"/> |
| </if-empty> |
| <if-compare field="differentCurrencies" operator="equals" value="false" type="Boolean"> |
| <if-compare-field field="inventoryItem.currencyUomId" operator="equals" to-field="currencyUomId"> |
| <calculate field="totalInventoryCost" type="BigDecimal"> |
| <calcop operator="add"> |
| <calcop operator="get" field="totalInventoryCost"/> |
| <calcop operator="multiply"> |
| <calcop operator="get" field="inventoryItem.quantityOnHandTotal"/> |
| <calcop operator="get" field="inventoryItem.unitCost"/> |
| </calcop> |
| </calcop> |
| </calculate> |
| |
| <!-- calculation of absolute values of QOH and total inventory cost --> |
| <if-compare field="inventoryItem.quantityOnHandTotal" operator="less" value="0"> |
| <calculate field="absValOfTotalQOH"> |
| <calcop operator="add"> |
| <calcop operator="get" field="absValOfTotalQOH"/> |
| <calcop operator="negative" field="inventoryItem.quantityOnHandTotal"/> |
| </calcop> |
| </calculate> |
| <calculate field="absValOfTotalInvCost" type="BigDecimal"> |
| <calcop operator="add"> |
| <calcop operator="get" field="absValOfTotalInvCost"/> |
| <calcop operator="multiply"> |
| <calcop operator="negative" field="inventoryItem.quantityOnHandTotal"/> |
| <calcop operator="get" field="inventoryItem.unitCost"/> |
| </calcop> |
| </calcop> |
| </calculate> |
| <else> |
| <calculate field="absValOfTotalQOH"> |
| <calcop operator="add" > |
| <calcop operator="get" field="absValOfTotalQOH"/> |
| <calcop operator="get" field="inventoryItem.quantityOnHandTotal"/> |
| </calcop> |
| </calculate> |
| <calculate field="absValOfTotalInvCost" type="BigDecimal"> |
| <calcop operator="add"> |
| <calcop operator="get" field="absValOfTotalInvCost"/> |
| <calcop operator="multiply"> |
| <calcop operator="get" field="inventoryItem.quantityOnHandTotal"/> |
| <calcop operator="get" field="inventoryItem.unitCost"/> |
| </calcop> |
| </calcop> |
| </calculate> |
| </else> |
| </if-compare> |
| <else> |
| <set field="differentCurrencies" type="Boolean" value="true"/> |
| </else> |
| </if-compare-field> |
| </if-compare> |
| </iterate> |
| |
| <if-compare field="absValOfTotalQOH" operator="not-equals" value="0" type="BigDecimal"> |
| <calculate field="productAverageCost" type="BigDecimal"> |
| <calcop operator="divide"> |
| <calcop operator="get" field="absValOfTotalInvCost"/> |
| <calcop operator="get" field="absValOfTotalQOH"/> |
| </calcop> |
| </calculate> |
| <else> |
| <set field="productAverageCost" type="BigDecimal" value="0"/> |
| </else> |
| </if-compare> |
| <field-to-result field="totalQuantityOnHand"/> |
| <if-compare field="differentCurrencies" operator="equals" value="false" type="Boolean"> |
| <field-to-result field="totalInventoryCost"/> |
| <field-to-result field="productAverageCost"/> |
| <field-to-result field="currencyUomId"/> |
| </if-compare> |
| </simple-method> |
| |
| <simple-method method-name="updateProductAverageCostOnReceiveInventory" short-description="Update a Product Average Cost record on receive inventory"> |
| <entity-one entity-name="InventoryItem" value-field="inventoryItem"/> |
| <set field="organizationPartyId" from-field="inventoryItem.ownerPartyId"/> |
| <if-empty field="organizationPartyId"> |
| <entity-one entity-name="Facility" value-field="facility" auto-field-map="true"/> |
| <set field="organizationPartyId" from-field="facility.ownerPartyId"/> |
| <if-empty field="organizationPartyId"> |
| <get-related-one relation-name="ProductStore" to-value-field="productStore" value-field="facility"/> |
| <set field="organizationPartyId" from-field="productStore.ownerPartyId"/> |
| <if-empty field="organizationPartyId"> |
| <add-error error-list-name="error_list"> |
| <fail-property resource="ProductUiLabels" property="ProductOwnerPartyIsMissing"/> |
| </add-error> |
| </if-empty> |
| <check-errors/> |
| </if-empty> |
| </if-empty> |
| |
| <entity-and entity-name="ProductAverageCost" list="productAverageCostList" filter-by-date="true"> |
| <field-map field-name="productId" from-field="parameters.productId"/> |
| <field-map field-name="facilityId" from-field="parameters.facilityId"/> |
| <field-map field-name="productAverageCostTypeId" value="SIMPLE_AVG_COST"/> |
| <field-map field-name="organizationPartyId"/> |
| </entity-and> |
| <first-from-list list="productAverageCostList" entry="productAverageCost"/> |
| |
| <!-- <log level="always" message="In updateProductAverageCostOnReceiveInventory found productAverageCost: ${productAverageCost}"/> --> |
| |
| <set-service-fields service-name="createProductAverageCost" map="parameters" to-map="productAverageCostMap"/> |
| <set field="productAverageCostMap.productAverageCostTypeId" value="SIMPLE_AVG_COST"/> |
| <set field="productAverageCostMap.organizationPartyId" from-field="organizationPartyId"/> |
| <if-empty field="productAverageCost"> |
| <set field="productAverageCostMap.averageCost" from-field="inventoryItem.unitCost"/> |
| <else> |
| <!-- Expire existing one and calculate average cost --> |
| <set-service-fields service-name="updateProductAverageCost" map="productAverageCost" to-map="updateProductAverageCostMap"/> |
| <now-timestamp field="updateProductAverageCostMap.thruDate"/> |
| <call-service service-name="updateProductAverageCost" in-map-name="updateProductAverageCostMap"/> |
| |
| <set field="serviceInMap.productId" from-field="parameters.productId"/> |
| <set field="serviceInMap.facilityId" from-field="parameters.facilityId"/> |
| <call-service service-name="getInventoryAvailableByFacility" in-map-name="serviceInMap"> |
| <result-to-field result-name="quantityOnHandTotal"/> |
| </call-service> |
| |
| <set field="oldProductQuantity" value="${quantityOnHandTotal - parameters.quantityAccepted}" type="BigDecimal"/> |
| <set field="productAverageCostMap.averageCost" value="${((productAverageCost.averageCost * oldProductQuantity) + (inventoryItem.unitCost * parameters.quantityAccepted))/(quantityOnHandTotal)}" type="BigDecimal"/> |
| <property-to-field resource="arithmetic" property="finaccount.decimals" field="roundingDecimals" default="2"/> |
| <property-to-field resource="arithmetic" property="finaccount.roundingSimpleMethod" field="roundingMode" default="HalfUp"/> |
| <calculate field="productAverageCostMap.averageCost" type="BigDecimal" decimal-scale="${roundingDecimals}" rounding-mode="${roundingMode}"> |
| <calcop operator="get" field="productAverageCostMap.averageCost"/> |
| </calculate> |
| |
| <!-- ensure that the new ProductAverageCost record has a different PK than the previous one by setting the fromDate to now, plus an offset if needed --> |
| <now-timestamp field="nowTimestamp"/> |
| <set field="timeDiff" value="${groovy: return nowTimestamp.getTime() - productAverageCost.fromDate.getTime()}" type="Long"/> |
| <if-compare field="timeDiff" operator="less-equals" value="1000" type="Long"> |
| <set-calendar field="productAverageCostMap.fromDate" from-field="nowTimestamp" seconds="+1"/> |
| <else> |
| <set field="productAverageCostMap.fromDate" from-field="nowTimestamp"/> |
| </else> |
| </if-compare> |
| </else> |
| </if-empty> |
| |
| <!-- <log level="always" message="In updateProductAverageCostOnReceiveInventory creating new average cost with productAverageCostMap: ${productAverageCostMap}"/> --> |
| <call-service service-name="createProductAverageCost" in-map-name="productAverageCostMap"/> |
| <log level="info" message="For facilityId ${parameters.facilityId}, Average cost of product ${parameters.productId} is set from ${updateProductAverageCostMap.averageCost} to ${productAverageCostMap.averageCost}"/> |
| </simple-method> |
| |
| <simple-method method-name="getProductAverageCost" short-description="Service to get the average cost of product"> |
| <set field="inventoryItem" from-field="parameters.inventoryItem"/> |
| <set field="getPartyAcctgPrefMap.organizationPartyId" from-field="inventoryItem.ownerPartyId"/> |
| <call-service service-name="getPartyAccountingPreferences" in-map-name="getPartyAcctgPrefMap"> |
| <result-to-field result-name="partyAccountingPreference"/> |
| </call-service> |
| <if-compare field="partyAccountingPreference.cogsMethodId" operator="equals" value="COGS_AVG_COST"> |
| <entity-and entity-name="ProductAverageCost" list="productAverageCostList" filter-by-date="true"> |
| <field-map field-name="productAverageCostTypeId" value="SIMPLE_AVG_COST"/> <!-- TODO: handle for WEIGHTED_AVG_COST and MOVING_AVG_COST --> |
| <field-map field-name="organizationPartyId" from-field="inventoryItem.ownerPartyId"/> |
| <field-map field-name="productId" from-field="inventoryItem.productId"/> |
| <field-map field-name="facilityId" from-field="inventoryItem.facilityId"/> |
| </entity-and> |
| <first-from-list list="productAverageCostList" entry="productAverageCost"/> |
| </if-compare> |
| <if-not-empty field="productAverageCost"> |
| <set field="unitCost" from-field="productAverageCost.averageCost" type="BigDecimal"/> |
| <else> |
| <set field="unitCost" from-field="inventoryItem.unitCost" type="BigDecimal"/> |
| </else> |
| </if-not-empty> |
| <field-to-result field="unitCost"/> |
| </simple-method> |
| |
| <simple-method method-name="productCostPercentageFormula" short-description="Formula that creates a cost component equal to a percentage of total product cost"> |
| <set field="productCostComponentCalc" from-field="parameters.productCostComponentCalc"/> |
| <set field="costComponentCalc" from-field="parameters.costComponentCalc"/> |
| <set field="inputMap.productId" from-field="productCostComponentCalc.productId"/> |
| <set field="inputMap.currencyUomId" from-field="parameters.currencyUomId"/> |
| <set field="inputMap.costComponentTypePrefix" from-field="parameters.costComponentTypePrefix"/> |
| <call-service service-name="getProductCost" in-map-name="inputMap"> |
| <result-to-field result-name="productCost"/> |
| </call-service> |
| <!--set field="productCostAdjustment" value="${parameters.baseCost * costComponentCalc.fixedCost}" type="BigDecimal"/--> |
| <calculate field="productCostAdjustment" type="BigDecimal" decimal-scale="6"> |
| <calcop operator="multiply" field="costComponentCalc.fixedCost"> |
| <calcop operator="get" field="parameters.baseCost"/> |
| </calcop> |
| </calculate> |
| <field-to-result field="productCostAdjustment"/> |
| </simple-method> |
| </simple-methods> |