[ofbiz-plugins] branch trunk updated: Improved: Convert FactServices.xml minilang to groovy.

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

[ofbiz-plugins] branch trunk updated: Improved: Convert FactServices.xml minilang to groovy.

mbrohl
This is an automated email from the ASF dual-hosted git repository.

mbrohl pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ofbiz-plugins.git


The following commit(s) were added to refs/heads/trunk by this push:
     new b50d949  Improved: Convert FactServices.xml minilang to groovy.
b50d949 is described below

commit b50d949376e7d04195dda1be5f72d68f8249a770
Author: Michael Brohl <[hidden email]>
AuthorDate: Fri Mar 13 17:44:51 2020 +0100

    Improved: Convert FactServices.xml minilang to groovy.
   
    (OFBIZ-11030)
   
    Thanks Pierre Smits for reporting and Sebastian Berg for providing the
    patch.
---
 bi/groovyScripts/FactServices.groovy | 664 ++++++++++++++++++++++++++++++
 bi/minilang/FactServices.xml         | 761 -----------------------------------
 bi/servicedef/services.xml           |  32 +-
 3 files changed, 680 insertions(+), 777 deletions(-)

diff --git a/bi/groovyScripts/FactServices.groovy b/bi/groovyScripts/FactServices.groovy
new file mode 100644
index 0000000..80816b7
--- /dev/null
+++ b/bi/groovyScripts/FactServices.groovy
@@ -0,0 +1,664 @@
+/*
+ * 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.sql.Timestamp
+import java.text.SimpleDateFormat
+import java.sql.Date
+
+import org.apache.ofbiz.base.util.UtilDateTime
+import org.apache.ofbiz.base.util.UtilProperties
+import org.apache.ofbiz.base.util.UtilValidate
+import org.apache.ofbiz.entity.GenericValue
+import org.apache.ofbiz.entity.condition.EntityCondition
+import org.apache.ofbiz.entity.condition.EntityOperator
+import org.apache.ofbiz.order.order.OrderReadHelper
+import org.apache.ofbiz.service.ServiceUtil
+
+
+def loadSalesInvoiceFact() {
+    GenericValue invoice = from("Invoice").where(parameters).queryOne()
+    if (!invoice) {
+        String errorMessage = UtilProperties.getMessage("AccountingUiLabels", "AccountingInvoiceDoesNotExists", parameters.locale)
+        logError(errorMessage)
+        return error(errorMessage)
+    }
+    if ("SALES_INVOICE".equals(invoice.invoiceTypeId)) {
+        Map andConditions = ["invoiceItemTypeId": "INV_FPROD_ITEM"]
+        List invoiceItems = delegator.getRelated("InvoiceItem", null, null, invoice, false)
+        for (GenericValue invoiceItem : invoiceItems) {
+            Map inMap = [invoice: invoice, invoiceItem: invoiceItem]
+            run service: "loadSalesInvoiceItemFact", with: inMap
+        }
+    }
+    return success()
+}
+
+def loadSalesInvoiceItemFact() {
+    GenericValue invoice = parameters.invoice
+    GenericValue invoiceItem = parameters.invoiceItem
+    if (!invoice) {
+        invoice = from("Invoice").where(parameters).queryOne()
+    }
+    if (UtilValidate.isEmpty(invoiceItem)) {
+        invoiceItem = from("InvoiceItem").where(parameters).queryOne()
+    }
+    if (!invoice) {
+        String errorMessage = UtilProperties.getMessage("AccountingUiLabels", "AccountingInvoiceDoesNotExists", parameters.locale)
+        logError(errorMessage)
+        return error(errorMessage)
+    }
+    if (!invoiceItem) {
+        String errorMessage = UtilProperties.getMessage("AccountingUiLabels", "AccountingInvoiceItemDoesNotExists", parameters.locale)
+        logError(errorMessage)
+        return error(errorMessage)
+    }
+
+    if ("SALES_INVOICE".equals(invoice.invoiceTypeId)) {
+        GenericValue fact = from("SalesInvoiceItemFact").where(invoiceId: invoiceItem.invoiceId, invoiceItemSeqId: invoiceItem.invoiceItemSeqId).queryOne()
+        // key handling
+        if (!fact) {
+            Map inMap
+            Map naturalKeyFields
+            Map serviceResult
+            String dimensionId
+            fact = makeValue("SalesInvoiceItemFact")
+            fact.invoiceId = invoice.invoiceId
+            fact.invoiceItemSeqId = invoiceItem.invoiceItemSeqId
+            // conversion of the invoice date
+            if (invoice.invoiceDate) {
+                inMap = [:]
+                naturalKeyFields = [:]
+                inMap.dimensionEntityName = "DateDimension"
+                Date invoiceDate = new Date(invoice.invoiceDate.getTime())
+                naturalKeyFields.dateValue = invoiceDate
+                inMap.naturalKeyFields = naturalKeyFields
+                serviceResult = run service: "getDimensionIdFromNaturalKey", with: inMap
+                fact.invoiceDateDimId = serviceResult.dimensionId
+                if (!fact.invoiceDateDimId) {
+                    fact.invoiceDateDimId = "_NF_"
+                }
+            } else {
+                fact.invoiceDateDimId = "_NA_"
+            }
+
+            // conversion of the product id
+            if (invoiceItem.productId) {
+                inMap = [:]
+                naturalKeyFields = [:]
+                inMap.dimensionEntityName = "ProductDimension"
+                naturalKeyFields.productId = invoiceItem.productId
+                inMap.naturalKeyFields = naturalKeyFields
+                serviceResult = run service: "getDimensionIdFromNaturalKey", with: inMap
+                fact.productDimId = serviceResult.dimensionId
+                if (!act.productDimId) {
+                    fact.productDimId = "_NF_"
+                }
+            } else {
+                fact.productDimId = "_NA_"
+            }
+
+            // conversion of the invoice currency
+            if (invoice.currencyUomId) {
+                inMap = [:]
+                naturalKeyFields = [:]
+                inMap.dimensionEntityName = "CurrencyDimension"
+                naturalKeyFields.currencyId = invoice.currencyUomId
+                inMap.naturalKeyFields = naturalKeyFields
+                serviceResult = run service: "getDimensionIdFromNaturalKey", with: inMap
+                fact.origCurrencyDimId = serviceResult.dimensionId
+                if (!fact.origCurrencyDimId) {
+                    fact.origCurrencyDimId = "_NF_"
+                }
+            } else {
+                fact.origCurrencyDimId = "_NA_"
+            }
+
+            // TODO
+            fact.orderId = "_NA_"
+            fact.billToCustomerDimId = "_NA_"
+            fact.create()
+        }
+        /*
+         * facts handling
+         */
+        fact.quantity = (BigDecimal) invoiceItem.quantity
+        fact.extGrossAmount = (BigDecimal) 0
+        fact.extDiscountAmount = (BigDecimal) 0
+        fact.extTaxAmount = (BigDecimal) 0
+        fact.extNetAmount = (BigDecimal) 0
+
+        if (invoiceItem.quantity && invoiceItem.amount) {
+            fact.extGrossAmount = invoiceItem.quantity * invoiceItem.amount
+        }
+
+        Map andConditions
+        // taxes
+        andConditions = [invoiceItemTypeId: "ITM_SALES_TAX"]
+        List taxes = delegator.getRelated("ChildrenInvoiceItem", null, null, invoiceItem, false)
+        for (GenericValue tax : taxes) {
+            if (tax.amount) {
+            fact.extTaxAmount = fact.extTaxAmount + tax.amount
+            }
+        }
+        // discounts
+        andConditions = [invoiceItemTypeId: "ITM_PROMOTION_ADJ"]
+        List discounts = delegator.getRelated("ChildrenInvoiceItem", null, null, invoiceItem, false)
+        for (GenericValue discount : discounts) {
+            if (discount.amount) {
+            fact.extDiscountAmount = fact.extDiscountAmount - discount.amount
+            }
+        }
+        fact.extNetAmount = fact.extGrossAmount - fact.extDiscountAmount
+        // TODO: prorate invoice header discounts and shipping charges
+        // TODO: costs
+        fact.extManFixedCost = (BigDecimal) 0
+        fact.extManVarCost = (BigDecimal) 0
+        fact.extStorageCost = (BigDecimal) 0
+        fact.extDistributionCost = (BigDecimal) 0
+
+        BigDecimal costs = fact.extManFixedCost + fact.extManVarCost + fact.extStorageCost + fact.extDistributionCost
+        fact.contributionAmount = fact.extNetAmount - costs
+
+        fact.store()
+    }
+    return success()
+}
+
+
+def loadSalesOrderFact() {
+    GenericValue orderHeader = from("OrderHeader").where(parameters).queryOne()
+    if (!orderHeader) {
+        String errorMessage = UtilProperties.getMessage("OrderErrorUiLabels", "OrderOrderIdDoesNotExists", parameters.locale)
+        logError(errorMessage)
+        return error(errorMessage)
+    }
+    if ("SALES_ORDER".equals(orderHeader.orderTypeId)) {
+        if ("ORDER_APPROVED".equals(orderHeader.statusId)) {
+            List orderItems = from("OrderItem")
+                .where("orderId", orderHeader.orderId, "orderItemTypeId", "PRODUCT_ORDER_ITEM")
+                .queryList()
+
+            for (GenericValue orderItem : orderItems) {
+                Map inMap = [:]
+                inMap.orderHeader = orderHeader
+                inMap.orderItem = orderItem
+                inMap.orderAdjustment = null
+                run service: "loadSalesOrderItemFact", with: inMap
+            }
+        }
+    }
+    return success()
+}
+
+def loadSalesOrderItemFact() {
+    Map inMap
+    Map naturalKeyFields
+    Map serviceResult
+    GenericValue orderHeader = parameters.orderHeader
+    GenericValue orderItem = parameters.orderItem
+    GenericValue orderAdjustment = parameters.orderAdjustment
+
+    List orderAdjustments
+    GenericValue orderStatus
+
+
+    if (!orderHeader) {
+        orderHeader = from("OrderHeader").where(parameters).queryOne()
+    }
+    if (!orderItem) {
+        orderItem = from("OrderItem").where(parameters).queryOne()
+    }
+    if (!orderAdjustment) {
+        orderAdjustments = from("OrderAdjustment").where("orderId": orderItem.orderId).queryList()
+    }
+    if (!orderHeader) {
+        String errorMessage = UtilProperties.getMessage("OrderErrorUiLabels", "OrderOrderIdDoesNotExists", parameters.locale)
+        logError(errorMessage)
+        return error(errorMessage)
+    }
+    if (!orderItem) {
+        String errorMessage = UtilProperties.getMessage("OrderErrorUiLabels", "OrderOrderItemIdDoesNotExists", parameters.locale)
+        logError(errorMessage)
+        return error(errorMessage)
+    }
+
+    if ("ORDER_APPROVED".equals(orderHeader.statusId)) {
+        GenericValue fact = from("SalesOrderItemFact").where(orderId: orderItem.orderId, orderItemSeqId: orderItem.orderItemSeqId).queryOne()
+        // key handling
+        if (!fact) {
+            fact = makeValue("SalesOrderItemFact")
+            fact.orderId = orderHeader.orderId
+            fact.orderItemSeqId = orderItem.orderItemSeqId
+            fact.productStoreId = orderHeader.productStoreId
+            fact.salesChannelEnumId = orderHeader.salesChannelEnumId
+            fact.statusId = orderItem.statusId
+
+            // account
+            if (orderHeader.productStoreId) {
+                GenericValue account =  from("ProductStore").where(productStoreId: orderHeader.productStoreId).queryOne()
+                fact.account = account.storeName
+            }
+
+            // pod
+            if ("EUR".equals(orderHeader.currencyUom)) {
+                fact.pod = "Latin"
+            } else {
+                fact.pod = "Engish"
+            }
+
+            // brand
+            if (orderHeader.salesChannelEnumId) {
+                GenericValue brand = from("Enumeration").where(enumId: orderHeader.salesChannelEnumId).queryOne()
+                fact.brand = brand.description
+            }
+
+            // conversion of the order date
+            orderStatus = from("OrderStatus")
+                .where(orderId: orderHeader.orderId, statusId: "ORDER_APPROVED")
+                .orderBy("-statusDatetime")
+                .queryFirst()
+            if (orderStatus.statusDatetime) {
+                inMap = [:]
+                naturalKeyFields = [:]
+                inMap.dimensionEntityName = "DateDimension"
+                Date statusDatetime = new Date(orderStatus.statusDatetime.getTime())
+                naturalKeyFields.dateValue = statusDatetime
+                inMap.naturalKeyFields = naturalKeyFields
+                serviceResult = run service: "getDimensionIdFromNaturalKey", with: inMap
+                fact.orderDateDimId = serviceResult.dimensionId
+                if (!fact.orderDateDimId) {
+                    fact.orderDateDimId = "_NF_"
+                }
+            } else {
+                fact.orderDateDimId = "_NA_"
+            }
+
+            // conversion of the product id
+            if (UtilValidate.isNotEmpty(orderItem.productId)) {
+                inMap = [:]
+                naturalKeyFields = [:]
+                inMap.dimensionEntityName = "ProductDimension"
+                naturalKeyFields.productId = orderItem.productId
+                inMap.naturalKeyFields = naturalKeyFields
+                serviceResult = run service: "getDimensionIdFromNaturalKey", with: inMap
+                fact.productDimId = serviceResult.dimensionId
+                if (!fact.productDimId) {
+                    fact.productDimId = "_NF_"
+                }
+            } else {
+                fact.productDimId = "_NA_"
+            }
+
+            // conversion of the order currency
+            if (orderHeader.currencyUom) {
+                inMap = [:]
+                naturalKeyFields = [:]
+                inMap.dimensionEntityName = "CurrencyDimension"
+                naturalKeyFields.currencyId = orderHeader.currencyUom
+                inMap.naturalKeyFields = naturalKeyFields
+                serviceResult = run service: "getDimensionIdFromNaturalKey", with: inMap
+                fact.origCurrencyDimId = serviceResult.dimensionId
+                if (!fact.origCurrencyDimId) {
+                    fact.origCurrencyDimId = "_NF_"
+                }
+            } else {
+                fact.origCurrencyDimId = "_NA_"
+            }
+
+            // productCategoryId
+            GenericValue productCategoryMember = from("ProductCategoryMember").where(productId: orderItem.productId, thruDate: null).queryFirst()
+            if (productCategoryMember) {
+                fact.productCategoryId = productCategoryMember.productCategoryId
+            }
+
+            // TODO
+            fact.billToCustomerDimId = "_NA_"
+
+            fact.create()
+        }
+        /*
+         * facts handling
+         */
+        Map partyAccountingPreferencesCallMap = [:]
+
+        OrderReadHelper orderReadHelper = new OrderReadHelper(orderHeader)
+        Map billFromParty = orderReadHelper.getBillFromParty()
+        partyAccountingPreferencesCallMap.organizationPartyId = billFromParty.partyId
+        Map accountResult = run service:"getPartyAccountingPreferences", with: partyAccountingPreferencesCallMap
+        GenericValue accPref = accountResult.partyAccountingPreference
+
+        fact.quantity = (BigDecimal) orderItem.quantity
+        fact.extGrossAmount = (BigDecimal) 0
+        fact.extGrossCost = (BigDecimal) 0
+        fact.extDiscountAmount = (BigDecimal) 0
+        fact.extNetAmount = (BigDecimal) 0
+        fact.extShippingAmount = (BigDecimal) 0
+        fact.extTaxAmount = (BigDecimal) 0
+
+        fact.GS = (BigDecimal) 0
+        fact.GMS = (BigDecimal) 0
+        fact.GMP = (BigDecimal) 0
+        fact.GSS = (BigDecimal) 0
+        fact.GSC = (BigDecimal) 0
+        fact.GSP = (BigDecimal) 0
+        fact.GP = (BigDecimal) 0
+
+        fact.countOrder = (BigDecimal) 0
+
+        // extGrossAmount
+        Map convertUomCurrencyMap = [:]
+        convertUomCurrencyMap.uomId = orderHeader.currencyUom
+        convertUomCurrencyMap.uomIdTo = accPref.baseCurrencyUomId
+        if (UtilValidate.isNotEmpty(orderStatus)) {
+        convertUomCurrencyMap.nowDate = orderStatus.statusDatetime
+        }
+        Map convertResult = run service: "convertUomCurrency", with: convertUomCurrencyMap
+        BigDecimal exchangeRate = convertResult.conversionFactor
+
+        if (exchangeRate) {
+            BigDecimal unitPrice = orderItem.unitPrice * exchangeRate
+
+            fact.extGrossAmount = fact.quantity * unitPrice
+        }
+
+        // extGrossCost
+        GenericValue cost = from("SupplierProduct")
+            .where(productId: orderItem.productId, availableThruDate: null, minimumOrderQuantity: (BigDecimal) 0)
+            .queryFirst()
+        if (cost) {
+            convertUomCurrencyMap.uomId = cost.currencyUomId
+            convertUomCurrencyMap.uomIdTo = accPref.baseCurrencyUomId
+            if (orderStatus) {
+                convertUomCurrencyMap.nowDate = orderStatus.statusDatetime
+            }
+            Map grossCostResult = run service: "convertUomCurrency", with: convertUomCurrencyMap
+            exchangeRate = grossCostResult.conversionFactor
+
+            if (exchangeRate) {
+                BigDecimal costPrice = cost.lastPrice * exchangeRate
+                fact.extGrossCost = fact.quantity * costPrice
+            }
+        }
+
+        // extShippingAmount
+        for (GenericValue shipping : orderAdjustments) {
+            if ("SHIPPING_CHARGES".equals(shipping.orderAdjustmentTypeId)) {
+                fact.extShippingAmount = fact.extShippingAmount + shipping.amount
+            }
+        }
+
+        // extTaxAmount
+        for (GenericValue tax : orderAdjustments) {
+            if ("SALES_TAX".equals(tax.orderAdjustmentTypeId)) {
+                fact.extTaxAmount = fact.extTaxAmount + tax.amount
+            }
+        }
+
+        // extDiscountAmount
+        for (GenericValue discount : orderAdjustments) {
+            if ("PROMOTION_ADJUSTMENT".equals(discount.orderAdjustmentTypeId)) {
+                fact.extDiscountAmount = fact.extDiscountAmount + discount.amount
+                // product promo code
+                GenericValue productPromoCode = from("ProductPromoCode").where(productPromoId: discount.productPromoId).queryFirst()
+                if (productPromoCode) {
+                    fact.productPromoCode = productPromoCode.productPromoCodeId
+                } else {
+                    fact.productPromoCode = "Not require code"
+                }
+            }
+        }
+
+        // extNetAmount
+        fact.extNetAmount = fact.extGrossAmount - fact.extDiscountAmount
+
+        // GS
+        BigDecimal countGS = (BigDecimal) 0
+        List checkGSList = from("SalesOrderItemFact").where(orderId: orderHeader.orderId).queryList()
+        for (GenericValue checkGS : checkGSList) {
+            if (checkGS.GS) {
+                if (0 != checkGS.GS) {
+                    countGS = 1
+                }
+            }
+        }
+        if (countGS == 0) {
+            convertUomCurrencyMap.uomId = orderHeader.currencyUom
+            convertUomCurrencyMap.uomIdTo = accPref.baseCurrencyUomId
+            if (orderStatus) {
+                convertUomCurrencyMap.nowDate = orderStatus.statusDatetime
+            }
+            Map GSResult = run service: "convertUomCurrency", with: convertUomCurrencyMap
+            exchangeRate = GSResult.conversionFactor
+
+            if (exchangeRate) {
+                fact.GS = orderHeader.grandTotal * exchangeRate
+            }
+        }
+
+        // GMS
+        fact.GMS = fact.GMS + fact.extGrossAmount
+
+        // GMP
+        fact.GMP = fact.GMS - fact.extGrossCost
+
+        // GSP
+        BigDecimal countGSP = (BigDecimal) 0
+        List checkGSPList = from("SalesOrderItemFact").where(orderId: orderHeader.orderId).queryList()
+        for (GenericValue checkGSP : checkGSPList) {
+            if (checkGSP.GSP) {
+                if (checkGSP.GSP != 0) {
+                    countGSP = 1
+                }
+            }
+        }
+        if (countGSP == 0) {
+            List orderItemList = from("OrderItem").where(orderId: orderHeader.orderId).queryList()
+
+            BigDecimal warrantyPrice = (BigDecimal) 0
+            for (GenericValue warranty : orderAdjustments) {
+                if ("WARRANTY_ADJUSTMENT".equals(warranty.orderAdjustmentTypeId)) {
+                    warrantyPrice = warrantyPrice + warranty.amount
+                }
+            }
+            BigDecimal GSS = fact.extShippingAmount + warrantyPrice
+
+            convertUomCurrencyMap.uomId = orderHeader.currencyUom
+            convertUomCurrencyMap.uomIdTo = accPref.baseCurrencyUomId
+            if (orderStatus) {
+                convertUomCurrencyMap.nowDate = orderStatus.statusDatetime
+            }
+            Map GSPResult = run service: "convertUomCurrency", with: convertUomCurrencyMap
+            exchangeRate = GSPResult.conversionFactor
+
+            if (exchangeRate) {
+                GSS = GSS * exchangeRate
+            }
+            fact.GSS = GSS
+            fact.GSP = (BigDecimal) GSS
+        }
+
+        // GP
+        fact.GP = fact.GMP + fact.GSP
+
+        // countOrder
+        BigDecimal countOrder = (BigDecimal) 0
+        List checkCountOrderList = from("SalesOrderItemFact").where(orderId: orderHeader.orderId).queryList()
+        for (GenericValue checkCountOrder : checkCountOrderList) {
+            if (checkCountOrder.countOrder) {
+                if (checkCountOrder.countOrder != 0) {
+                    countOrder = 1
+                }
+            }
+        }
+        if (countOrder == 0) {
+            fact.countOrder = (BigDecimal) 1
+        }
+        fact.store()
+    }
+    return success()
+}
+
+/**
+ * Load Sales Order Data Daily
+ */
+def loadSalesOrderDataDaily() {
+    Timestamp nowTimestamp = UtilDateTime.nowTimestamp()
+    Date nowDate = new Date(nowTimestamp.getTime())
+    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd 07:00:00.000")
+    def today = sdf.format(nowDate)
+    Date yesterdayDate = new Date(nowTimestamp.getTime()-86400000)
+    def yesterday = sdf.format(yesterdayDate)
+
+    Map inMap = [:]
+    inMap.fromDate = yesterday
+    inMap.thruDate = today
+
+    run service: "importSalesOrderData", with: inMap
+    return success()
+}
+
+
+/**
+ * Import Sales Order Data
+ */
+def importSalesOrderData() {
+    Map inMap = [fromDate: parameters.fromDate, thruDate: parameters.thruDate]
+
+    Map res = run service:"loadDateDimension", with: inMap
+    if (!ServiceUtil.isSuccess(res)) {
+        return res
+    }
+    EntityCondition condition = EntityCondition.makeCondition(
+        EntityCondition.makeCondition("statusId", "ORDER_APPROVED"),
+        EntityCondition.makeCondition("statusDatetime", EntityOperator.GREATER_THAN_EQUAL_TO, parameters.fromDate),
+        EntityCondition.makeCondition("statusDatetime", EntityOperator.LESS_THAN, parameters.thruDate)
+        )
+    List orderStatusList = from("OrderStatus").where(condition).queryList()
+    for (GenericValue orderHeader : orderStatusList) {
+        inMap =[:]
+        inMap.orderId = orderHeader.orderId
+        res = run service:"loadSalesOrderFact", with: inMap
+        if (!ServiceUtil.isSuccess(res)) {
+            return res
+        }
+    }
+    return success()
+}
+
+/**
+ * Convert Uom Currency from UomConversionDated entity
+ */
+def convertUomCurrency() {
+    if (!parameters.nowDate) {
+        Timestamp now = UtilDateTime.nowTimestamp()
+        parameters.nowDate = now
+    }
+    Map result = success()
+    EntityCondition condition = EntityCondition.makeCondition(
+        EntityCondition.makeCondition("uomId", parameters.uomId),
+        EntityCondition.makeCondition("uomIdTo", parameters.uomIdTo),
+        EntityCondition.makeCondition("fromDate", EntityOperator.LESS_THAN_EQUAL_TO, parameters.nowDate),
+        EntityCondition.makeCondition("thruDate", EntityOperator.GREATER_THAN, parameters.nowDate)
+        )
+    GenericValue UomConversion = from("UomConversionDated").where(condition).orderBy("-fromDate").queryFirst()
+    if (UomConversion) {
+        result.conversionFactor = UomConversion.conversionFactor
+    } else {
+        GenericValue UomConversionLastest = from("UomConversionDated")
+            .where(uomId: parameters.uomId, uomIdTo: parameters.uomIdTo, thruDate: null)
+            .queryFirst()
+        if (UomConversionLastest) {
+            result.conversionFactor = UomConversionLastest.conversionFactor
+        }
+    }
+    return result
+}
+
+
+def loadInventoryFact() {
+    GenericValue inventory = from("InventoryItem").where(inventoryItemId: parameters.inventoryItemId).queryOne()
+    GenericValue fact = from("InventoryItemFact").where(inventoryItemId: parameters.inventoryItemId).queryOne()
+
+    Map inMap = [:]
+    Map naturalKeyFields = [:]
+    Map serviceResult
+    if (!fact) {
+        fact = makeValue("InventoryItemFact")
+        fact.inventoryItemId = inventory.inventoryItemId
+        // conversion of the inventory date
+        if (inventory?.createdStamp) {
+            inMap = [:]
+            naturalKeyFields = [:]
+            inMap.dimensionEntityName = "DateDimension"
+            Date createdStampDatetime = new Date(inventory.createdStamp.getTime())
+            naturalKeyFields.dateValue = createdStampDatetime
+            inMap.naturalKeyFields = naturalKeyFields
+            serviceResult = run service:"getDimensionIdFromNaturalKey", with: inMap
+            fact.inventoryDateDimId = serviceResult.dimensionId
+            if (!fact.inventoryDateDimId) {
+                fact.inventoryDateDimId = "_NF_"
+            }
+        } else {
+            fact.inventoryDateDimId = "_NA_"
+        }
+        // conversion of the productId
+        if (inventory?.productId) {
+            inMap = [:]
+            naturalKeyFields = [:]
+            inMap.dimensionEntityName = "ProductDimension"
+            naturalKeyFields.productId = inventory.productId
+            inMap.naturalKeyFields = naturalKeyFields
+            serviceResult = run service:"getDimensionIdFromNaturalKey", with: inMap
+            fact.productDimId = serviceResult.dimensionId
+            if (!fact.productDimId) {
+                fact.productDimId = "_NF_"
+            }
+        } else {
+            fact.productDimId = "_NA_"
+        }
+        // conversion of the order currency
+        if (inventory?.currencyUomId) {
+            inMap =[:]
+            naturalKeyFields = [:]
+            inMap.dimensionEntityName = "CurrencyDimension"
+            naturalKeyFields.currencyId = inventory.currencyUomId
+            inMap.naturalKeyFields = naturalKeyFields
+            serviceResult = run service:"getDimensionIdFromNaturalKey", with: inMap
+            fact.origCurrencyDimId = serviceResult.dimensionId
+            if (!fact.origCurrencyDimId) {
+                fact.origCurrencyDimId = "_NF_"
+            }
+        } else {
+            fact.origCurrencyDimId = "_NA_"
+        }
+        fact.create()
+    }
+
+    fact.facilityId = inventory.facilityId
+    fact.inventoryItemId = inventory.inventoryItemId
+    fact.quantityOnHandTotal = inventory.quantityOnHandTotal
+    fact.availableToPromiseTotal = inventory.availableToPromiseTotal
+    fact.unitCost = inventory.unitCost
+
+    // calculate sold out amount
+    fact.soldoutAmount = inventory.quantityOnHandTotal - inventory.availableToPromiseTotal
+
+    fact.store()
+    return success()
+}
diff --git a/bi/minilang/FactServices.xml b/bi/minilang/FactServices.xml
deleted file mode 100644
index 13a72be..0000000
--- a/bi/minilang/FactServices.xml
+++ /dev/null
@@ -1,761 +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="loadSalesInvoiceFact" short-description="">
-        <entity-one entity-name="Invoice" value-field="invoice"/>
-        <if-empty field="invoice">
-            <add-error>
-                <fail-property resource="AccountingUiLabels" property="AccountingInvoiceDoesNotExists"/>
-            </add-error>
-        </if-empty>
-        <check-errors/>
-        <if-compare field="invoice.invoiceTypeId" operator="equals" value="SALES_INVOICE">
-            <set field="andConditions.invoiceItemTypeId" value="INV_FPROD_ITEM"/>
-            <get-related relation-name="InvoiceItem" value-field="invoice" list="invoiceItems" map="andConditions"/>
-            <iterate list="invoiceItems" entry="invoiceItem">
-                <clear-field field="inMap"/>
-                <set field="inMap.invoice" from-field="invoice"/>
-                <set field="inMap.invoiceItem" from-field="invoiceItem"/>
-                <call-service service-name="loadSalesInvoiceItemFact" in-map-name="inMap"/>
-            </iterate>
-        </if-compare>
-    </simple-method>
-    <simple-method method-name="loadSalesInvoiceItemFact" short-description="">
-        <set field="invoice" from-field="parameters.invoice"/>
-        <set field="invoiceItem" from-field="parameters.invoiceItem"/>
-        <if-empty field="invoice">
-            <entity-one entity-name="Invoice" value-field="invoice"/>
-        </if-empty>
-        <if-empty field="invoiceItem">
-            <entity-one entity-name="InvoiceItem" value-field="invoiceItem"/>
-        </if-empty>
-        <if-empty field="invoice">
-            <add-error>
-                <fail-property resource="AccountingUiLabels" property="AccountingInvoiceDoesNotExists"/>
-            </add-error>
-        </if-empty>
-        <if-empty field="invoiceItem">
-            <add-error>
-                <fail-property resource="AccountingUiLabels" property="AccountingInvoiceItemDoesNotExists"/>
-            </add-error>
-        </if-empty>
-        <check-errors/>
-
-        <if-compare field="invoice.invoiceTypeId" operator="equals" value="SALES_INVOICE">
-            <entity-one entity-name="SalesInvoiceItemFact" value-field="fact" auto-field-map="false">
-                <field-map field-name="invoiceId" from-field="invoiceItem.invoiceId"/>
-                <field-map field-name="invoiceItemSeqId" from-field="invoiceItem.invoiceItemSeqId"/>
-            </entity-one>
-            <!-- key handling -->
-            <if-empty field="fact">
-                <make-value entity-name="SalesInvoiceItemFact" value-field="fact"/>
-                <set field="fact.invoiceId" from-field="invoice.invoiceId"/>
-                <set field="fact.invoiceItemSeqId" from-field="invoiceItem.invoiceItemSeqId"/>
-                <!-- conversion of the invoice date -->
-                <if-not-empty field="invoice.invoiceDate">
-                    <clear-field field="inMap"/>
-                    <set field="inMap.dimensionEntityName" value="DateDimension"/>
-                    <set field="inMap.naturalKeyFields.dateValue" from-field="invoice.invoiceDate" type="Date"/>
-                    <call-service service-name="getDimensionIdFromNaturalKey" in-map-name="inMap">
-                        <result-to-field result-name="dimensionId" field="fact.invoiceDateDimId"/>
-                    </call-service>
-                    <if-empty field="fact.invoiceDateDimId">
-                        <set field="fact.invoiceDateDimId" value="_NF_"/>
-                    </if-empty>
-                <else>
-                    <set field="fact.invoiceDateDimId" value="_NA_"/>
-                </else>
-                </if-not-empty>
-                <!-- conversion of the product id -->
-                <if-not-empty field="invoiceItem.productId">
-                    <clear-field field="inMap"/>
-                    <set field="inMap.dimensionEntityName" value="ProductDimension"/>
-                    <set field="inMap.naturalKeyFields.productId" from-field="invoiceItem.productId"/>
-                    <call-service service-name="getDimensionIdFromNaturalKey" in-map-name="inMap">
-                        <result-to-field result-name="dimensionId" field="fact.productDimId"/>
-                    </call-service>
-                    <if-empty field="fact.productDimId">
-                        <set field="fact.productDimId" value="_NF_"/>
-                    </if-empty>
-                <else>
-                    <set field="fact.productDimId" value="_NA_"/>
-                </else>
-                </if-not-empty>
-                <!-- conversion of the invoice currency -->
-                <if-not-empty field="invoice.currencyUomId">
-                    <clear-field field="inMap"/>
-                    <set field="inMap.dimensionEntityName" value="CurrencyDimension"/>
-                    <set field="inMap.naturalKeyFields.currencyId" from-field="invoice.currencyUomId"/>
-                    <call-service service-name="getDimensionIdFromNaturalKey" in-map-name="inMap">
-                        <result-to-field result-name="dimensionId" field="fact.origCurrencyDimId"/>
-                    </call-service>
-                    <if-empty field="fact.origCurrencyDimId">
-                        <set field="fact.origCurrencyDimId" value="_NF_"/>
-                    </if-empty>
-                <else>
-                    <set field="fact.origCurrencyDimId" value="_NA_"/>
-                </else>
-                </if-not-empty>
-                <!-- TODO -->
-                <set field="fact.orderId" value="_NA_"/>
-                <set field="fact.billToCustomerDimId" value="_NA_"/>
-                <create-value value-field="fact"/>
-            </if-empty>
-            <!-- =============== -->
-            <!-- facts handling  -->
-            <!-- =============== -->
-            <set field="fact.quantity" from-field="invoiceItem.quantity" type="BigDecimal"/>
-            <set field="fact.extGrossAmount" value="0.0" type="BigDecimal"/>
-            <set field="fact.extDiscountAmount" value="0.0" type="BigDecimal"/>
-            <set field="fact.extTaxAmount" value="0.0" type="BigDecimal"/>
-            <set field="fact.extNetAmount" value="0.0" type="BigDecimal"/>
-            <calculate field="fact.extGrossAmount">
-                <calcop field="invoiceItem.quantity" operator="multiply">
-                    <calcop field="invoiceItem.amount" operator="get"/>
-                </calcop>
-            </calculate>
-            <!-- taxes -->
-            <clear-field field="andConditions"/>
-            <set field="andConditions.invoiceItemTypeId" value="ITM_SALES_TAX"/>
-            <get-related relation-name="ChildrenInvoiceItem" value-field="invoiceItem" list="taxes" map="andConditions"/>
-            <iterate list="taxes" entry="tax">
-                <calculate field="fact.extTaxAmount">
-                    <calcop field="fact.extTaxAmount" operator="add">
-                        <calcop field="tax.amount" operator="get"/>
-                    </calcop>
-                </calculate>
-            </iterate>
-            <!-- discounts -->
-            <clear-field field="andConditions"/>
-            <set field="andConditions.invoiceItemTypeId" value="ITM_PROMOTION_ADJ"/>
-            <get-related relation-name="ChildrenInvoiceItem" value-field="invoiceItem" list="discounts" map="andConditions"/>
-            <iterate list="discounts" entry="discount">
-                <calculate field="fact.extDiscountAmount" type="BigDecimal">
-                    <calcop field="fact.extDiscountAmount" operator="add">
-                        <calcop field="discount.amount" operator="negative"/>
-                    </calcop>
-                </calculate>
-            </iterate>
-
-            <calculate field="fact.extNetAmount">
-                <calcop field="fact.extGrossAmount" operator="subtract">
-                    <calcop field="fact.extDiscountAmount" operator="get"/>
-                </calcop>
-            </calculate>
-            <!-- TODO: prorate invoice header discounts and shipping charges -->
-            <!-- TODO: costs -->
-            <set field="fact.extManFixedCost" value="0.0" type="BigDecimal"/>
-            <set field="fact.extManVarCost" value="0.0" type="BigDecimal"/>
-            <set field="fact.extStorageCost" value="0.0" type="BigDecimal"/>
-            <set field="fact.extDistributionCost" value="0.0" type="BigDecimal"/>
-
-            <calculate field="fact.contributionAmount">
-                <calcop field="fact.extNetAmount" operator="subtract">
-                    <calcop field="fact.extManFixedCost" operator="get"/>
-                    <calcop field="fact.extManVarCost" operator="get"/>
-                    <calcop field="fact.extStorageCost" operator="get"/>
-                    <calcop field="fact.extDistributionCost" operator="get"/>
-                </calcop>
-            </calculate>
-
-            <store-value value-field="fact"/>
-        </if-compare>
-    </simple-method>
-
-    <simple-method method-name="loadSalesOrderFact" short-description="">
-        <entity-one entity-name="OrderHeader" value-field="orderHeader"/>
-        <if-empty field="orderHeader">
-            <add-error>
-                <fail-property resource="OrderErrorUiLabels" property="OrderOrderIdDoesNotExists"/>
-            </add-error>
-        </if-empty>
-        <check-errors/>
-        <if-compare field="orderHeader.orderTypeId" operator="equals" value="SALES_ORDER">
-            <if-compare field="orderHeader.statusId" operator="equals" value="ORDER_APPROVED">
-                <entity-condition entity-name="OrderItem" list="orderItems">
-                    <condition-list combine="and">
-                        <condition-expr field-name="orderId" operator="equals" from-field="orderHeader.orderId"/>
-                        <!--<condition-expr field-name="productId" operator="not-like" value="M00%"/>-->
-                        <condition-expr field-name="orderItemTypeId" operator="equals" value="PRODUCT_ORDER_ITEM"/>
-                        <!--<condition-expr field-name="statusId" operator="equals" value="ITEM_APPROVED"/>-->
-                    </condition-list>
-                </entity-condition>
-
-                <!--<set field="andConditions.orderItemTypeId" value="PRODUCT_ORDER_ITEM"/>
-                <get-related relation-name="OrderItem" value-field="orderHeader" list="orderItems" map="andConditions"/>-->
-
-                <iterate list="orderItems" entry="orderItem">
-                    <clear-field field="inMap"/>
-                    <set field="inMap.orderHeader" from-field="orderHeader"/>
-                    <set field="inMap.orderItem" from-field="orderItem"/>
-                    <set field="inMap.orderAdjustment" from-field="orderAdjustment"/>
-                    <call-service service-name="loadSalesOrderItemFact" in-map-name="inMap"/>
-                </iterate>
-            </if-compare>
-        </if-compare>
-    </simple-method>
-
-    <simple-method method-name="loadSalesOrderItemFact" short-description="">
-        <set field="orderHeader" from-field="parameters.orderHeader"/>
-        <set field="orderItem" from-field="parameters.orderItem"/>
-        <set field="orderAdjustment" from-field="parameters.orderAdjustment"/>
-        <if-empty field="orderHeader">
-            <entity-one entity-name="OrderHeader" value-field="orderHeader"/>
-        </if-empty>
-        <if-empty field="orderItem">
-            <entity-one entity-name="OrderItem" value-field="orderItem"/>
-        </if-empty>
-        <if-empty field="orderAdjustment">
-            <entity-and entity-name="OrderAdjustment" list="orderAdjustments">
-                <field-map field-name="orderId" from-field="orderItem.orderId"/>
-            </entity-and>
-        </if-empty>
-        <if-empty field="orderHeader">
-            <add-error>
-                <fail-property resource="OrderErrorUiLabels" property="OrderOrderIdDoesNotExists"/>
-            </add-error>
-        </if-empty>
-        <if-empty field="orderItem">
-            <add-error>
-                <fail-property resource="OrderErrorUiLabels" property="OrderOrderItemIdDoesNotExists"/>
-            </add-error>
-        </if-empty>
-        <check-errors/>
-
-        <if-compare field="orderHeader.statusId" operator="equals" value="ORDER_APPROVED">
-            <entity-one entity-name="SalesOrderItemFact" value-field="fact" auto-field-map="false">
-                <field-map field-name="orderId" from-field="orderItem.orderId"/>
-                <field-map field-name="orderItemSeqId" from-field="orderItem.orderItemSeqId"/>
-            </entity-one>
-            <!-- key handling -->
-            <if-empty field="fact">
-                <make-value entity-name="SalesOrderItemFact" value-field="fact"/>
-                <set field="fact.orderId" from-field="orderHeader.orderId"/>
-                <set field="fact.orderItemSeqId" from-field="orderItem.orderItemSeqId"/>
-                <set field="fact.productStoreId" from-field="orderHeader.productStoreId"/>
-                <set field="fact.salesChannelEnumId" from-field="orderHeader.salesChannelEnumId"/>
-                <set field="fact.statusId" from-field="orderItem.statusId"/>
-
-                <!-- account -->
-                <if-not-empty field="orderHeader.productStoreId">
-                    <entity-one entity-name="ProductStore" value-field="account">
-                        <field-map field-name="productStoreId" from-field="orderHeader.productStoreId"/>
-                    </entity-one>
-                    <set field="fact.account" from-field="account.storeName"/>
-                </if-not-empty>
-
-                <!-- pod -->
-                <if-compare field="orderHeader.currencyUom" operator="equals" value="EUR">
-                    <set field="fact.pod" value="Latin"/>
-                <else>
-                    <set field="fact.pod" value="English"/>
-                </else>
-                </if-compare>
-
-                <!-- brand -->
-                <if-not-empty field="orderHeader.salesChannelEnumId">
-                    <entity-one entity-name="Enumeration" value-field="brand">
-                        <field-map field-name="enumId" from-field="orderHeader.salesChannelEnumId"/>
-                    </entity-one>
-                    <set field="fact.brand" from-field="brand.description"/>
-                </if-not-empty>
-
-                <!-- conversion of the order date -->
-                <entity-condition entity-name="OrderStatus" list="orderStatusList">
-                    <condition-list combine="and">
-                        <condition-expr field-name="orderId" from-field="orderHeader.orderId"/>
-                        <condition-expr field-name="statusId" value="ORDER_APPROVED"/>
-                    </condition-list>
-                    <order-by field-name="-statusDatetime"/>
-                </entity-condition>
-                <first-from-list list="orderStatusList" entry="orderStatus"/>
-                <if-not-empty field="orderStatus.statusDatetime">
-                    <clear-field field="inMap"/>
-                    <set field="inMap.dimensionEntityName" value="DateDimension"/>
-                    <set field="inMap.naturalKeyFields.dateValue" from-field="orderStatus.statusDatetime" type="Date"/>
-                    <call-service service-name="getDimensionIdFromNaturalKey" in-map-name="inMap">
-                        <result-to-field result-name="dimensionId" field="fact.orderDateDimId"/>
-                    </call-service>
-                    <if-empty field="fact.orderDateDimId">
-                        <set field="fact.orderDateDimId" value="_NF_"/>
-                    </if-empty>
-                <else>
-                    <set field="fact.orderDateDimId" value="_NA_"/>
-                </else>
-                </if-not-empty>
-
-                <!-- conversion of the product id -->
-                <if-not-empty field="orderItem.productId">
-                    <clear-field field="inMap"/>
-                    <set field="inMap.dimensionEntityName" value="ProductDimension"/>
-                    <set field="inMap.naturalKeyFields.productId" from-field="orderItem.productId"/>
-                    <call-service service-name="getDimensionIdFromNaturalKey" in-map-name="inMap">
-                        <result-to-field result-name="dimensionId" field="fact.productDimId"/>
-                    </call-service>
-                    <if-empty field="fact.productDimId">
-                        <set field="fact.productDimId" value="_NF_"/>
-                    </if-empty>
-                <else>
-                    <set field="fact.productDimId" value="_NA_"/>
-                </else>
-                </if-not-empty>
-
-                <!-- conversion of the order currency -->
-                <if-not-empty field="orderHeader.currencyUom">
-                    <clear-field field="inMap"/>
-                    <set field="inMap.dimensionEntityName" value="CurrencyDimension"/>
-                    <set field="inMap.naturalKeyFields.currencyId" from-field="orderHeader.currencyUom"/>
-                    <call-service service-name="getDimensionIdFromNaturalKey" in-map-name="inMap">
-                        <result-to-field result-name="dimensionId" field="fact.origCurrencyDimId"/>
-                    </call-service>
-                    <if-empty field="fact.origCurrencyDimId">
-                        <set field="fact.origCurrencyDimId" value="_NF_"/>
-                    </if-empty>
-                <else>
-                    <set field="fact.origCurrencyDimId" value="_NA_"/>
-                </else>
-                </if-not-empty>
-
-                <!-- productCategoryId -->
-                <entity-and entity-name="ProductCategoryMember" list="productCategoryMembers">
-                    <field-map field-name="productId" from-field="orderItem.productId"/>
-                    <field-map field-name="thruDate" from-field="nullField"/>
-                </entity-and>
-                <if-not-empty field="productCategoryMembers">
-                    <first-from-list list="productCategoryMembers" entry="productCategoryMember"/>
-                    <set field="fact.productCategoryId" from-field="productCategoryMember.productCategoryId"/>
-                </if-not-empty>
-
-                <!-- TODO -->
-                <set field="fact.billToCustomerDimId" value="_NA_"/>
-
-                <create-value value-field="fact"/>
-            </if-empty>
-            <!-- =============== -->
-            <!-- facts handling  -->
-            <!-- =============== -->
-            <script>groovy:
-                import org.apache.ofbiz.order.order.OrderReadHelper
-
-                orderReadHelper = new OrderReadHelper(orderHeader)
-                context.billFromParty = orderReadHelper.getBillFromParty()
-            </script>
-            <set field="partyAccountingPreferencesCallMap.organizationPartyId" from-field="billFromParty.partyId"/>
-            <call-service service-name="getPartyAccountingPreferences" in-map-name="partyAccountingPreferencesCallMap">
-                <result-to-field result-name="partyAccountingPreference" field="accPref"/>
-            </call-service>
-            <set field="fact.quantity" from-field="orderItem.quantity" type="BigDecimal"/>
-            <set field="fact.extGrossAmount" value="0" type="BigDecimal"/>
-            <set field="fact.extGrossCost" value="0" type="BigDecimal"/>
-            <set field="fact.extDiscountAmount" value="0" type="BigDecimal"/>
-            <set field="fact.extNetAmount" value="0" type="BigDecimal"/>
-            <set field="fact.extShippingAmount" value="0" type="BigDecimal"/>
-            <set field="fact.extTaxAmount" value="0" type="BigDecimal"/>
-
-            <set field="fact.GS" value="0" type="BigDecimal"/>
-            <set field="fact.GMS" value="0" type="BigDecimal"/>
-            <set field="fact.GMP" value="0" type="BigDecimal"/>
-            <set field="fact.GSS" value="0" type="BigDecimal"/>
-            <set field="fact.GSC" value="0" type="BigDecimal"/>
-            <set field="fact.GSP" value="0" type="BigDecimal"/>
-            <set field="fact.GP" value="0" type="BigDecimal"/>
-
-            <set field="fact.countOrder" value="0" type="BigDecimal"/>
-
-            <!-- extGrossAmount -->
-            <set field="convertUomCurrencyMap.uomId" from-field="orderHeader.currencyUom"/>
-            <set field="convertUomCurrencyMap.uomIdTo" from-field="accPref.baseCurrencyUomId"/>
-            <set field="convertUomCurrencyMap.nowDate" from-field="orderStatus.statusDatetime"/>
-            <call-service service-name="convertUomCurrency" in-map-name="convertUomCurrencyMap">
-                <result-to-field result-name="conversionFactor" field="exchangeRate"/>
-            </call-service>
-
-            <if-not-empty field="exchangeRate">
-                <calculate field="unitPrice">
-                    <calcop field="orderItem.unitPrice" operator="multiply">
-                        <calcop field="exchangeRate" operator="get"/>
-                    </calcop>
-                </calculate>
-
-                <calculate field="fact.extGrossAmount">
-                    <calcop field="fact.quantity" operator="multiply">
-                        <calcop field="unitPrice" operator="get"/>
-                    </calcop>
-                </calculate>
-            </if-not-empty>
-
-            <!-- extGrossCost -->
-            <entity-condition entity-name="SupplierProduct" list="costs">
-                <condition-list combine="and">
-                    <condition-expr field-name="productId" operator="equals" from-field="orderItem.productId"/>
-                    <condition-expr field-name="availableThruDate" operator="equals" from-field="nullField"/>
-                    <condition-expr field-name="minimumOrderQuantity" operator="equals" value="0"/>
-                </condition-list>
-            </entity-condition>
-            <if-not-empty field="costs">
-                <first-from-list list="costs" entry="cost"/>
-
-                <set field="convertUomCurrencyMap.uomId" from-field="cost.currencyUomId"/>
-                <set field="convertUomCurrencyMap.uomIdTo" from-field="accPref.baseCurrencyUomId"/>
-                <set field="convertUomCurrencyMap.nowDate" from-field="orderStatus.statusDatetime"/>
-                <call-service service-name="convertUomCurrency" in-map-name="convertUomCurrencyMap">
-                    <result-to-field result-name="conversionFactor" field="exchangeRate"/>
-                </call-service>
-
-                <if-not-empty field="exchangeRate">
-                    <calculate field="costPrice">
-                        <calcop field="cost.lastPrice" operator="multiply">
-                            <calcop field="exchangeRate" operator="get"/>
-                        </calcop>
-                    </calculate>
-
-                    <calculate field="fact.extGrossCost">
-                        <calcop field="fact.quantity" operator="multiply">
-                            <calcop field="costPrice" operator="get"/>
-                        </calcop>
-                    </calculate>
-                </if-not-empty>
-            </if-not-empty>
-
-            <!-- extShippingAmount -->
-            <iterate list="orderAdjustments" entry="shipping">
-                <if-compare field="shipping.orderAdjustmentTypeId" operator="equals" value="SHIPPING_CHARGES">
-                    <calculate field="fact.extShippingAmount">
-                        <calcop field="fact.extShippingAmount" operator="add">
-                            <calcop field="shipping.amount" operator="get"/>
-                        </calcop>
-                    </calculate>
-                </if-compare>
-            </iterate>
-
-            <!-- extTaxAmount -->
-            <iterate list="orderAdjustments" entry="tax">
-                <if-compare field="tax.orderAdjustmentTypeId" operator="equals" value="SALES_TAX">
-                    <calculate field="fact.extTaxAmount">
-                        <calcop field="fact.extTaxAmount" operator="add">
-                            <calcop field="tax.amount" operator="get"/>
-                        </calcop>
-                    </calculate>
-                </if-compare>
-            </iterate>
-
-            <!-- extDiscountAmount -->
-            <iterate list="orderAdjustments" entry="discount">
-                <if-compare field="discount.orderAdjustmentTypeId" operator="equals" value="PROMOTION_ADJUSTMENT">
-                    <calculate field="fact.extDiscountAmount">
-                        <calcop field="fact.extDiscountAmount" operator="add">
-                            <calcop field="discount.amount" operator="get"/>
-                        </calcop>
-                    </calculate>
-                    <!-- product promo code -->
-                    <entity-and entity-name="ProductPromoCode" list="productPromo">
-                        <field-map field-name="productPromoId" from-field="discount.productPromoId"/>
-                    </entity-and>
-                    <first-from-list list="productPromo" entry="productPromoCode"/>
-                    <if-not-empty field="productPromoCode">
-                        <set field="fact.productPromoCode" from-field="productPromoCode.productPromoCodeId"/>
-                        <else>
-                            <set field="fact.productPromoCode" value="Not require code"/>
-                        </else>
-                    </if-not-empty>
-                </if-compare>
-            </iterate>
-
-            <!-- extNetAmount -->
-            <calculate field="fact.extNetAmount">
-                <calcop field="fact.extGrossAmount" operator="subtract">
-                    <calcop field="fact.extDiscountAmount" operator="get"/>
-                </calcop>
-            </calculate>
-
-            <!-- GS -->
-            <set field="countGS" value="0"/>
-            <entity-and entity-name="SalesOrderItemFact" list="checkGSList">
-                <field-map field-name="orderId" from-field="orderHeader.orderId"/>
-            </entity-and>
-            <iterate list="checkGSList" entry="checkGS">
-                <if-not-empty field="checkGS.GS">
-                    <if-compare field="checkGS.GS" operator="not-equals" value="0">
-                        <set field="countGS" value="1"/>
-                    </if-compare>
-                </if-not-empty>
-            </iterate>
-            <if-compare field="countGS" operator="equals" value="0">
-                <set field="convertUomCurrencyMap.uomId" from-field="orderHeader.currencyUom"/>
-                <set field="convertUomCurrencyMap.uomIdTo" from-field="accPref.baseCurrencyUomId"/>
-                <set field="convertUomCurrencyMap.nowDate" from-field="orderStatus.statusDatetime"/>
-                <call-service service-name="convertUomCurrency" in-map-name="convertUomCurrencyMap">
-                    <result-to-field result-name="conversionFactor" field="exchangeRate"/>
-                </call-service>
-
-                <if-not-empty field="exchangeRate">
-                    <calculate field="fact.GS">
-                        <calcop field="orderHeader.grandTotal" operator="multiply">
-                            <calcop field="exchangeRate" operator="get"/>
-                        </calcop>
-                    </calculate>
-                </if-not-empty>
-            </if-compare>
-
-            <!-- GMS -->
-            <calculate field="fact.GMS">
-                <calcop field="fact.GMS" operator="add">
-                    <calcop field="fact.extGrossAmount" operator="get"/>
-                </calcop>
-            </calculate>
-
-            <!-- GMP -->
-            <calculate field="fact.GMP">
-                <calcop field="fact.GMS" operator="subtract">
-                    <calcop field="fact.extGrossCost" operator="get"/>
-                </calcop>
-            </calculate>
-
-            <!-- GSP -->
-            <set field="countGSP" value="0"/>
-            <entity-and entity-name="SalesOrderItemFact" list="checkGSPList">
-                <field-map field-name="orderId" from-field="orderHeader.orderId"/>
-            </entity-and>
-            <iterate list="checkGSPList" entry="checkGSP">
-                <if-not-empty field="checkGSP.GSP">
-                    <if-compare field="checkGSP.GSP" operator="not-equals" value="0">
-                        <set field="countGSP" value="1"/>
-                    </if-compare>
-                </if-not-empty>
-            </iterate>
-            <if-compare field="countGSP" operator="equals" value="0">
-                <entity-and entity-name="OrderItem" list="orderItemList">
-                    <field-map field-name="orderId" from-field="orderHeader.orderId"/>
-                </entity-and>
-
-                <set field="warrantyPrice" value="0" type="BigDecimal"/>
-                <iterate list="orderAdjustments" entry="warranty">
-                    <if-compare field="warranty.orderAdjustmentTypeId" operator="equals" value="WARRANTY_ADJUSTMENT">
-                        <calculate field="warrantyPrice">
-                            <calcop field="warrantyPrice" operator="add">
-                                <calcop field="warranty.amount" operator="get"/>
-                            </calcop>
-                        </calculate>
-                    </if-compare>
-                </iterate>
-                <calculate field="GSS">
-                    <calcop field="fact.extShippingAmount" operator="add">
-                        <calcop field="warrantyPrice" operator="get"/>
-                    </calcop>
-                </calculate>
-
-                <set field="convertUomCurrencyMap.uomId" from-field="orderHeader.currencyUom"/>
-                <set field="convertUomCurrencyMap.uomIdTo" from-field="accPref.baseCurrencyUomId"/>
-                <set field="convertUomCurrencyMap.nowDate" from-field="orderStatus.statusDatetime"/>
-                <call-service service-name="convertUomCurrency" in-map-name="convertUomCurrencyMap">
-                    <result-to-field result-name="conversionFactor" field="exchangeRate"/>
-                </call-service>
-
-                <if-not-empty field="exchangeRate">
-                    <calculate field="GSS">
-                        <calcop field="GSS" operator="multiply">
-                            <calcop field="exchangeRate" operator="get"/>
-                        </calcop>
-                    </calculate>
-                </if-not-empty>
-                <set field="fact.GSS" from-field="GSS"/>
-
-                <set field="fact.GSP" from-field="GSS" type="BigDecimal"/>
-            </if-compare>
-
-            <!-- GP -->
-            <calculate field="fact.GP">
-                <calcop field="fact.GMP" operator="add">
-                    <calcop field="fact.GSP" operator="get"/>
-                </calcop>
-            </calculate>
-
-            <!-- countOrder -->
-            <set field="countOrder" value="0"/>
-            <entity-and entity-name="SalesOrderItemFact" list="checkCountOrderList">
-                <field-map field-name="orderId" from-field="orderHeader.orderId"/>
-            </entity-and>
-            <iterate list="checkCountOrderList" entry="checkCountOrder">
-                <if-not-empty field="checkCountOrder.countOrder">
-                    <if-compare field="checkCountOrder.countOrder" operator="not-equals" value="0">
-                        <set field="countOrder" value="1"/>
-                    </if-compare>
-                </if-not-empty>
-            </iterate>
-            <if-compare field="countOrder" operator="equals" value="0">
-                <set field="fact.countOrder" value="1" type="BigDecimal"/>
-            </if-compare>
-
-            <store-value value-field="fact"/>
-        </if-compare>
-    </simple-method>
-
-    <simple-method method-name="loadSalesOrderDataDaily" short-description="Load Sales Order Data Daily">
-        <now-date-to-env field="nowDate"/>
-        <set field="yesterday" value="${groovy:
-            import java.text.SimpleDateFormat;
-            def sdf = new SimpleDateFormat(&quot;yyyy-MM-dd 07:00:00.000&quot;);
-            def yesterday = sdf.format(nowDate-1);
-            return yesterday;
-        }" type="Timestamp"/>
-        <set field="today" value="${groovy:
-            import java.text.SimpleDateFormat;
-            def sdf = new SimpleDateFormat(&quot;yyyy-MM-dd 07:00:00.000&quot;);
-            def today = sdf.format(nowDate);
-            return today;
-        }" type="Timestamp"/>
-
-         <set field="inMap.fromDate" from-field="yesterday"/>
-         <set field="inMap.thruDate" from-field="today"/>
-         <call-service service-name="importSalesOrderData" in-map-name="inMap"></call-service>
-    </simple-method>
-
-    <simple-method method-name="importSalesOrderData" short-description="Import Sales Order Data">
-        <set-service-fields service-name="loadDateDimension" map="parameters" to-map="inMap"/>
-        <set field="inMap.fromDate" from-field="parameters.fromDate"/>
-        <set field="inMap.thruDate" from-field="parameters.thruDate"/>
-        <call-service service-name="loadDateDimension" in-map-name="inMap"/>
-        <check-errors/>
-
-        <entity-condition entity-name="OrderStatus" list="orderStatusList">
-            <condition-list combine="and">
-                <condition-expr field-name="statusId" value="ORDER_APPROVED"/>
-                <condition-expr field-name="statusDatetime" operator="greater-equals" from-field="parameters.fromDate"/>
-                <condition-expr field-name="statusDatetime" operator="less" from-field="parameters.thruDate"/>
-            </condition-list>
-        </entity-condition>
-        <iterate list="orderStatusList" entry="orderHeader">
-            <clear-field field="inMap"/>
-            <set field="inMap.orderId" from-field="orderHeader.orderId"/>
-            <call-service service-name="loadSalesOrderFact" in-map-name="inMap"></call-service>
-            <check-errors/>
-        </iterate>
-    </simple-method>
-
-    <simple-method method-name="convertUomCurrency" short-description="Convert Uom Currency from UomConversionDated entity">
-        <if-empty field="parameters.nowDate">
-            <now field="now"/>
-            <set field="parameters.nowDate" from-field="now"/>
-        </if-empty>
-        <entity-condition entity-name="UomConversionDated" list="UomConversionDatedList">
-            <condition-list combine="and">
-                <condition-expr field-name="uomId" operator="equals" from-field="parameters.uomId"/>
-                <condition-expr field-name="uomIdTo" operator="equals" from-field="parameters.uomIdTo"/>
-                <condition-expr field-name="fromDate" operator="less-equals" from-field="parameters.nowDate"/>
-                <condition-expr field-name="thruDate" operator="greater" from-field="parameters.nowDate"/>
-            </condition-list>
-            <order-by field-name="-fromDate"/>
-        </entity-condition>
-        <if-not-empty field="UomConversionDatedList">
-            <first-from-list list="UomConversionDatedList" entry="UomConversion"/>
-            <field-to-result field="UomConversion.conversionFactor" result-name="conversionFactor"/>
-        <else>
-            <entity-condition entity-name="UomConversionDated" list="UomConversionDatedLastestList">
-                <condition-list combine="and">
-                    <condition-expr field-name="uomId" operator="equals" from-field="parameters.uomId"/>
-                    <condition-expr field-name="uomIdTo" operator="equals" from-field="parameters.uomIdTo"/>
-                    <condition-expr field-name="thruDate" operator="equals" from-field="nullField"/>
-                </condition-list>
-            </entity-condition>
-            <if-not-empty field="UomConversionDatedLastestList">
-                <first-from-list list="UomConversionDatedLastestList" entry="UomConversionLastest"/>
-                <field-to-result field="UomConversionLastest.conversionFactor" result-name="conversionFactor"/>
-            </if-not-empty>
-        </else>
-        </if-not-empty>
-    </simple-method>
-
-    <simple-method method-name="loadInventoryFact" short-description="">
-        <entity-one entity-name="InventoryItem" value-field="inventory">
-            <field-map field-name="inventoryItemId" from-field="parameters.inventoryItemId"/>
-        </entity-one>
-        <entity-one entity-name="InventoryItemFact" value-field="fact">
-            <field-map field-name="inventoryItemId" from-field="parameters.inventoryItemId"/>
-        </entity-one>
-        <if-empty field="fact">
-            <make-value entity-name="InventoryItemFact" value-field="fact"/>
-            <set field="fact.inventoryItemId" from-field="inventory.inventoryItemId"/>
-            <!-- conversion of the inventory date -->
-            <if-not-empty field="inventory.createdStamp">
-                <clear-field field="inMap"/>
-                <set field="inMap.dimensionEntityName" value="DateDimension"/>
-                <set field="inMap.naturalKeyFields.dateValue" from-field="inventory.createdStamp" type="Date"/>
-                <call-service service-name="getDimensionIdFromNaturalKey" in-map-name="inMap">
-                    <result-to-field result-name="dimensionId" field="fact.inventoryDateDimId"/>
-                </call-service>
-                <if-empty field="fact.inventoryDateDimId">
-                    <set field="fact.inventoryDateDimId" value="_NF_"/>
-                </if-empty>
-                <else>
-                    <set field="fact.inventoryDateDimId" value="_NA_"/>
-                </else>
-            </if-not-empty>
-            <!-- conversion of the product id -->
-            <if-not-empty field="inventory.productId">
-                <clear-field field="inMap"/>
-                <set field="inMap.dimensionEntityName" value="ProductDimension"/>
-                <set field="inMap.naturalKeyFields.productId" from-field="inventory.productId"/>
-                <call-service service-name="getDimensionIdFromNaturalKey" in-map-name="inMap">
-                    <result-to-field result-name="dimensionId" field="fact.productDimId"/>
-                </call-service>
-                <if-empty field="fact.productDimId">
-                    <set field="fact.productDimId" value="_NF_"/>
-                </if-empty>
-                <else>
-                    <set field="fact.productDimId" value="_NA_"/>
-                </else>
-            </if-not-empty>
-            <!-- conversion of the order currency -->
-            <if-not-empty field="inventory.currencyUomId">
-                <clear-field field="inMap"/>
-                <set field="inMap.dimensionEntityName" value="CurrencyDimension"/>
-                <set field="inMap.naturalKeyFields.currencyId" from-field="inventory.currencyUomId"/>
-                <call-service service-name="getDimensionIdFromNaturalKey" in-map-name="inMap">
-                    <result-to-field result-name="dimensionId" field="fact.origCurrencyDimId"/>
-                </call-service>
-                <if-empty field="fact.origCurrencyDimId">
-                    <set field="fact.origCurrencyDimId" value="_NF_"/>
-                </if-empty>
-                <else>
-                    <set field="fact.origCurrencyDimId" value="_NA_"/>
-                </else>
-            </if-not-empty>
-            <create-value value-field="fact"/>
-        </if-empty>
-
-        <set field="fact.facilityId" from-field="inventory.facilityId"/>
-        <set field="fact.inventoryItemId" from-field="inventory.inventoryItemId"/>
-        <set field="fact.quantityOnHandTotal" from-field="inventory.quantityOnHandTotal"/>
-        <set field="fact.availableToPromiseTotal" from-field="inventory.availableToPromiseTotal"/>
-        <set field="fact.unitCost" from-field="inventory.unitCost"/>
-
-        <!-- calculate sold out amount -->
-        <calculate field="fact.soldoutAmount">
-            <calcop operator="get" field="inventory.quantityOnHandTotal">
-                <calcop operator="negative" field="inventory.availableToPromiseTotal"/>
-            </calcop>
-        </calculate>
-        <store-value value-field="fact"/>
-    </simple-method>
-
-</simple-methods>
diff --git a/bi/servicedef/services.xml b/bi/servicedef/services.xml
index 163a1e6..17c4dc8 100644
--- a/bi/servicedef/services.xml
+++ b/bi/servicedef/services.xml
@@ -85,8 +85,8 @@ under the License.
     </service>
 
     <!-- Accounting Facts -->
-    <service name="loadSalesInvoiceItemFact" auth="true" engine="simple"
-        location="component://bi/minilang/FactServices.xml" invoke="loadSalesInvoiceItemFact">
+    <service name="loadSalesInvoiceItemFact" auth="true" engine="groovy"
+        location="component://bi/groovyScripts/FactServices.groovy" invoke="loadSalesInvoiceItemFact">
         <description>
             Pulls information from the Invoice* entities and stores them in the SalesInvoiceItem entity (olap entity).
             One of invoiceId/invoiceItemSeqId or invoice/invoiceItem must be passed or an error is returned.
@@ -96,15 +96,15 @@ under the License.
         <attribute name="invoice" type="org.apache.ofbiz.entity.GenericValue" mode="IN" optional="true"/>
         <attribute name="invoiceItem" type="org.apache.ofbiz.entity.GenericValue" mode="IN" optional="true"/>
     </service>
-    <service name="loadSalesInvoiceFact" auth="true" engine="simple"
-        location="component://bi/minilang/FactServices.xml" invoke="loadSalesInvoiceFact">
+    <service name="loadSalesInvoiceFact" auth="true" engine="groovy"
+        location="component://bi/groovyScripts/FactServices.groovy" invoke="loadSalesInvoiceFact">
         <description>Calls the loadSalesInvoiceItemFact service for all the invoice items.</description>
         <attribute name="invoiceId" type="String" mode="IN" optional="false"/>
     </service>
 
     <!-- Order Facts -->
-    <service name="loadSalesOrderItemFact" auth="true" engine="simple"
-        location="component://bi/minilang/FactServices.xml" invoke="loadSalesOrderItemFact">
+    <service name="loadSalesOrderItemFact" auth="true" engine="groovy"
+        location="component://bi/groovyScripts/FactServices.groovy" invoke="loadSalesOrderItemFact">
         <description>
             Pulls information from the OrderItem* entities and stores them in the SalesOrderItem entity (olap entity).
             One of orderId/orderItemSeqId or order/orderItem must be passed or an error is returned.
@@ -115,23 +115,23 @@ under the License.
         <attribute name="orderItem" type="org.apache.ofbiz.entity.GenericValue" mode="IN" optional="true"/>
         <attribute name="orderAdjustment " type="org.apache.ofbiz.entity.GenericValue" mode="IN" optional="true"/>
     </service>
-    <service name="loadSalesOrderFact" auth="true" engine="simple"
-        location="component://bi/minilang/FactServices.xml" invoke="loadSalesOrderFact">
+    <service name="loadSalesOrderFact" auth="true" engine="groovy"
+        location="component://bi/groovyScripts/FactServices.groovy" invoke="loadSalesOrderFact">
         <description>Calls the loadSalesOrderItemFact service for all the order items.</description>
         <attribute name="orderId" type="String" mode="IN" optional="false"/>
     </service>
-    <service name="loadSalesOrderDataDaily" auth="true" engine="simple"
-        location="component://bi/minilang/FactServices.xml" invoke="loadSalesOrderDataDaily" transaction-timeout="10000000">
+    <service name="loadSalesOrderDataDaily" auth="true" engine="groovy"
+        location="component://bi/groovyScripts/FactServices.groovy" invoke="loadSalesOrderDataDaily" transaction-timeout="10000000">
         <description>Load Sales Order Data Daily.</description>
     </service>
-    <service name="importSalesOrderData" auth="true" engine="simple"
-        location="component://bi/minilang/FactServices.xml" invoke="importSalesOrderData" transaction-timeout="10000000">
+    <service name="importSalesOrderData" auth="true" engine="groovy"
+        location="component://bi/groovyScripts/FactServices.groovy" invoke="importSalesOrderData" transaction-timeout="10000000">
         <description>Import Sales Order Data.</description>
         <attribute name="fromDate" type="Timestamp" mode="IN" optional="false"/>
         <attribute name="thruDate" type="Timestamp" mode="IN" optional="false"/>
     </service>
-    <service name="convertUomCurrency" auth="true" engine="simple"
-        location="component://bi/minilang/FactServices.xml" invoke="convertUomCurrency">
+    <service name="convertUomCurrency" auth="true" engine="groovy"
+        location="component://bi/groovyScripts/FactServices.groovy" invoke="convertUomCurrency">
         <description>Import Sales Order Data.</description>
         <attribute name="uomId" type="String" mode="IN"/>
         <attribute name="uomIdTo" type="String" mode="IN"/>
@@ -140,8 +140,8 @@ under the License.
     </service>
 
     <!-- Inventory Facts -->
-    <service name="loadInventoryFact" auth="true" engine="simple"
-        location="component://bi/minilang/FactServices.xml" invoke="loadInventoryFact">
+    <service name="loadInventoryFact" auth="true" engine="groovy"
+        location="component://bi/groovyScripts/FactServices.groovy" invoke="loadInventoryFact">
         <attribute name="inventoryItemId" type="String" mode="IN" optional="true"/>
     </service>