You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ofbiz.apache.org by mb...@apache.org on 2020/02/21 16:51:06 UTC
[ofbiz-framework] branch trunk updated: Improved: Convert
ProductServices.xml mini lang to groovy (OFBIZ-10231)
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-framework.git
The following commit(s) were added to refs/heads/trunk by this push:
new e0a26fc Improved: Convert ProductServices.xml mini lang to groovy (OFBIZ-10231)
e0a26fc is described below
commit e0a26fce43eec7c84d87c0d5055ff0a87f2af796
Author: Michael Brohl <mi...@ecomify.de>
AuthorDate: Fri Feb 21 16:59:37 2020 +0100
Improved: Convert ProductServices.xml mini lang to groovy
(OFBIZ-10231)
Thanks Dennis Balkir for reporting and Sebastian Berg for the implementation.
---
.../product/product/ProductServices.groovy | 1095 ++++++++++++++++++++
.../minilang/product/product/ProductServices.xml | 1051 -------------------
applications/product/servicedef/services.xml | 112 +-
3 files changed, 1151 insertions(+), 1107 deletions(-)
diff --git a/applications/product/groovyScripts/product/product/ProductServices.groovy b/applications/product/groovyScripts/product/product/ProductServices.groovy
new file mode 100644
index 0000000..b4be894
--- /dev/null
+++ b/applications/product/groovyScripts/product/product/ProductServices.groovy
@@ -0,0 +1,1095 @@
+/*
+ * 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 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.serialize.XmlSerializer
+import org.apache.ofbiz.entity.util.EntityUtil
+import org.apache.ofbiz.product.product.KeywordIndex
+import org.apache.ofbiz.product.product.ProductWorker
+import org.apache.ofbiz.service.ServiceUtil
+
+
+
+ module = "ProductServices.groovy" // this is used for logging
+
+ /**
+ * Create a Product
+ */
+ def createProduct() {
+ Map result = success()
+ if (!(security.hasEntityPermission("CATALOG", "_CREATE", parameters.userLogin)
+ || security.hasEntityPermission("CATALOG_ROLE", "_CREATE", parameters.userLogin))) {
+ return error(UtilProperties.getMessage("ProductUiLabels", "ProductCatalogCreatePermissionError", parameters.locale))
+ }
+
+ GenericValue newEntity = makeValue("Product")
+ newEntity.setNonPKFields(parameters)
+
+ newEntity.productId = parameters.productId
+
+ if (UtilValidate.isEmpty(newEntity.productId)) {
+ newEntity.productId = delegator.getNextSeqId("Product")
+ } else {
+ String errorMessage = UtilValidate.checkValidDatabaseId(newEntity.productId)
+ if(errorMessage != null) {
+ logError(errorMessage)
+ return error(errorMessage)
+ }
+ GenericValue dummyProduct = findOne("Product", ["productId": parameters.productId], false)
+ if (UtilValidate.isNotEmpty(dummyProduct)) {
+ errorMessage = UtilProperties.getMessage("CommonErrorUiLabels", CommonErrorDuplicateKey, parameters.locale)
+ logError(errorMessage)
+ return error(errorMessage)
+ }
+ }
+ result.productId = newEntity.productId
+
+ Timestamp nowTimestamp = UtilDateTime.nowTimestamp()
+
+ newEntity.createdDate = nowTimestamp
+ newEntity.lastModifiedDate = nowTimestamp
+ newEntity.lastModifiedByUserLogin = userLogin.userLoginId
+ newEntity.createdByUserLogin = userLogin.userLoginId
+
+ if (UtilValidate.isEmpty(newEntity.isVariant)) {
+ newEntity.isVariant = "N"
+ }
+ if (UtilValidate.isEmpty(newEntity.isVirtual)) {
+ newEntity.isVirtual = "N"
+ }
+ if (UtilValidate.isEmpty(newEntity.billOfMaterialLevel)) {
+ newEntity.billOfMaterialLevel = (Long) 0
+ }
+
+ newEntity.create()
+
+ /*
+ * if setting the primaryProductCategoryId create a member entity too
+ * THIS IS REMOVED BECAUSE IT CAUSES PROBLEMS FOR WORKING ON PRODUCTION SITES
+ * <if-not-empty field="newEntity.primaryProductCategoryId">
+ * <make-value entity-name="ProductCategoryMember" value-field="newMember"/>
+ * <set from-field="productId" map-name="newEntity" to-field-name="productId" to-map-name="newMember"/>
+ * <set from-field="primaryProductCategoryId" map-name="newEntity" to-field-name="productCategoryId" to-map-name="newMember"/>
+ * <now-timestamp field="nowStamp"/>
+ * <set from-field="nowStamp" field="newMember.fromDate"/>
+ * <create-value value-field="newMember"/>
+ * </if-not-empty>
+ */
+
+ // if the user has the role limited position, add this product to the limit category/ies
+
+
+ if (security.hasEntityPermission("CATALOG_ROLE","_CREATE", parameters.userLogin)) {
+ List productCategoryRoles = from("ProductCategoryRole").where("partyId": userLogin.partyId, "roleTypeId": "LTD_ADMIN").queryList()
+
+ for (GenericValue productCategoryRole : productCategoryRoles) {
+ // add this new product to the category
+ GenericValue newLimitMember = makeValue("ProductCategoryMember")
+ newLimitMember.productId = newEntity.productId
+ newLimitMember.productCateogryId = productCategoryRole.productCategoryId
+ newLimitMember.fromDate = nowTimestamp
+ newLimitMember.create()
+ }
+ }
+
+ return result
+}
+
+/**
+ * Update a product
+ */
+def updateProduct() {
+ Map res = checkProductRelatedPermission("updateProduct", "UPDATE")
+ if (!ServiceUtil.isSuccess(res)) {
+ return res
+ }
+ GenericValue lookedUpValue = findOne("Product", ["productId": parameters.productId], false)
+ // save this value before overwriting it so we can compare it later
+ Map saveIdMap = ["primaryProductCategoryId": lookedUpValue.primaryProductCategoryId]
+
+ lookedUpValue.setNonPKFields(parameters)
+ lookedUpValue.lastModifiedDate = UtilDateTime.nowTimestamp()
+ lookedUpValue.lastModifiedByUserLogin = userLogin.userLoginId
+ lookedUpValue.store()
+
+ return success()
+ }
+
+ /**
+ * Update a Product Name from quick admin
+ */
+def updateProductQuickAdminName() {
+ Map res = checkProductRelatedPermission("updateQuickAdminName", "UPDATE")
+
+ if (!ServiceUtil.isSuccess(res)) {
+ return res
+ }
+
+ GenericValue lookedUpValue = findOne("Product", ["productId": parameters.productId], false)
+ lookedUpValue.productName = parameters.productName
+ if ("Y".equals(lookedUpValue.isVirtual)) {
+ lookedUpValue.internalName = lookedUpValue.productName
+ }
+
+ lookedUpValue.lastModifiedDate = UtilDateTime.nowTimestamp();
+ lookedUpValue.lastModifiedByUserLogin = userLogin.userLoginId
+
+ lookedUpValue.store()
+
+ if ("Y".equals(lookedUpValue.isVirtual)) {
+ // get all variant products, to update their productNames
+ Map variantProductAssocMap = ["productId": parameters.productId, "productAssocTypeId": "PRODUCT_VARIANT"]
+
+ // get all productAssocs, then get the actual product to update
+ List variantProductAssocs = from("ProductAssoc").where(variantProductAssocMap).queryList()
+ variantProductAssocs = EntityUtil.filterByDate(variantProductAssocs)
+ for(GenericValue variantProductAssoc : variantProductAssocs) {
+ GenericValue variantProduct = null
+ variantProduct = findOne("Product", ["productId": variantProductAssoc.productIdTo], false)
+
+ variantProduct.productName = parameters.productName
+ variantProduct.lastModifiedDate = UtilDateTime.nowTimestamp()
+ variantProduct.lastModifiedByUserLogin = userLogin.userLoginId
+ variantProduct.store()
+ }
+ }
+ return success()
+}
+
+/**
+ * Duplicate a Product
+ */
+def duplicateProduct() {
+ String callingMethodName = "duplicateProduct"
+ String checkAction = "CREATE"
+ Map res = checkProductRelatedPermission(callingMethodName, checkAction)
+ if (!ServiceUtil.isSuccess(res)) {
+ return res
+ }
+ checkAction = "DELETE"
+ res = checkProductRelatedPermission(callingMethodName, checkAction)
+ if (!ServiceUtil.isSuccess(res)) {
+ return res
+ }
+ GenericValue dummyProduct = findOne("Product", ["productId": parameters.productId], false)
+ if (UtilValidate.isNotEmpty(dummyProduct)) {
+ String errorMessage = UtilProperties.getMessage("CommonErrorUiLabels", CommonErrorDuplicateKey, parameters.locale)
+ logError(errorMessage)
+ return error(errorMessage)
+ }
+
+ // look up the old product and clone it
+ GenericValue oldProduct = findOne("Product", ["productId": parameters.oldProductId], false)
+ GenericValue newProduct = oldProduct.clone()
+
+ // set the productId, and write it to the datasource
+ newProduct.productId = parameters.productId
+
+ // if requested, set the new internalName field
+ if (UtilValidate.isNotEmpty(parameters.newInternalName)) {
+ newProduct.internalName = parameters.newInternalName
+ }
+
+ // if requested, set the new productName field
+ if (UtilValidate.isNotEmpty(parameters.newProductName)) {
+ newProduct.productName = parameters.newProductName
+ }
+
+ // if requested, set the new description field
+ if (UtilValidate.isNotEmpty(parameters.newDescription)) {
+ newProduct.description = parameters.newDescription
+ }
+
+ // if requested, set the new longDescription field
+ if (UtilValidate.isNotEmpty(parameters.newLongDescription)) {
+ newProduct.longDescription = parameters.newLongDescription
+ }
+
+ newProduct.create()
+
+ // set up entity filter
+ Map productFindContext = ["productId": parameters.oldProductId]
+ Map reverseProductFindContext = ["productIdTo": parameters.oldProductId]
+
+ // if requested, duplicate related data as well
+ if (UtilValidate.isNotEmpty(parameters.duplicatePrices)) {
+ List foundValues = from("ProductPrice").where(productFindContext).queryList()
+ for (GenericValue foundValue : foundValues) {
+ GenericValue newTempValue = foundValue.clone()
+ newTempValue.productId = parameters.productId
+ newTempValue.create()
+ }
+ }
+ if (UtilValidate.isNotEmpty(parameters.duplicateIDs)) {
+ List foundValues = from("GoodIdentification").where(productFindContext).queryList()
+ for (GenericValue foundValue : foundValues) {
+ GenericValue newTempValue = foundValue.clone()
+ newTempValue.productId = parameters.productId
+ newTempValue.create()
+ }
+ }
+ if (UtilValidate.isNotEmpty(parameters.duplicateContent)) {
+ List foundValues = from("ProductContent").where(productFindContext).queryList()
+ for (GenericValue foundValue : foundValues) {
+ GenericValue newTempValue = foundValue.clone()
+ newTempValue.productId = parameters.productId
+ newTempValue.create()
+ }
+ }
+ if (UtilValidate.isNotEmpty(parameters.duplicateCategoryMembers)) {
+ List foundValues = from("ProductCategoryMember").where(productFindContext).queryList()
+ for (GenericValue foundValue : foundValues) {
+ GenericValue newTempValue = foundValue.clone()
+ newTempValue.productId = parameters.productId
+
+ newTempValue.create()
+ }
+ }
+ if (UtilValidate.isNotEmpty(parameters.duplicateAssocs)) {
+ List foundValues = from("ProductAssoc").where(productFindContext).queryList()
+ for (GenericValue foundValue : foundValues) {
+ GenericValue newTempValue = foundValue.clone()
+ newTempValue.productId = parameters.productId
+ newTempValue.create()
+ }
+
+ // small difference here, also do the reverse assocs...
+ foundValues = from("ProductAssoc").where("productIdTo": parameters.oldProductId).queryList()
+ for (GenericValue foundValue : foundValues) {
+ GenericValue newTempValue = foundValue.clone()
+ newTempValue.productIdTo = parameters.productId
+ newTempValue.create()
+ }
+ }
+ if (UtilValidate.isNotEmpty(parameters.duplicateAttributes)) {
+ List foundValues = from("ProductAttribute").where(productFindContext).queryList()
+ for (GenericValue foundValue : foundValues) {
+ GenericValue newTempValue = foundValue.clone()
+ newTempValue.productId = parameters.productId
+ newTempValue.create()
+ }
+ }
+ if (UtilValidate.isNotEmpty(parameters.duplicateFeatureAppls)) {
+ List foundValues = from("ProductFeatureAppl").where(productFindContext).queryList()
+ for (GenericValue foundValue : foundValues) {
+ GenericValue newTempValue = foundValue.clone()
+ newTempValue.productId = parameters.productId
+ newTempValue.create()
+ }
+ }
+ if (UtilValidate.isNotEmpty(parameters.duplicateInventoryItems)) {
+ List foundValues = from("InventoryItem").where(productFindContext).queryList()
+ for (GenericValue foundValue : foundValues) {
+ /*
+ * NOTE: new inventory items should always be created calling the
+ * createInventoryItem service because in this way we are sure
+ * that all the relevant fields are filled with default values.
+ * However, the code here should work fine because all the values
+ * for the new inventory item are inerited from the existing item.
+ * TODO: is this code correct? What is the meaning of duplicating inventory items?
+ * What about the InventoryItemDetail entries?
+ */
+ GenericValue newTempValue = foundValue.clone()
+ newTempValue.productId = parameters.productId
+ // this one is slightly different because it needs a new sequenced inventoryItemId
+ newTempValue.inventoryItemId = delegator.getNextSeqId("InventoryItem")
+ newTempValue.create()
+ }
+ }
+
+ // if requested, remove related data as well
+ if (UtilValidate.isNotEmpty(parameters.removePrices)) {
+ delegator.removeByAnd("ProductPrice", productFindContext)
+ }
+ if (UtilValidate.isNotEmpty(parameters.removeIDs)) {
+ delegator.removeByAnd("GoodIdentification", productFindContext)
+ }
+ if (UtilValidate.isNotEmpty(parameters.removeContent)) {
+ delegator.removeByAnd("ProductContent", productFindContext)
+ }
+ if (UtilValidate.isNotEmpty(parameters.removeCategoryMembers)) {
+ delegator.removeByAnd("ProductCategoryMember", productFindContext)
+ }
+ if (UtilValidate.isNotEmpty(parameters.removeAssocs)) {
+ delegator.removeByAnd("ProductAssoc", productFindContext)
+ // small difference here, also do the reverse assocs...
+ delegator.removeByAnd("ProductAssoc", reverseProductFindContext)
+ }
+ if (UtilValidate.isNotEmpty(parameters.removeAttributes)) {
+ delegator.removeByAnd("ProductAttribute", productFindContext)
+ }
+ if (UtilValidate.isNotEmpty(parameters.removeFeatureAppls)) {
+ delegator.removeByAnd("ProductFeatureAppl", productFindContext)
+ }
+ if (UtilValidate.isNotEmpty(parameters.removeInventoryItems)) {
+ delegator.removeByAnd("InventoryItem", productFindContext)
+ }
+ return success()
+}
+
+// Product Keyword Services
+
+/**
+ * induce all the keywords of a product
+ */
+def forceIndexProductKeywords() {
+ GenericValue product = findOne("Product", [productId: parameters.productId], false)
+ KeywordIndex.forceIndexKeywords(product)
+ return success()
+}
+
+/**
+ * delete all the keywords of a produc
+ */
+def deleteProductKeywords() {
+ GenericValue product = findOne("Product", [productId: parameters.productId], false)
+ delegator.removeRelated("ProductKeyword", product)
+ return success()
+}
+
+/**
+ * Index the Keywords for a Product
+ */
+def indexProductKeywords() {
+ //this service is meant to be called from an entity ECA for entities that include a productId
+ //if it is the Product entity itself triggering this action, then a [productInstance] parameter
+ //will be passed and we can save a few cycles looking that up
+ GenericValue productInstance = parameters.productInstance
+ if (productInstance == null) {
+ Map findProductMap = [productId: parameters.productId]
+ productInstance = findOne("Product", findProductMap, false)
+ }
+ //induce keywords if autoCreateKeywords is empty or Y
+ if (UtilValidate.isEmpty(productInstance.autoCreateKeywords) || "Y".equals(productInstance.autoCreateKeywords)) {
+ KeywordIndex.indexKeywords(productInstance)
+ }
+ return success()
+}
+
+/**
+ * Discontinue Product Sales
+ * set sales discontinuation date to now
+ */
+def discontinueProductSales() {
+ // set sales discontinuation date to now
+ Timestamp nowTimestamp = UtilDateTime.nowTimestamp()
+ GenericValue product = findOne("Product", parameters, false)
+ product.salesDiscontinuationDate = nowTimestamp
+ product.store()
+
+ // expire product from all categories
+ List productCategoryMembers = delegator.getRelated("ProductCategoryMember", null, null, product, false)
+ for (GenericValue productCategoryMember : productCategoryMembers) {
+ if (UtilValidate.isEmpty(productCategoryMember.thruDate)) {
+ productCategoryMember.thruDate = UtilDateTime.nowTimestamp()
+ productCategoryMember.store()
+ }
+ }
+ // expire product from all associations going to it
+ List assocProductAssocs = delegator.getRelated("AssocProductAssoc", null, null, product, false)
+ for (GenericValue assocProductAssoc : assocProductAssocs) {
+ if (UtilValidate.isEmpty(assocProductAssoc.thruDate)) {
+ assocProductAssoc.thruDate = UtilDateTime.nowTimestamp()
+ assocProductAssoc.store()
+ }
+ }
+ return success()
+}
+
+
+def countProductView() {
+ if (UtilValidate.isEmpty(parameters.weight)) {
+ parameters.weight = (Long) 1
+ }
+ GenericValue productCalculatedInfo = findOne("ProductCalculatedInfo", ["productId": parameters.productId], false)
+ if (UtilValidate.isEmpty(productCalculatedInfo)) {
+ // go ahead and create it
+ productCalculatedInfo = makeValue("ProductCalculatedInfo")
+ productCalculatedInfo.productId = parameters.productId
+ productCalculatedInfo.totalTimesViewed = parameters.weight
+ productCalculatedInfo.create()
+ } else {
+ productCalculatedInfo.totalTimesViewed = productCalculatedInfo.totalTimesViewed + parameters.weight
+ productCalculatedInfo.store()
+ }
+
+ // do the same for the virtual product...
+ GenericValue product = findOne("Product", ["productId": parameters.productId], true)
+ ProductWorker productWorker = new ProductWorker()
+ String virtualProductId = productWorker.getVariantVirtualId(product)
+ if (UtilValidate.isNotEmpty(virtualProductId)) {
+ Map callSubMap = ["productId": virtualProductId, "weight": parameters.weight]
+ run service: "countProductView", with: callSubMap
+ }
+ return success()
+
+}
+
+/**
+ * Create a ProductReview
+ */
+def createProductReview() {
+ GenericValue newEntity = makeValue("ProductReview", parameters)
+ newEntity.userLoginId = userLogin.userLoginId
+ newEntity.statusId = "PRR_PENDING"
+
+ // code to check for auto-approved reviews (store setting)
+ GenericValue productStore = findOne("ProductStore", ["productStoreId": parameters.productStoreId], false)
+
+ if (!UtilValidate.isEmpty(productStore)) {
+ if ("Y".equals(productStore.autoApproveReviews)) {
+ newEntity.statusId = "PRR_APPROVED"
+ }
+ }
+
+ // create the new ProductReview
+ newEntity.productReviewId = delegator.getNextSeqId("ProductReview")
+ Map result = success()
+ result.productReviewId = newEntity.productReviewId
+
+ if (UtilValidate.isEmpty(newEntity.postedDateTime)) {
+ newEntity.postedDateTime = UtilDateTime.nowTimestamp()
+ }
+
+ newEntity.create()
+
+ String productId = newEntity.productId
+ String successMessage = UtilProperties.getMessage("ProductUiLabels", "ProductCreateProductReviewSuccess", parameters.locale)
+ updateProductWithReviewRatingAvg(productId)
+
+ return result
+}
+
+/**
+ * Update ProductReview
+ */
+def updateProductReview() {
+ Map res = checkProductRelatedPermission("updateProductReview", "UPDATE")
+ if (!ServiceUtil.isSuccess(res)) {
+ return res
+ }
+
+ GenericValue lookupPKMap = makeValue("ProductReview")
+ lookupPKMap.setPKFields(parameters)
+ GenericValue lookedUpValue = findOne("ProductReview", lookupPKMap, false)
+ lookupPKMap.setNonPKFields(parameters)
+ lookupPKMap.store()
+
+ String productId = lookedUpValue.productId
+ updateProductWithReviewRatingAvg(productId)
+
+ return success()
+}
+
+/**
+ * change the product review Status
+ */
+def setProductReviewStatus(){
+ Map res = checkProductRelatedPermission("setProductReviewStatus", "UPDATE")
+ if (!ServiceUtil.isSuccess(res)) {
+ return res
+ }
+
+ GenericValue productReview = findOne("ProductReview", parameters, false)
+ if (UtilValidate.isNotEmpty(productReview)) {
+ if (!productReview.statusId.equals(parameters.statusId)) {
+ GenericValue statusChange = from("StatusValidChange")
+ .where("statusId", productReview.statusId, "statusIdTo", parameters.statusId)
+ .queryOne()
+ if (UtilValidate.isEmpty(statusChange)) {
+ String msg = "Status is not a valid change: from " + productReview.statusId + " to " + parameters.statusId
+ logError(msg)
+ String errorMessage = UtilProperties.getMessage("ProductErrorUiLabels", ProductReviewErrorCouldNotChangeOrderStatusFromTo, parameters.locale)
+ logError(errorMessage)
+ return error(errorMessage)
+ }
+ }
+ }
+
+ productReview.statusId = parameters.statusId
+ productReview.store()
+ Map result = success()
+ result.productReviewId = productReview.productReviewId
+
+ return result
+}
+
+/**
+ * Update Product with new Review Rating Avg
+ * this method is meant to be called in-line and depends in a productId parameter
+ */
+def updateProductWithReviewRatingAvg(String productId) {
+ ProductWorker productWorker = new ProductWorker()
+ BigDecimal averageCustomerRating = productWorker.getAverageProductRating(delegator, productId)
+ logInfo("Got new average customer rating "+ averageCustomerRating)
+
+ if (averageCustomerRating == 0) {
+ return success()
+ }
+
+ // update the review average on the ProductCalculatedInfo entity
+ GenericValue productCalculatedInfo = findOne("ProductCalculatedInfo", parameters, false)
+ if (UtilValidate.isEmpty(productCalculatedInfo)) {
+ // go ahead and create it
+ productCalculatedInfo = makeValue("ProductCalculatedInfo")
+ productCalculatedInfo.productId = productId
+ productCalculatedInfo.averageCustomerRating = averageCustomerRating
+ productCalculatedInfo.create()
+ } else {
+ productCalculatedInfo.averageCustomerRating = averageCustomerRating
+ productCalculatedInfo.store()
+ }
+
+ return success()
+}
+
+/**
+ * Updates the Product's Variants
+ */
+def copyToProductVariants() {
+ String callingMethodName = "copyToProductVariants"
+ String checkAction = "CREATE"
+ Map res = checkProductRelatedPermission(callingMethodName, checkAction)
+ if (!ServiceUtil.isSuccess(res)) {
+ return res
+ }
+ checkAction = "DELETE"
+ res = checkProductRelatedPermission(callingMethodName, checkAction)
+ if (!ServiceUtil.isSuccess(res)) {
+ return res
+ }
+
+ Map productFindContext = ["productId": parameters.virtualProductId]
+ GenericValue oldProduct = findOne("Product", productFindContext, false)
+
+ Map variantsFindContext = ["productId": parameters.virtualProductId, "productAssocTypeId": "PRODUCT_VARIANT"]
+
+ List variants = from("ProductAssoc").where(variantsFindContext).filterByDate().queryList()
+ List foundVariantValues = []
+ List foundValues = []
+ for (GenericValue newProduct : variants) {
+ Map productVariantContext = ["productId": newProduct.productIdTo]
+ // if requested, duplicate related data
+ if (UtilValidate.isNotEmpty(parameters.duplicatePrices)) {
+ if (UtilValidate.isNotEmpty(parameters.removeBefore)) {
+ foundVariantValues = from("ProductPrice").where(productVariantContext).queryList()
+ for (GenericValue foundVariantValue : foundVariantValues) {
+ foundVariantValue.remove()
+ }
+ }
+ foundValues = from("ProductPrice").where(productFindContext).queryList()
+ for (GenericValue foundValue : foundValues) {
+ GenericValue newTempValue = foundValue.clone()
+ newTempValue.productId = newProduct.productIdTo
+ newTempValue.create()
+ }
+ }
+ if (UtilValidate.isNotEmpty(parameters.duplicateIDs)) {
+ if (UtilValidate.isNotEmpty(parameters.removeBefore)) {
+ foundVariantValues = from("GoodIdentification").where(productVariantContext).queryList()
+ for (GenericValue foundVariantValue : foundVariantValues) {
+ foundVariantValue.remove()
+ }
+ }
+ foundValues = from("GoodIdentification").where(productFindContext).queryList()
+ for (GenericValue foundValue : foundValues) {
+ GenericValue newTempValue = foundValue.clone()
+ newTempValue.productId = newProduct.productIdTo
+ newTempValue.create()
+ }
+
+ }
+ if (UtilValidate.isNotEmpty(parameters.duplicateContent)) {
+ if (UtilValidate.isNotEmpty(parameters.removeBefore)) {
+ foundVariantValues = from("ProductContent").where(productVariantContext).queryList()
+ for (GenericValue foundVariantValue : foundVariantValues) {
+ foundVariantValue.remove()
+ }
+ }
+ foundValues = from("ProductContent").where(productFindContext).queryList()
+ for (GenericValue foundValue : foundValues) {
+ GenericValue newTempValue = foundValue.clone()
+ newTempValue.productId = newProduct.productIdTo
+ newTempValue.create()
+ }
+ }
+ if (UtilValidate.isNotEmpty(parameters.duplicateCategoryMembers)) {
+ if (UtilValidate.isNotEmpty(parameters.removeBefore)) {
+ foundVariantValues = from("ProductCategoryMember").where(productVariantContext).queryList()
+ for (GenericValue foundVariantValue : foundVariantValues) {
+ foundVariantValue.remove()
+ }
+ }
+ foundValues = from("ProductCategoryMember").where(productFindContext).queryList()
+ for (GenericValue foundValue : foundValues) {
+ GenericValue newTempValue = foundValue.clone()
+ newTempValue.productId = newProduct.productIdTo
+ newTempValue.create()
+ }
+ }
+ if (UtilValidate.isNotEmpty(parameters.duplicateAttributes)) {
+ if (UtilValidate.isNotEmpty(parameters.removeBefore)) {
+ foundVariantValues = from("ProductAttribute").where(productVariantContext).queryList()
+ for (GenericValue foundVariantValue : foundVariantValues) {
+ foundVariantValue.remove()
+ }
+ }
+ foundValues = from("ProductAttribute").where(productFindContext).queryList()
+ for (GenericValue foundValue : foundValues) {
+ GenericValue newTempValue = foundValue.clone()
+ newTempValue.productId = newProduct.productIdTo
+ newTempValue.create()
+ }
+ }
+ if (UtilValidate.isNotEmpty(parameters.duplicateFacilities)) {
+ if (UtilValidate.isNotEmpty(parameters.removeBefore)) {
+ foundVariantValues = from("ProductFacility").where(productVariantContext).queryList()
+ for (GenericValue foundVariantValue : foundVariantValues) {
+ foundVariantValue.remove()
+ }
+ }
+ foundValues = from("ProductFacility").where(productFindContext).queryList()
+ for (GenericValue foundValue : foundValues) {
+ GenericValue newTempValue = foundValue.clone()
+ newTempValue.productId = newProduct.productIdTo
+ newTempValue.create()
+ }
+ }
+ if (UtilValidate.isNotEmpty(parameters.duplicateLocations)) {
+ if (UtilValidate.isNotEmpty(parameters.removeBefore)) {
+ foundVariantValues = from("ProductFacilityLocation").where(productVariantContext).queryList()
+ for (GenericValue foundVariantValue : foundVariantValues) {
+ foundVariantValue.remove()
+ }
+ }
+ foundValues = from("ProductFacilityLocation").where(productFindContext).queryList()
+ for (GenericValue foundValue : foundValues) {
+ GenericValue newTempValue = foundValue.clone()
+ newTempValue.productId = newProduct.productIdTo
+ newTempValue.create()
+ }
+ }
+ }
+ return success()
+}
+
+/**
+ * Check Product Related Permission
+ * a method to centralize product security code, meant to be called in-line with
+ * call-simple-method, and the checkAction and callingMethodName attributes should be in the method context
+ */
+def checkProductRelatedPermission (String callingMethodName, String checkAction){
+ if (UtilValidate.isEmpty(callingMethodName)) {
+ callingMethodName = UtilProperties.getMessage("CommonUiLabels", "CommonPermissionThisOperation", parameters.locale)
+ }
+ if (UtilValidate.isEmpty(checkAction)) {
+ checkAction = "UPDATE"
+ }
+ List roleCategories = []
+ // find all role-categories that this product is a member of
+ if (!security.hasEntityPermission("CATALOG", "_${checkAction}", parameters.userLogin)) {
+ Map lookupRoleCategoriesMap = ["productId": parameters.productId, "partyId": userLogin.partyId, "roleTypeId": "LTD_ADMIN"]
+ roleCategories = from("ProductCategoryMemberAndRole").where(lookupRoleCategoriesMap).filterByDate("roleFromDate", "roleThruDate").queryList()
+ }
+
+ if (! ((security.hasEntityPermission("CATALOG", "_${checkAction}", parameters.userLogin))
+ || (security.hasEntityPermission("CATALOG_ROLE", "_${checkAction}", parameters.userLogin) && !UtilValidate.isEmpty(roleCategories))
+ || (!UtilValidate.isEmpty(parameters.alternatePermissionRoot) && security.hasEntityPermission(parameters.alternatePermissionRoot, checkAction, parameters.userLogin)))) {
+ String checkActionLabel = "ProductCatalog" + checkAction.charAt(0) + checkAction.substring(1).toLowerCase() + "PermissionError"
+ String resourceDescription = callingMethodName
+
+ String errorMessage = UtilProperties.getMessage("ProductUiLabels", checkActionLabel, parameters.locale)
+ logError(errorMessage)
+ return error(errorMessage)
+ }
+ return success()
+}
+
+/**
+ * Main permission logic
+ */
+def productGenericPermission(){
+ String mainAction = parameters.mainAction
+ Map result = success()
+ if (UtilValidate.isEmpty(mainAction)) {
+ String errorMessage = UtilProperties.getMessage("ProductUiLabels", "ProductMissingMainActionInPermissionService", parameters.locale)
+ logError(errorMessage)
+ return error(errorMessage)
+ }
+ Map res = checkProductRelatedPermission(parameters.resourceDescription, parameters.mainAction)
+ if (!ServiceUtil.isSuccess(res)) {
+ String failMessage = UtilProperties.getMessage("ProductUiLabels", "ProductPermissionError", parameters.locale)
+ Boolean hasPermission = false
+ result = fail(failMessage)
+ result.hasPermission = hasPermission
+ } else {
+ Boolean hasPermission = true
+ result.hasPermission = hasPermission
+ }
+ return result
+}
+
+/**
+ * product price permission logic
+ */
+def productPriceGenericPermission(){
+ String mainAction = parameters.mainAction
+ if (UtilValidate.isEmpty(mainAction)) {
+ String errorMessage = UtilProperties.getMessage("ProductUiLabels", "ProductMissingMainActionInPermissionService", parameters.locale)
+ logError(errorMessage)
+ return error(errorMessage)
+ }
+ Map result = success()
+ if (!security.hasEntityPermission("CATALOG_PRICE_MAINT", null, parameters.userLogin)) {
+ String errorMessage = UtilProperties.getMessage("ProductUiLabels", "ProductPriceMaintPermissionError", parameters.locale)
+ logError(errorMessage)
+ result = error(errorMessage)
+ }
+ Map res = checkProductRelatedPermission(null, null)
+ if (ServiceUtil.isSuccess(result) && ServiceUtil.isSuccess(res)) {
+ result.hasPermission = true
+ } else {
+ String failMessage = UtilProperties.getMessage("ProductUiLabels", "ProductPermissionError", parameters.locale)
+ result = fail(failMessage)
+ result.hasPermission = false
+ }
+ return result
+}
+
+/**
+ * ================================================================
+ * ProductRole Services
+ * ================================================================
+ */
+
+
+/**
+ * Add Party to Product
+ */
+def addPartyToProduct(){
+ Map result = checkProductRelatedPermission("addPartyToProduct", "CREATE")
+ if (!ServiceUtil.isSuccess(result)) {
+ return result
+ }
+ GenericValue newEntity = makeValue("ProductRole", parameters)
+
+ if (UtilValidate.isEmpty(newEntity.fromDate)) {
+ newEntity.fromDate = UtilDateTime.nowTimestamp()
+ }
+ newEntity.create()
+ return success()
+}
+
+/**
+ * Update Party to Product
+ */
+def updatePartyToProduct(){
+ Map result = checkProductRelatedPermission("updatePartyToProduct", "UPDATE")
+ if (!ServiceUtil.isSuccess(result)) {
+ return result
+ }
+ GenericValue lookupPKMap = makeValue("ProductRole")
+ lookupPKMap.setPKFields(parameters)
+ GenericValue lookedUpValue = findOne("ProductRole", lookupPKMap, false)
+ lookedUpValue.setNonPKFields(parameters)
+ lookedUpValue.store()
+ return success()
+}
+
+/**
+ * Remove Party From Product
+ */
+def removePartyFromProduct(){
+ Map res = checkProductRelatedPermission("removePartyFromProduct", "DELETE")
+ if (!ServiceUtil.isSuccess(res)) {
+ return res
+ }
+ Map lookupPKMap = makeValue("ProductRole")
+ lookupPKMap.setPKFields(parameters)
+ GenericValue lookedUpValue = findOne("ProductRole", lookupPKMap, false)
+ lookedUpValue.remove()
+
+ return success()
+}
+
+// ProductCategoryGlAccount methods
+ /**
+ * Create a ProductCategoryGlAccount
+ */
+def createProductCategoryGlAccount(){
+ Map res = checkProductRelatedPermission("createProductCategoryGlAccount", "CREATE")
+ if (!ServiceUtil.isSuccess(res)) {
+ return res
+ }
+
+ GenericValue newEntity = makeValue("ProductCategoryGlAccount", parameters)
+ newEntity.create()
+
+ return success()
+}
+
+/**
+ * Update a ProductCategoryGlAccount
+ */
+def updateProductCategoryGlAccount(){
+ Map res = checkProductRelatedPermission("updateProductCategoryGlAccount", "UPDATE")
+ if (!ServiceUtil.isSuccess(res)) {
+ return res
+ }
+
+ GenericValue lookedUpValue = findOne("ProductCategoryGlAccount", parameters, false)
+ lookedUpValue.setNonPKFields(parameters)
+ lookedUpValue.store()
+
+ return success()
+}
+
+/**
+ * Delete a ProductCategoryGlAccount
+ */
+def deleteProductCategoryGlAccount(){
+ Map res = checkProductRelatedPermission("deleteProductCategorGLAccount", "DELETE")
+ if (!ServiceUtil.isSuccess(res)) {
+ return res
+ }
+ GenericValue lookedUpValue = findOne("ProductCategoryGlAccount", parameters, false)
+ lookedUpValue.remove()
+
+ return success()
+}
+
+// Product GroupOrder Services -->
+
+/**
+ * Create ProductGroupOrder
+ */
+def createProductGroupOrder(){
+ GenericValue newEntity = makeValue("ProductGroupOrder")
+ delegator.setNextSubSeqId(newEntity, "groupOrderId", 5, 1)
+ Map result = success()
+ result.groupOrderId = newEntity.groupOrderId
+ newEntity.setNonPKFields(parameters)
+ newEntity.create()
+
+ return result
+}
+
+/**
+ * Update ProductGroupOrder
+ */
+def updateProductGroupOrder(){
+ GenericValue productGroupOrder = findOne("ProductGroupOrder", ["groupOrderId": parameters.groupOrderId], false)
+ productGroupOrder.setNonPKFields(parameters)
+ productGroupOrder.store()
+
+ if ("GO_CREATED".equals(productGroupOrder.statusId)) {
+ GenericValue jobSandbox = findOne("JobSandbox", ["jobId": productGroupOrder.jobId], false)
+ if (UtilValidate.isNotEmpty(jobSandbox)) {
+ jobSandbox.runTime = parameters.thruDate
+ jobSandbox.store()
+ }
+ }
+ return success()
+}
+
+/**
+ * Delete ProductGroupOrder
+ */
+def deleteProductGroupOrder(){
+ List orderItemGroupOrders = from("OrderItemGroupOrder").where("groupOrderId": parameters.groupOrderId).queryList()
+ for (GenericValue orderItemGroupOrder : orderItemGroupOrders) {
+ orderItemGroupOrder.remove()
+ }
+ GenericValue productGroupOrder = findOne("ProductGroupOrder", ["groupOrderId": parameters.groupOrderId], false)
+ if (UtilValidate.isEmpty(productGroupOrder)) {
+ return error("Entity value not found with name: " + productGroupOrder)
+ }
+ productGroupOrder.remove()
+
+ GenericValue jobSandbox = findOne("JobSandbox", ["jobId": productGroupOrder.jobId], false)
+ if (UtilValidate.isEmpty(jobSandbox)) {
+ return error("Entity value not found with name: " + jobSandbox)
+ }
+ jobSandbox.remove()
+
+ List jobSandboxList = from("JobSandbox").where("runtimeDataId": jobSandbox.runtimeDataId).queryList()
+ for (GenericValue jobSandboxRelatedRuntimeData : jobSandboxList) {
+ jobSandboxRelatedRuntimeData.remove()
+ }
+
+ GenericValue runtimeData = findOne("RuntimeData", ["runtimeDataId": jobSandbox.runtimeDataId], false)
+ if (UtilValidate.isEmpty(runtimeData)) {
+ return error("Entity value not found with name: " + runtimeData)
+ }
+ runtimeData.remove()
+
+ return success()
+}
+
+/**
+ * Create ProductGroupOrder
+ */
+def createJobForProductGroupOrder(){
+ GenericValue productGroupOrder = findOne("ProductGroupOrder", ["groupOrderId": parameters.groupOrderId], false)
+ if (UtilValidate.isEmpty(productGroupOrder.jobId)) {
+ // Create RuntimeData For ProductGroupOrder
+ Map runtimeDataMap = ["groupOrderId": parameters.groupOrderId]
+ XmlSerializer xmlSerializer = new XmlSerializer()
+ String runtimeInfo = xmlSerializer.serialize(runtimeDataMap)
+
+ GenericValue runtimeData = makeValue("RuntimeData")
+ runtimeData.runtimeDataId = delegator.getNextSeqId("RuntimeData")
+ String runtimeDataId = runtimeData.runtimeDataId
+ runtimeData.runtimeInfo = runtimeInfo
+ runtimeData.create()
+
+ // Create Job For ProductGroupOrder
+ // FIXME: Jobs should not be manually created
+ GenericValue jobSandbox = makeValue("JobSandbox")
+ jobSandbox.jobId = delegator.getNextSeqId("JobSandbox")
+ String jobId = jobSandbox.jobId
+ jobSandbox.jobName = "Check ProductGroupOrder Expired"
+ jobSandbox.runTime = parameters.thruDate
+ jobSandbox.poolId = "pool"
+ jobSandbox.statusId = "SERVICE_PENDING"
+ jobSandbox.serviceName = "checkProductGroupOrderExpired"
+ jobSandbox.runAsUser = "system"
+ jobSandbox.runtimeDataId = runtimeDataId
+ jobSandbox.maxRecurrenceCount = (Long) 1
+ jobSandbox.priority = (Long) 50
+ jobSandbox.create()
+
+ productGroupOrder.jobId = jobId
+ productGroupOrder.store()
+ }
+ return success()
+}
+
+/**
+ * Check OrderItem For ProductGroupOrder
+ */
+def checkOrderItemForProductGroupOrder(){
+ List orderItems = from("OrderItem").where("orderId": parameters.orderId).queryList()
+ for (GenericValue orderItem : orderItems) {
+ String productId = orderItem.productId
+ GenericValue product = findOne("Product", ["productId": orderItem.productId], false)
+ if ("Y".equals(product.isVariant)) {
+ List variantProductAssocs = from("ProductAssoc").where("productIdTo": orderItem.productId, "productAssocTypeId": "PRODUCT_VARIANT").queryList()
+ variantProductAssocs = EntityUtil.filterByDate(variantProductAssocs)
+ GenericValue variantProductAssoc = variantProductAssocs.get(0)
+ productId = variantProductAssoc.productId
+ }
+ List productGroupOrders = from("ProductGroupOrder").where("productId": productId).queryList()
+ if (UtilValidate.isNotEmpty(productGroupOrders)) {
+ productGroupOrders = EntityUtil.filterByDate(productGroupOrders)
+ GenericValue productGroupOrder = productGroupOrders.get(0)
+ if (UtilValidate.isEmpty(productGroupOrder.soldOrderQty)) {
+ productGroupOrder.soldOrderQty = orderItem.quantity
+ } else {
+ productGroupOrder.soldOrderQty = productGroupOrder.soldOrderQty + orderItem.quantity
+ }
+ productGroupOrder.store()
+
+ Map createOrderItemGroupOrderMap = ["orderId": orderItem.orderId, "orderItemSeqId": orderItem.orderItemSeqId, "groupOrderId": productGroupOrder.groupOrderId]
+
+ run service: "createOrderItemGroupOrder", with: createOrderItemGroupOrderMap
+ }
+ }
+ return success()
+}
+
+/**
+ * Cancle OrderItemGroupOrder
+ */
+def cancleOrderItemGroupOrder(){
+ List orderItems = []
+ if (UtilValidate.isNotEmpty(parameters.orderItemSeqId)) {
+ orderItems = from("OrderItem")
+ .where("orderId", parameters.orderId, "orderItemSeqId", parameters.orderItemSeqId)
+ .queryList()
+ } else {
+ orderItems = from("OrderItem")
+ .where("orderId", parameters.orderId)
+ .queryList()
+ }
+ for(GenericValue orderItem : orderItems) {
+ List orderItemGroupOrders = from("OrderItemGroupOrder")
+ .where("orderId", orderItem.orderId, "orderItemSeqId", orderItem.orderItemSeqId)
+ .queryList()
+ if (UtilValidate.isNotEmpty(orderItemGroupOrders)) {
+ GenericValue orderItemGroupOrder = orderItemGroupOrders.get(0)
+ GenericValue productGroupOrder = findOne("ProductGroupOrder", [groupOrderId: orderItemGroupOrder.groupOrderId], false)
+
+ if (UtilValidate.isNotEmpty(productGroupOrder)) {
+ if ("GO_CREATED".equals(productGroupOrder.statusId)) {
+ if ("ITEM_CANCELLED".equals(orderItem.statusId)) {
+ BigDecimal cancelQuantity
+ if (UtilValidate.isNotEmpty(orderItem.cancelQuantity)) {
+ cancelQuantity = orderItem.cancelQuantity
+ } else {
+ cancelQuantity = orderItem.quantity
+ }
+ productGroupOrder.soldOrderQty = productGroupOrder.soldOrderQty - cancelQuantity
+ }
+ productGroupOrder.store()
+ orderItemGroupOrder.remove()
+ }
+ }
+ }
+ }
+ return success()
+}
+
+/**
+ * Check ProductGroupOrder Expired
+ */
+def checkProductGroupOrderExpired(){
+ GenericValue productGroupOrder = findOne("ProductGroupOrder", parameters, false)
+ if (UtilValidate.isNotEmpty(productGroupOrder)) {
+ String groupOrderStatusId
+ String newItemStatusId
+ if (productGroupOrder.soldOrderQty >= productGroupOrder.reqOrderQty) {
+ newItemStatusId = "ITEM_APPROVED"
+ groupOrderStatusId = "GO_SUCCESS"
+ } else {
+ newItemStatusId = "ITEM_CANCELLED"
+ groupOrderStatusId = "GO_CANCELLED"
+ }
+ Map updateProductGroupOrderMap = [:]
+ updateProductGroupOrderMap.groupOrderId = productGroupOrder.groupOrderId
+ updateProductGroupOrderMap.statusId = groupOrderStatusId
+ run service: "updateProductGroupOrder", with: updateProductGroupOrderMap
+
+ List orderItemGroupOrders = from("OrderItemGroupOrder")
+ .where("groupOrderId", productGroupOrder.groupOrderId)
+ .queryList()
+ for(GenericValue orderItemGroupOrder : orderItemGroupOrders) {
+ Map changeOrderItemStatusMap = ["orderId": orderItemGroupOrder.orderId, "orderItemSeqId": orderItemGroupOrder.orderItemSeqId, "statusId": newItemStatusId]
+ run service: "changeOrderItemStatus", with: changeOrderItemStatusMap
+ }
+ return success()
+ }
+}
+
diff --git a/applications/product/minilang/product/product/ProductServices.xml b/applications/product/minilang/product/product/ProductServices.xml
deleted file mode 100644
index b331b32..0000000
--- a/applications/product/minilang/product/product/ProductServices.xml
+++ /dev/null
@@ -1,1051 +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="createProduct" short-description="Create a Product">
- <check-permission permission="CATALOG" action="_CREATE">
- <alt-permission permission="CATALOG_ROLE" action="_CREATE"/>
- <fail-property resource="ProductUiLabels" property="ProductCatalogCreatePermissionError"/>
- </check-permission>
- <check-errors/>
-
- <make-value entity-name="Product" value-field="newEntity"/>
- <set-nonpk-fields map="parameters" value-field="newEntity"/>
-
- <set from-field="parameters.productId" field="newEntity.productId"/>
- <if-empty field="newEntity.productId">
- <sequenced-id sequence-name="Product" field="newEntity.productId"/>
- <else>
- <check-id field="newEntity.productId"/>
- <check-errors />
- <entity-one entity-name="Product" value-field="dummyProduct"><field-map field-name="productId" from-field="parameters.productId"/></entity-one>
- <if-not-empty field="dummyProduct">
- <add-error ><fail-property resource="CommonErrorUiLabels" property="CommonErrorDuplicateKey" /></add-error>
- </if-not-empty>
- <check-errors />
- </else>
- </if-empty>
- <field-to-result field="newEntity.productId" result-name="productId"/>
-
- <now-timestamp field="nowTimestamp"/>
- <set from-field="nowTimestamp" field="newEntity.createdDate"/>
- <set from-field="nowTimestamp" field="newEntity.lastModifiedDate"/>
- <set from-field="userLogin.userLoginId" field="newEntity.lastModifiedByUserLogin"/>
- <set from-field="userLogin.userLoginId" field="newEntity.createdByUserLogin"/>
- <if-empty field="newEntity.isVariant">
- <set field="newEntity.isVariant" value="N"/>
- </if-empty>
- <if-empty field="newEntity.isVirtual">
- <set field="newEntity.isVirtual" value="N"/>
- </if-empty>
- <if-empty field="newEntity.billOfMaterialLevel">
- <set field="newEntity.billOfMaterialLevel" value="0" type="Long"/>
- </if-empty>
-
- <create-value value-field="newEntity"/>
-
- <!-- if setting the primaryProductCategoryId create a member entity too -->
- <!-- THIS IS REMOVED BECAUSE IT CAUSES PROBLEMS FOR WORKING ON PRODUCTION SITES
- <if-not-empty field="newEntity.primaryProductCategoryId">
- <make-value entity-name="ProductCategoryMember" value-field="newMember"/>
- <set from-field="productId" map-name="newEntity" to-field-name="productId" to-map-name="newMember"/>
- <set from-field="primaryProductCategoryId" map-name="newEntity" to-field-name="productCategoryId" to-map-name="newMember"/>
- <now-timestamp field="nowStamp"/>
- <set from-field="nowStamp" field="newMember.fromDate"/>
- <create-value value-field="newMember"/>
- </if-not-empty>
- -->
-
- <!-- if the user has the role limited position, add this product to the limit category/ies -->
- <if-has-permission permission="CATALOG_ROLE" action="_CREATE">
- <entity-and entity-name="ProductCategoryRole" list="productCategoryRoles" filter-by-date="true">
- <field-map field-name="partyId" from-field="userLogin.partyId"/>
- <field-map field-name="roleTypeId" value="LTD_ADMIN"/>
- </entity-and>
-
- <iterate list="productCategoryRoles" entry="productCategoryRole">
- <!-- add this new product to the category -->
- <make-value entity-name="ProductCategoryMember" value-field="newLimitMember"/>
- <set from-field="newEntity.productId" field="newLimitMember.productId"/>
- <set from-field="productCategoryRole.productCategoryId" field="newLimitMember.productCategoryId"/>
- <set from-field="nowTimestamp" field="newLimitMember.fromDate"/>
- <create-value value-field="newLimitMember"/>
- </iterate>
- </if-has-permission>
- </simple-method>
- <simple-method method-name="updateProduct" short-description="Update a Product">
- <set value="updateProduct" field="callingMethodName"/>
- <set value="UPDATE" field="checkAction"/>
- <call-simple-method method-name="checkProductRelatedPermission"/>
- <check-errors/>
-
- <entity-one entity-name="Product" value-field="lookedUpValue"/>
- <!-- save this value before overwriting it so we can compare it later -->
- <set from-field="lookedUpValue.primaryProductCategoryId" field="saveIdMap.primaryProductCategoryId"/>
- <set-nonpk-fields map="parameters" value-field="lookedUpValue"/>
-
- <now-timestamp field="lookedUpValue.lastModifiedDate"/>
- <set from-field="userLogin.userLoginId" field="lookedUpValue.lastModifiedByUserLogin"/>
-
- <store-value value-field="lookedUpValue"/>
-
- <!-- if setting the primaryParentCategoryId, create a rollup entity too -->
- <!-- THIS IS REMOVED BECAUSE IT CAUSES PROBLEMS FOR WORKING ON PRODUCTION SITES
- <if-not-empty field="lookedUpValue.primaryProductCategoryId">
- <if-compare-field to-field="saveIdMap.primaryProductCategoryId" field="lookedUpValue.primaryProductCategoryId" operator="equals">
- <make-value entity-name="ProductCategoryMember" value-field="newMember"/>
- <set from-field="productId" map-name="newEntity" to-field-name="productId" to-map-name="newMember"/>
- <set from-field="primaryProductCategoryId" map-name="newEntity" to-field-name="productCategoryId" to-map-name="newMember"/>
- <now-timestamp field="newMember.fromDate"/>
- <create-value value-field="newMember"/>
- </if-compare-field>
- </if-not-empty>
- -->
- </simple-method>
-
- <!-- update the name of a product - handles real , virtual and variant products -->
- <simple-method method-name="updateProductQuickAdminName" short-description="Update a Product Name from quick admin">
- <set value="updateProductQuickAdminName" field="callingMethodName"/>
- <set value="UPDATE" field="checkAction"/>
- <call-simple-method method-name="checkProductRelatedPermission"/>
- <check-errors/>
-
- <entity-one entity-name="Product" value-field="lookedUpValue"/>
- <set from-field="parameters.productName" field="lookedUpValue.productName"/>
- <if-compare field="lookedUpValue.isVirtual" operator="equals" value="Y">
- <set from-field="lookedUpValue.productName" field="lookedUpValue.internalName"/>
- </if-compare>
-
- <now-timestamp field="lookedUpValue.lastModifiedDate"/>
- <set from-field="userLogin.userLoginId" field="lookedUpValue.lastModifiedByUserLogin"/>
-
- <store-value value-field="lookedUpValue"/>
-
- <if-compare field="lookedUpValue.isVirtual" operator="equals" value="Y">
- <!-- get all variant products, to update their productNames -->
- <set from-field="parameters.productId" field="variantProductAssocMap.productId"/>
- <set value="PRODUCT_VARIANT" field="variantProductAssocMap.productAssocTypeId"/>
-
- <!-- get all productAssocs, then get the actual product to update -->
- <find-by-and entity-name="ProductAssoc" map="variantProductAssocMap" list="variantProductAssocs"/>
- <filter-list-by-date list="variantProductAssocs"/>
- <iterate list="variantProductAssocs" entry="variantProductAssoc">
- <clear-field field="variantProduct"/>
- <entity-one entity-name="Product" value-field="variantProduct" auto-field-map="false">
- <field-map field-name="productId" from-field="variantProductAssoc.productIdTo"/>
- </entity-one>
-
- <set from-field="parameters.productName" field="variantProduct.productName"/>
- <now-timestamp field="variantProduct.lastModifiedDate"/>
- <set from-field="userLogin.userLoginId" field="variantProduct.lastModifiedByUserLogin"/>
- <store-value value-field="variantProduct"/>
- </iterate>
- </if-compare>
- </simple-method>
-
- <simple-method method-name="duplicateProduct" short-description="Duplicate a Product">
- <set value="duplicateProduct" field="callingMethodName"/>
- <set value="CREATE" field="checkAction"/>
- <call-simple-method method-name="checkProductRelatedPermission"/>
- <set value="DELETE" field="checkAction"/>
- <call-simple-method method-name="checkProductRelatedPermission"/>
- <entity-one entity-name="Product" value-field="dummyProduct">
- <field-map field-name="productId" from-field="parameters.productId"/>
- </entity-one>
- <if-not-empty field="dummyProduct">
- <add-error ><fail-property resource="CommonErrorUiLabels" property="CommonErrorDuplicateKey" /></add-error>
- </if-not-empty>
- <check-errors/>
-
- <!-- look up the old product and clone it -->
- <entity-one entity-name="Product" value-field="oldProduct" auto-field-map="false">
- <field-map field-name="productId" from-field="parameters.oldProductId"/>
- </entity-one>
- <clone-value value-field="oldProduct" new-value-field="newProduct"/>
-
- <!-- set the productId, and write it to the datasource -->
- <set from-field="parameters.productId" field="newProduct.productId"/>
-
- <!-- if requested, set the new internalName field -->
- <if-not-empty field="parameters.newInternalName">
- <set from-field="parameters.newInternalName" field="newProduct.internalName"/>
- </if-not-empty>
-
- <!-- if requested, set the new productName field -->
- <if-not-empty field="parameters.newProductName">
- <set from-field="parameters.newProductName" field="newProduct.productName"/>
- </if-not-empty>
-
- <!-- if requested, set the new description field -->
- <if-not-empty field="parameters.newDescription">
- <set from-field="parameters.newDescription" field="newProduct.description"/>
- </if-not-empty>
-
- <!-- if requested, set the new longDescription field -->
- <if-not-empty field="parameters.newLongDescription">
- <set from-field="parameters.newLongDescription" field="newProduct.longDescription"/>
- </if-not-empty>
-
- <create-value value-field="newProduct"/>
-
- <!-- set up entity filter -->
- <set field="productFindContext.productId" from-field="parameters.oldProductId"/>
- <set field="reverseProductFindContext.productIdTo" from-field="parameters.oldProductId"/>
-
- <!-- if requested, duplicate related data as well -->
- <if-not-empty field="parameters.duplicatePrices">
- <find-by-and entity-name="ProductPrice" map="productFindContext" list="foundValues"/>
- <iterate list="foundValues" entry="foundValue">
- <clone-value value-field="foundValue" new-value-field="newTempValue"/>
- <set from-field="parameters.productId" field="newTempValue.productId"/>
- <create-value value-field="newTempValue"/>
- </iterate>
- </if-not-empty>
- <if-not-empty field="parameters.duplicateIDs">
- <find-by-and entity-name="GoodIdentification" map="productFindContext" list="foundValues"/>
- <iterate list="foundValues" entry="foundValue">
- <clone-value value-field="foundValue" new-value-field="newTempValue"/>
- <set from-field="parameters.productId" field="newTempValue.productId"/>
- <create-value value-field="newTempValue"/>
- </iterate>
- </if-not-empty>
- <if-not-empty field="parameters.duplicateContent">
- <find-by-and entity-name="ProductContent" map="productFindContext" list="foundValues"/>
- <iterate list="foundValues" entry="foundValue">
- <clone-value value-field="foundValue" new-value-field="newTempValue"/>
- <set from-field="parameters.productId" field="newTempValue.productId"/>
- <create-value value-field="newTempValue"/>
- </iterate>
- </if-not-empty>
- <if-not-empty field="parameters.duplicateCategoryMembers">
- <find-by-and entity-name="ProductCategoryMember" map="productFindContext" list="foundValues"/>
- <iterate list="foundValues" entry="foundValue">
- <clone-value value-field="foundValue" new-value-field="newTempValue"/>
- <set from-field="parameters.productId" field="newTempValue.productId"/>
- <!-- Clone Content -->
- <sequenced-id sequence-name="Content" field="newContentId"/>
- <entity-one entity-name="Content" value-field="oldContent" use-cache="false">
- <field-map field-name="contentId" value="${newTempValue.contentId}"/>
- </entity-one>
- <if-not-empty field="oldContent">
- <clone-value value-field="oldContent" new-value-field="clonedContent"/>
- <set from-field="newContentId" field="clonedContent.contentId"/>
- <set from-field="newContentId" field="newTempValue.contentId"/>
- <!-- Clone DataResource -->
- <entity-one entity-name="DataResource" value-field="oldDataResource" use-cache="false">
- <field-map field-name="dataResourceId" value="${clonedContent.dataResourceId}"/>
- </entity-one>
- <if-not-empty field="oldDataResource">
- <sequenced-id sequence-name="DataResource" field="newDataResourceId"/>
- <clone-value new-value-field="clonedDataresource" value-field="oldDataResource"/>
- <set from-field="newDataResourceId" field="clonedDataresource.dataResourceId"/>
- <!-- set new data resource id in cloned content -->
- <set from-field="newDataResourceId" field="clonedContent.dataResourceId"/>
- <create-value value-field="clonedDataresource"/>
- <!-- Clone Electronic Text if exists -->
- <get-related-one value-field="oldDataResource" relation-name="ElectronicText" to-value-field="oldElectronicText"/>
- <if-not-empty field="oldElectronicText">
- <clone-value value-field="oldElectronicText" new-value-field="clonedElectronicText"/>
- <set from-field="newDataResourceId" field="clonedElectronicText.dataResourceId"/>
- <create-value value-field="clonedElectronicText"/>
- </if-not-empty>
- </if-not-empty>
- <create-value value-field="clonedContent"/>
- </if-not-empty>
- <!-- End Clone Contet -->
- <create-value value-field="newTempValue"/>
- </iterate>
- </if-not-empty>
- <if-not-empty field="parameters.duplicateAssocs">
- <find-by-and entity-name="ProductAssoc" map="productFindContext" list="foundValues"/>
- <iterate list="foundValues" entry="foundValue">
- <clone-value value-field="foundValue" new-value-field="newTempValue"/>
- <set from-field="parameters.productId" field="newTempValue.productId"/>
- <create-value value-field="newTempValue"/>
- </iterate>
-
- <!-- small difference here, also do the reverse assocs... -->
- <entity-and entity-name="ProductAssoc" list="foundValues">
- <field-map field-name="productIdTo" from-field="parameters.oldProductId"/>
- </entity-and>
- <iterate list="foundValues" entry="foundValue">
- <clone-value value-field="foundValue" new-value-field="newTempValue"/>
- <set from-field="parameters.productId" field="newTempValue.productIdTo"/>
- <create-value value-field="newTempValue"/>
- </iterate>
- </if-not-empty>
- <if-not-empty field="parameters.duplicateAttributes">
- <find-by-and entity-name="ProductAttribute" map="productFindContext" list="foundValues"/>
- <iterate list="foundValues" entry="foundValue">
- <clone-value value-field="foundValue" new-value-field="newTempValue"/>
- <set from-field="parameters.productId" field="newTempValue.productId"/>
- <create-value value-field="newTempValue"/>
- </iterate>
- </if-not-empty>
- <if-not-empty field="parameters.duplicateFeatureAppls">
- <find-by-and entity-name="ProductFeatureAppl" map="productFindContext" list="foundValues"/>
- <iterate list="foundValues" entry="foundValue">
- <clone-value value-field="foundValue" new-value-field="newTempValue"/>
- <set from-field="parameters.productId" field="newTempValue.productId"/>
- <create-value value-field="newTempValue"/>
- </iterate>
- </if-not-empty>
- <if-not-empty field="parameters.duplicateInventoryItems">
- <find-by-and entity-name="InventoryItem" map="productFindContext" list="foundValues"/>
- <iterate list="foundValues" entry="foundValue">
- <!--
- NOTE: new inventory items should always be created calling the
- createInventoryItem service because in this way we are sure
- that all the relevant fields are filled with default values.
- However, the code here should work fine because all the values
- for the new inventory item are inerited from the existing item.
- TODO: is this code correct? What is the meaning of duplicating inventory items?
- What about the InventoryItemDetail entries?
- -->
- <clone-value value-field="foundValue" new-value-field="newTempValue"/>
- <set from-field="parameters.productId" field="newTempValue.productId"/>
- <!-- this one is slightly different because it needs a new sequenced inventoryItemId -->
- <sequenced-id sequence-name="InventoryItem" field="newTempValue.inventoryItemId"/>
- <create-value value-field="newTempValue"/>
- </iterate>
- </if-not-empty>
-
- <!-- if requested, remove related data as well -->
- <if-not-empty field="parameters.removePrices">
- <remove-by-and entity-name="ProductPrice" map="productFindContext"/>
- </if-not-empty>
- <if-not-empty field="parameters.removeIDs">
- <remove-by-and entity-name="GoodIdentification" map="productFindContext"/>
- </if-not-empty>
- <if-not-empty field="parameters.removeContent">
- <remove-by-and entity-name="ProductContent" map="productFindContext"/>
- </if-not-empty>
- <if-not-empty field="parameters.removeCategoryMembers">
- <remove-by-and entity-name="ProductCategoryMember" map="productFindContext"/>
- </if-not-empty>
- <if-not-empty field="parameters.removeAssocs">
- <remove-by-and entity-name="ProductAssoc" map="productFindContext"/>
- <!-- small difference here, also do the reverse assocs... -->
- <remove-by-and entity-name="ProductAssoc" map="reverseProductFindContext"/>
- </if-not-empty>
- <if-not-empty field="parameters.removeAttributes">
- <remove-by-and entity-name="ProductAttribute" map="productFindContext"/>
- </if-not-empty>
- <if-not-empty field="parameters.removeFeatureAppls">
- <remove-by-and entity-name="ProductFeatureAppl" map="productFindContext"/>
- </if-not-empty>
- <if-not-empty field="parameters.removeInventoryItems">
- <remove-by-and entity-name="InventoryItem" map="productFindContext"/>
- </if-not-empty>
- </simple-method>
-
- <!-- Product Keyword Services -->
- <simple-method method-name="forceIndexProductKeywords" short-description="induce all the keywords of a product">
- <entity-one entity-name="Product" value-field="product"/>
- <call-class-method class-name="org.apache.ofbiz.product.product.KeywordIndex" method-name="forceIndexKeywords">
- <field field="product" type="org.apache.ofbiz.entity.GenericValue"/>
- </call-class-method>
- </simple-method>
- <simple-method method-name="deleteProductKeywords" short-description="delete all the keywords of a product">
- <entity-one entity-name="Product" value-field="product"/>
- <remove-related value-field="product" relation-name="ProductKeyword"/>
- </simple-method>
-
- <simple-method method-name="indexProductKeywords" short-description="Index the Keywords for a Product" login-required="false">
- <!-- this service is meant to be called from an entity ECA for entities that include a productId -->
- <!-- if it is the Product entity itself triggering this action, then a [productInstance] parameter
- will be passed and we can save a few cycles looking that up -->
- <set from-field="parameters.productInstance" field="productInstance"/>
- <if-empty field="productInstance">
- <set from-field="parameters.productId" field="findProductMap.productId"/>
- <find-by-primary-key entity-name="Product" map="findProductMap" value-field="productInstance"/>
- </if-empty>
-
- <!-- induce keywords if autoCreateKeywords is emtpy or Y-->
- <if>
- <condition>
- <or>
- <if-empty field="productInstance.autoCreateKeywords"/>
- <if-compare field="productInstance.autoCreateKeywords" operator="equals" value="Y"/>
- </or>
- </condition>
- <then>
- <call-class-method class-name="org.apache.ofbiz.product.product.KeywordIndex" method-name="indexKeywords">
- <field field="productInstance" type="org.apache.ofbiz.entity.GenericValue"/>
- </call-class-method>
- </then>
- </if>
- </simple-method>
-
- <simple-method method-name="discontinueProductSales" short-description="Discontinue Product Sales" login-required="false">
- <!-- set sales discontinuation date to now -->
- <now-timestamp field="nowTimestamp"/>
- <entity-one entity-name="Product" value-field="product"/>
- <set from-field="nowTimestamp" field="product.salesDiscontinuationDate"/>
- <store-value value-field="product"/>
- <!-- expire product from all categories -->
- <get-related value-field="product" relation-name="ProductCategoryMember" list="productCategoryMembers"/>
- <iterate list="productCategoryMembers" entry="productCategoryMember">
- <if-empty field="productCategoryMember.thruDate">
- <set from-field="nowTimestamp" field="productCategoryMember.thruDate"/>
- <store-value value-field="productCategoryMember"/>
- </if-empty>
- </iterate>
- <!-- expire product from all associations going to it -->
- <get-related value-field="product" relation-name="AssocProductAssoc" list="assocProductAssocs"/>
- <iterate list="assocProductAssocs" entry="assocProductAssoc">
- <if-empty field="assocProductAssoc.thruDate">
- <set from-field="nowTimestamp" field="assocProductAssoc.thruDate"/>
- <store-value value-field="assocProductAssoc"/>
- </if-empty>
- </iterate>
- </simple-method>
-
- <simple-method method-name="countProductView" short-description="Count Product View" login-required="false">
- <if-empty field="parameters.weight">
- <calculate field="parameters.weight" type="Long"><number value="1"/></calculate>
- </if-empty>
- <entity-one entity-name="ProductCalculatedInfo" value-field="productCalculatedInfo"/>
- <if-empty field="productCalculatedInfo">
- <!-- go ahead and create it -->
- <make-value entity-name="ProductCalculatedInfo" value-field="productCalculatedInfo"/>
- <set from-field="parameters.productId" field="productCalculatedInfo.productId"/>
- <set from-field="parameters.weight" field="productCalculatedInfo.totalTimesViewed"/>
- <create-value value-field="productCalculatedInfo"/>
- <else>
- <calculate field="productCalculatedInfo.totalTimesViewed" type="Long">
- <calcop operator="add" field="productCalculatedInfo.totalTimesViewed">
- <calcop operator="get" field="parameters.weight"></calcop>
- </calcop>
- </calculate>
- <store-value value-field="productCalculatedInfo"/>
- </else>
- </if-empty>
-
- <!-- do the same for the virtual product... -->
- <entity-one entity-name="Product" value-field="product" use-cache="true"/>
- <call-class-method class-name="org.apache.ofbiz.product.product.ProductWorker" method-name="getVariantVirtualId" ret-field="virtualProductId">
- <field field="product" type="GenericValue"/>
- </call-class-method>
- <if-not-empty field="virtualProductId">
- <set from-field="virtualProductId" field="callSubMap.productId"/>
- <set from-field="parameters.weight" field="callSubMap.weight"/>
- <call-service service-name="countProductView" in-map-name="callSubMap"></call-service>
- </if-not-empty>
- </simple-method>
-
- <simple-method method-name="createProductReview" short-description="Create a ProductReview" login-required="false">
- <make-value entity-name="ProductReview" value-field="newEntity"/>
- <set-nonpk-fields map="parameters" value-field="newEntity"/>
- <set from-field="userLogin.userLoginId" field="newEntity.userLoginId"/>
- <set value="PRR_PENDING" field="newEntity.statusId"/>
-
- <!-- code to check for auto-approved reviews (store setting) -->
- <entity-one entity-name="ProductStore" value-field="productStore"/>
-
- <if-not-empty field="productStore">
- <if-compare field="productStore.autoApproveReviews" operator="equals" value="Y">
- <set value="PRR_APPROVED" field="newEntity.statusId"/>
- </if-compare>
- </if-not-empty>
-
- <!-- auto approve the review if it is just a rating and has no review text -->
- <if-empty field="parameters.productReview">
- <set value="PRR_APPROVED" field="newEntity.statusId"/>
- </if-empty>
-
- <!-- create the new ProductReview -->
- <sequenced-id sequence-name="ProductReview" field="newEntity.productReviewId"/>
- <field-to-result field="newEntity.productReviewId" result-name="productReviewId"/>
-
- <if-empty field="newEntity.postedDateTime">
- <now-timestamp field="newEntity.postedDateTime"/>
- </if-empty>
-
- <create-value value-field="newEntity"/>
-
- <set from-field="newEntity.productId" field="productId"/>
- <property-to-field resource="ProductUiLabels" property="ProductCreateProductReviewSuccess" field="successMessage"/>
- <call-simple-method method-name="updateProductWithReviewRatingAvg"/>
- </simple-method>
- <simple-method method-name="updateProductReview" short-description="Update ProductReview">
- <set value="updateProductReview" field="callingMethodName"/>
- <set value="UPDATE" field="checkAction"/>
- <call-simple-method method-name="checkProductRelatedPermission"/>
- <check-errors/>
-
- <make-value entity-name="ProductReview" value-field="lookupPKMap"/>
- <set-pk-fields map="parameters" value-field="lookupPKMap"/>
- <find-by-primary-key map="lookupPKMap" value-field="lookedUpValue"/>
- <set-nonpk-fields map="parameters" value-field="lookedUpValue"/>
- <store-value value-field="lookedUpValue"/>
-
- <set from-field="lookedUpValue.productId" field="productId"/>
- <call-simple-method method-name="updateProductWithReviewRatingAvg"/>
- </simple-method>
- <simple-method method-name="updateProductWithReviewRatingAvg" short-description="Update Product with new Review Rating Avg" login-required="false">
- <!-- this method is meant to be called in-line and depends in a productId parameter -->
- <call-class-method class-name="org.apache.ofbiz.product.product.ProductWorker" method-name="getAverageProductRating" ret-field="averageCustomerRating">
- <field field="delegator" type="org.apache.ofbiz.entity.Delegator"/>
- <field field="productId" type="java.lang.String"/>
- </call-class-method>
- <log level="info" message="Got new average customer rating ${averageCustomerRating}"/>
- <if-compare field="averageCustomerRating" operator="equals" value="0" type="BigDecimal">
- <return/>
- </if-compare>
- <!-- update the review average on the ProductCalculatedInfo entity -->
- <entity-one entity-name="ProductCalculatedInfo" value-field="productCalculatedInfo"/>
- <if-empty field="productCalculatedInfo">
- <!-- go ahead and create it -->
- <make-value entity-name="ProductCalculatedInfo" value-field="productCalculatedInfo"/>
- <set from-field="productId" field="productCalculatedInfo.productId"/>
- <set from-field="averageCustomerRating" field="productCalculatedInfo.averageCustomerRating"/>
- <create-value value-field="productCalculatedInfo"/>
- <else>
- <set from-field="averageCustomerRating" field="productCalculatedInfo.averageCustomerRating"/>
- <store-value value-field="productCalculatedInfo"/>
- </else>
- </if-empty>
- </simple-method>
- <simple-method method-name="copyToProductVariants" short-description="Updates the Product's Variants">
- <set value="copyToProductVariants" field="callingMethodName"/>
- <set value="CREATE" field="checkAction"/>
- <call-simple-method method-name="checkProductRelatedPermission"/>
- <set value="DELETE" field="checkAction"/>
- <call-simple-method method-name="checkProductRelatedPermission"/>
- <check-errors/>
-
- <set from-field="parameters.virtualProductId" field="productFindContext.productId"/>
- <find-by-primary-key entity-name="Product" map="productFindContext" value-field="oldProduct"/>
-
- <set from-field="parameters.virtualProductId" field="variantsFindContext.productId"/>
- <set value="PRODUCT_VARIANT" field="variantsFindContext.productAssocTypeId"/>
- <find-by-and entity-name="ProductAssoc" map="variantsFindContext" list="variants"/>
- <filter-list-by-date list="variants"/>
- <iterate list="variants" entry="newProduct">
- <set from-field="newProduct.productIdTo" field="productVariantContext.productId"/>
- <!-- if requested, duplicate related data -->
- <if-not-empty field="parameters.duplicatePrices">
- <if-not-empty field="parameters.removeBefore">
- <find-by-and entity-name="ProductPrice" map="productVariantContext" list="foundVariantValues"/>
- <iterate list="foundVariantValues" entry="foundVariantValue">
- <remove-value value-field="foundVariantValue"/>
- </iterate>
- </if-not-empty>
- <find-by-and entity-name="ProductPrice" map="productFindContext" list="foundValues"/>
- <iterate list="foundValues" entry="foundValue">
- <clone-value value-field="foundValue" new-value-field="newTempValue"/>
- <set from-field="newProduct.productIdTo" field="newTempValue.productId"/>
- <create-value value-field="newTempValue"/>
- </iterate>
- </if-not-empty>
- <if-not-empty field="parameters.duplicateIDs">
- <if-not-empty field="parameters.removeBefore">
- <find-by-and entity-name="GoodIdentification" map="productVariantContext" list="foundVariantValues"/>
- <iterate list="foundVariantValues" entry="foundVariantValue">
- <remove-value value-field="foundVariantValue"/>
- </iterate>
- </if-not-empty>
- <find-by-and entity-name="GoodIdentification" map="productFindContext" list="foundValues"/>
- <iterate list="foundValues" entry="foundValue">
- <clone-value value-field="foundValue" new-value-field="newTempValue"/>
- <set from-field="newProduct.productIdTo" field="newTempValue.productId"/>
- <create-value value-field="newTempValue"/>
- </iterate>
- </if-not-empty>
- <if-not-empty field="parameters.duplicateContent">
- <if-not-empty field="parameters.removeBefore">
- <find-by-and entity-name="ProductContent" map="productVariantContext" list="foundVariantValues"/>
- <iterate list="foundVariantValues" entry="foundVariantValue">
- <remove-value value-field="foundVariantValue"/>
- </iterate>
- </if-not-empty>
- <find-by-and entity-name="ProductContent" map="productFindContext" list="foundValues"/>
- <iterate list="foundValues" entry="foundValue">
- <clone-value value-field="foundValue" new-value-field="newTempValue"/>
- <set from-field="newProduct.productIdTo" field="newTempValue.productId"/>
- <create-value value-field="newTempValue"/>
- </iterate>
- </if-not-empty>
- <if-not-empty field="parameters.duplicateCategoryMembers">
- <if-not-empty field="parameters.removeBefore">
- <find-by-and entity-name="ProductCategoryMember" map="productVariantContext" list="foundVariantValues"/>
- <iterate list="foundVariantValues" entry="foundVariantValue">
- <remove-value value-field="foundVariantValue"/>
- </iterate>
- </if-not-empty>
- <find-by-and entity-name="ProductCategoryMember" map="productFindContext" list="foundValues"/>
- <iterate list="foundValues" entry="foundValue">
- <clone-value value-field="foundValue" new-value-field="newTempValue"/>
- <set from-field="newProduct.productIdTo" field="newTempValue.productId"/>
- <create-value value-field="newTempValue"/>
- </iterate>
- </if-not-empty>
- <if-not-empty field="parameters.duplicateAttributes">
- <if-not-empty field="parameters.removeBefore">
- <find-by-and entity-name="ProductAttribute" map="productVariantContext" list="foundVariantValues"/>
- <iterate list="foundVariantValues" entry="foundVariantValue">
- <remove-value value-field="foundVariantValue"/>
- </iterate>
- </if-not-empty>
- <find-by-and entity-name="ProductAttribute" map="productFindContext" list="foundValues"/>
- <iterate list="foundValues" entry="foundValue">
- <clone-value value-field="foundValue" new-value-field="newTempValue"/>
- <set from-field="newProduct.productIdTo" field="newTempValue.productId"/>
- <create-value value-field="newTempValue"/>
- </iterate>
- </if-not-empty>
- <if-not-empty field="parameters.duplicateFacilities">
- <if-not-empty field="parameters.removeBefore">
- <find-by-and entity-name="ProductFacility" map="productVariantContext" list="foundVariantValues"/>
- <iterate list="foundVariantValues" entry="foundVariantValue">
- <remove-value value-field="foundVariantValue"/>
- </iterate>
- </if-not-empty>
- <find-by-and entity-name="ProductFacility" map="productFindContext" list="foundValues"/>
- <iterate list="foundValues" entry="foundValue">
- <clone-value value-field="foundValue" new-value-field="newTempValue"/>
- <set from-field="newProduct.productIdTo" field="newTempValue.productId"/>
- <create-value value-field="newTempValue"/>
- </iterate>
- </if-not-empty>
- <if-not-empty field="parameters.duplicateLocations">
- <if-not-empty field="parameters.removeBefore">
- <find-by-and entity-name="ProductFacilityLocation" map="productVariantContext" list="foundVariantValues"/>
- <iterate list="foundVariantValues" entry="foundVariantValue">
- <remove-value value-field="foundVariantValue"/>
- </iterate>
- </if-not-empty>
- <find-by-and entity-name="ProductFacilityLocation" map="productFindContext" list="foundValues"/>
- <iterate list="foundValues" entry="foundValue">
- <clone-value value-field="foundValue" new-value-field="newTempValue"/>
- <set from-field="newProduct.productIdTo" field="newTempValue.productId"/>
- <create-value value-field="newTempValue"/>
- </iterate>
- </if-not-empty>
- </iterate>
- </simple-method>
-
- <!-- a method to centralize product security code, meant to be called in-line with
- call-simple-method, and the checkAction and callingMethodName attributes should be in the method context -->
- <simple-method method-name="checkProductRelatedPermission" short-description="Check Product Related Permission">
- <if-empty field="callingMethodName">
- <property-to-field resource="CommonUiLabels" property="CommonPermissionThisOperation" field="callingMethodName"/>
- </if-empty>
- <if-empty field="checkAction">
- <set value="UPDATE" field="checkAction"/>
- </if-empty>
-
- <!-- find all role-categories that this product is a member of -->
- <if>
- <condition>
- <not><if-has-permission permission="CATALOG" action="_${checkAction}"/></not>
- </condition>
- <then>
- <set from-field="parameters.productId" field="lookupRoleCategoriesMap.productId"/>
- <set from-field="userLogin.partyId" field="lookupRoleCategoriesMap.partyId"/>
- <set value="LTD_ADMIN" field="lookupRoleCategoriesMap.roleTypeId"/>
- <find-by-and entity-name="ProductCategoryMemberAndRole" map="lookupRoleCategoriesMap" list="roleCategories"/>
- <filter-list-by-date list="roleCategories"/>
- <filter-list-by-date list="roleCategories" from-field-name="roleFromDate" thru-field-name="roleThruDate"/>
- </then>
- </if>
- <if>
- <condition>
- <not>
- <or>
- <if-has-permission permission="CATALOG" action="_${checkAction}"/>
- <and>
- <if-has-permission permission="CATALOG_ROLE" action="_${checkAction}"/>
- <not><if-empty field="roleCategories"/></not>
- </and>
- <and>
- <not><if-empty field="alternatePermissionRoot"/></not>
- <if-has-permission permission="${alternatePermissionRoot}" action="_${checkAction}"/>
- </and>
- </or>
- </not>
- </condition>
- <then>
- <set field="checkActionLabel" value="${groovy: 'ProductCatalog' + checkAction.charAt(0) + checkAction.substring(1).toLowerCase() + 'PermissionError'}"/>
- <set field="resourceDescription" from-field="callingMethodName"/>
- <add-error>
- <fail-property resource="ProductUiLabels" property="${checkActionLabel}"/>
- </add-error>
- </then>
- </if>
- </simple-method>
- <simple-method method-name="productGenericPermission" short-description="Main permission logic">
- <set field="mainAction" from-field="parameters.mainAction"/>
- <if-empty field="mainAction">
- <add-error>
- <fail-property resource="ProductUiLabels" property="ProductMissingMainActionInPermissionService"/>
- </add-error>
- <check-errors/>
- </if-empty>
-
- <set field="callingMethodName" from-field="parameters.resourceDescription"/>
- <set field="checkAction" from-field="parameters.mainAction"/>
- <call-simple-method method-name="checkProductRelatedPermission"/>
-
- <if-empty field="error_list">
- <set field="hasPermission" type="Boolean" value="true"/>
- <field-to-result field="hasPermission"/>
-
- <else>
- <property-to-field resource="ProductUiLabels" property="ProductPermissionError" field="failMessage"/>
- <set field="hasPermission" type="Boolean" value="false"/>
- <field-to-result field="hasPermission"/>
- <field-to-result field="failMessage"/>
- </else>
- </if-empty>
- </simple-method>
- <simple-method method-name="productPriceGenericPermission" short-description="product price permission logic">
- <set field="mainAction" from-field="parameters.mainAction"/>
- <if-empty field="mainAction">
- <add-error>
- <fail-property resource="ProductUiLabels" property="ProductMissingMainActionInPermissionService"/>
- </add-error>
- <check-errors/>
- </if-empty>
- <check-permission permission="CATALOG_PRICE_MAINT">
- <fail-property resource="ProductUiLabels" property="ProductPriceMaintPermissionError"/>
- </check-permission>
- <call-simple-method method-name="checkProductRelatedPermission"/>
- <if-empty field="error_list">
- <set field="hasPermission" type="Boolean" value="true"/>
- <field-to-result field="hasPermission"/>
- <else>
- <property-to-field resource="ProductUiLabels" property="ProductPermissionError" field="failMessage"/>
- <set field="hasPermission" type="Boolean" value="false"/>
- <field-to-result field="hasPermission"/>
- <field-to-result field="failMessage"/>
- </else>
- </if-empty>
- </simple-method>
-
- <!-- ================================================================ -->
- <!-- ProductRole Services -->
- <!-- ================================================================ -->
-
- <simple-method method-name="addPartyToProduct" short-description="Add Party to Product">
- <set value="addPartyToProduct" field="callingMethodName"/>
- <set value="CREATE" field="checkAction"/>
- <call-simple-method method-name="checkProductRelatedPermission"/>
- <check-errors/>
-
- <make-value entity-name="ProductRole" value-field="newEntity"/>
- <set-pk-fields map="parameters" value-field="newEntity"/>
- <set-nonpk-fields map="parameters" value-field="newEntity"/>
-
- <if-empty field="newEntity.fromDate">
- <now-timestamp field="newEntity.fromDate"/>
- </if-empty>
-
- <create-value value-field="newEntity"/>
- </simple-method>
- <simple-method method-name="updatePartyToProduct" short-description="Update Party to Product">
- <set value="updatePartyToProduct" field="callingMethodName"/>
- <set value="UPDATE" field="checkAction"/>
- <call-simple-method method-name="checkProductRelatedPermission"/>
- <check-errors/>
-
- <make-value entity-name="ProductRole" value-field="lookupPKMap"/>
- <set-pk-fields map="parameters" value-field="lookupPKMap"/>
- <find-by-primary-key entity-name="ProductRole" map="lookupPKMap" value-field="lookedUpValue"/>
- <set-nonpk-fields map="parameters" value-field="lookedUpValue"/>
- <store-value value-field="lookedUpValue"/>
- </simple-method>
- <simple-method method-name="removePartyFromProduct" short-description="Remove Party From Product">
- <set value="removePartyFromProduct" field="callingMethodName"/>
- <set value="DELETE" field="checkAction"/>
- <call-simple-method method-name="checkProductRelatedPermission"/>
- <check-errors/>
-
- <make-value entity-name="ProductRole" value-field="lookupPKMap"/>
- <set-pk-fields map="parameters" value-field="lookupPKMap"/>
- <find-by-primary-key entity-name="ProductRole" map="lookupPKMap" value-field="lookedUpValue"/>
- <remove-value value-field="lookedUpValue"/>
- </simple-method>
-
- <!-- ProductCategoryGlAccount methods -->
- <simple-method method-name="createProductCategoryGlAccount" short-description="Create a ProductCategoryGlAccount">
- <set value="createProductCategoryGlAccount" field="callingMethodName"/>
- <set value="CREATE" field="checkAction"/>
- <call-simple-method method-name="checkProductRelatedPermission"/>
- <check-errors/>
-
- <make-value entity-name="ProductCategoryGlAccount" value-field="newEntity"/>
- <set-nonpk-fields map="parameters" value-field="newEntity"/>
- <set-pk-fields map="parameters" value-field="newEntity"/>
- <create-value value-field="newEntity"/>
- </simple-method>
-
- <simple-method method-name="updateProductCategoryGlAccount" short-description="Update a ProductCategoryGlAccount">
- <set value="updateProductCategoryGlAccount" field="callingMethodName"/>
- <set value="UPDATE" field="checkAction"/>
- <call-simple-method method-name="checkProductRelatedPermission"/>
- <check-errors/>
-
- <entity-one entity-name="ProductCategoryGlAccount" value-field="lookedUpValue"/>
- <set-nonpk-fields map="parameters" value-field="lookedUpValue"/>
- <store-value value-field="lookedUpValue"/>
- </simple-method>
-
- <simple-method method-name="deleteProductCategoryGlAccount" short-description="Delete a ProductCategoryGlAccount">
- <set value="deleteProductCategoryGlAccount" field="callingMethodName"/>
- <set value="DELETE" field="checkAction"/>
- <call-simple-method method-name="checkProductRelatedPermission"/>
- <check-errors/>
-
- <entity-one entity-name="ProductCategoryGlAccount" value-field="lookedUpValue"/>
- <remove-value value-field="lookedUpValue"/>
- </simple-method>
-
- <!-- Product GroupOrder Services -->
- <simple-method method-name="createProductGroupOrder" short-description="Create ProductGroupOrder">
- <make-value entity-name="ProductGroupOrder" value-field="newEntity"/>
- <make-next-seq-id value-field="newEntity" seq-field-name="groupOrderId"/>
- <field-to-result field="newEntity.groupOrderId" result-name="groupOrderId"/>
- <set-nonpk-fields map="parameters" value-field="newEntity"/>
- <create-value value-field="newEntity"/>
- </simple-method>
-
- <simple-method method-name="updateProductGroupOrder" short-description="Update ProductGroupOrder">
- <entity-one entity-name="ProductGroupOrder" value-field="productGroupOrder"/>
- <set-nonpk-fields map="parameters" value-field="productGroupOrder"/>
- <store-value value-field="productGroupOrder"/>
-
- <if-compare field="productGroupOrder.statusId" operator="equals" value="GO_CREATED">
- <entity-one entity-name="JobSandbox" value-field="jobSandbox">
- <field-map field-name="jobId" from-field="productGroupOrder.jobId"/>
- </entity-one>
- <if-not-empty field="jobSandbox">
- <set field="jobSandbox.runTime" from-field="parameters.thruDate"/>
- <store-value value-field="jobSandbox"/>
- </if-not-empty>
- </if-compare>
- </simple-method>
-
- <simple-method method-name="deleteProductGroupOrder" short-description="Delete ProductGroupOrder">
- <entity-and entity-name="OrderItemGroupOrder" list="orderItemGroupOrders">
- <field-map field-name="groupOrderId" from-field="parameters.groupOrderId"/>
- </entity-and>
- <iterate list="orderItemGroupOrders" entry="orderItemGroupOrder">
- <remove-value value-field="orderItemGroupOrder"/>
- </iterate>
-
- <entity-one entity-name="ProductGroupOrder" value-field="productGroupOrder"/>
- <remove-value value-field="productGroupOrder"/>
-
- <entity-one entity-name="JobSandbox" value-field="jobSandbox">
- <field-map field-name="jobId" from-field="productGroupOrder.jobId"/>
- </entity-one>
- <remove-value value-field="jobSandbox"/>
-
- <entity-and entity-name="JobSandbox" list="jobSandboxList">
- <field-map field-name="runtimeDataId" from-field="jobSandbox.runtimeDataId"/>
- </entity-and>
- <iterate list="jobSandboxList" entry="jobSandboxRelatedRuntimeData">
- <remove-value value-field="jobSandboxRelatedRuntimeData"/>
- </iterate>
-
- <entity-one entity-name="RuntimeData" value-field="runtimeData">
- <field-map field-name="runtimeDataId" from-field="jobSandbox.runtimeDataId"/>
- </entity-one>
- <remove-value value-field="runtimeData"/>
- </simple-method>
-
- <simple-method method-name="createJobForProductGroupOrder" short-description="Create ProductGroupOrder">
- <entity-one entity-name="ProductGroupOrder" value-field="productGroupOrder"/>
- <if-empty field="productGroupOrder.jobId">
- <!-- Create RuntimeData For ProductGroupOrder -->
- <set field="runtimeDataMap.groupOrderId" from-field="parameters.groupOrderId"/>
- <call-class-method class-name="org.apache.ofbiz.entity.serialize.XmlSerializer" method-name="serialize" ret-field="runtimeInfo">
- <field field="runtimeDataMap" type="Object"/>
- </call-class-method>
- <make-value entity-name="RuntimeData" value-field="runtimeData"/>
- <sequenced-id sequence-name="RuntimeData" field="runtimeData.runtimeDataId"/>
- <set field="runtimeDataId" from-field="runtimeData.runtimeDataId"/>
- <set field="runtimeData.runtimeInfo" from-field="runtimeInfo"/>
- <create-value value-field="runtimeData"/>
-
- <!-- Create Job For ProductGroupOrder -->
- <!-- FIXME: Jobs should not be manually created -->
- <make-value entity-name="JobSandbox" value-field="jobSandbox"/>
- <sequenced-id sequence-name="JobSandbox" field="jobSandbox.jobId"/>
- <set field="jobId" from-field="jobSandbox.jobId"/>
- <set field="jobSandbox.jobName" value="Check ProductGroupOrder Expired"/>
- <set field="jobSandbox.runTime" from-field="parameters.thruDate"/>
- <set field="jobSandbox.poolId" value="pool"/>
- <set field="jobSandbox.statusId" value="SERVICE_PENDING"/>
- <set field="jobSandbox.serviceName" value="checkProductGroupOrderExpired"/>
- <set field="jobSandbox.runAsUser" value="system"/>
- <set field="jobSandbox.runtimeDataId" from-field="runtimeDataId"/>
- <set field="jobSandbox.maxRecurrenceCount" value="1" type="Long"/>
- <set field="jobSandbox.priority" value="50" type="Long"/>
- <create-value value-field="jobSandbox"/>
-
- <set field="productGroupOrder.jobId" from-field="jobId"/>
- <store-value value-field="productGroupOrder"/>
- </if-empty>
- </simple-method>
-
- <simple-method method-name="checkOrderItemForProductGroupOrder" short-description="Check OrderItem For ProductGroupOrder">
- <entity-and entity-name="OrderItem" list="orderItems">
- <field-map field-name="orderId" from-field="parameters.orderId"/>
- </entity-and>
- <iterate list="orderItems" entry="orderItem">
- <set field="productId" from-field="orderItem.productId"/>
- <entity-one entity-name="Product" value-field="product">
- <field-map field-name="productId" from-field="orderItem.productId"/>
- </entity-one>
- <if-compare field="product.isVariant" operator="equals" value="Y">
- <entity-and entity-name="ProductAssoc" list="variantProductAssocs" filter-by-date="true">
- <field-map field-name="productIdTo" from-field="orderItem.productId"/>
- <field-map field-name="productAssocTypeId" value="PRODUCT_VARIANT"/>
- </entity-and>
- <first-from-list list="variantProductAssocs" entry="variantProductAssoc"/>
- <set field="productId" from-field="variantProductAssoc.productId"/>
- </if-compare>
-
- <entity-and entity-name="ProductGroupOrder" list="productGroupOrders" filter-by-date="true">
- <field-map field-name="productId" from-field="productId"/>
- </entity-and>
- <if-not-empty field="productGroupOrders">
- <first-from-list list="productGroupOrders" entry="productGroupOrder"/>
- <calculate field="productGroupOrder.soldOrderQty">
- <calcop operator="add" field="productGroupOrder.soldOrderQty">
- <calcop operator="get" field="orderItem.quantity"/>
- </calcop>
- </calculate>
- <store-value value-field="productGroupOrder"/>
-
- <set field="createOrderItemGroupOrderMap.orderId" from-field="orderItem.orderId"/>
- <set field="createOrderItemGroupOrderMap.orderItemSeqId" from-field="orderItem.orderItemSeqId"/>
- <set field="createOrderItemGroupOrderMap.groupOrderId" from-field="productGroupOrder.groupOrderId"/>
- <call-service service-name="createOrderItemGroupOrder" in-map-name="createOrderItemGroupOrderMap"/>
- </if-not-empty>
- </iterate>
- </simple-method>
-
- <simple-method method-name="cancleOrderItemGroupOrder" short-description="Cancle OrderItemGroupOrder">
- <if-not-empty field="parameters.orderItemSeqId">
- <entity-and entity-name="OrderItem" list="orderItems">
- <field-map field-name="orderId" from-field="parameters.orderId"/>
- <field-map field-name="orderItemSeqId" from-field="parameters.orderItemSeqId" />
- </entity-and>
- <else>
- <entity-and entity-name="OrderItem" list="orderItems">
- <field-map field-name="orderId" from-field="parameters.orderId"/>
- </entity-and>
- </else>
- </if-not-empty>
- <iterate list="orderItems" entry="orderItem">
- <entity-and entity-name="OrderItemGroupOrder" list="orderItemGroupOrders">
- <field-map field-name="orderId" from-field="orderItem.orderId"/>
- <field-map field-name="orderItemSeqId" from-field="orderItem.orderItemSeqId"/>
- </entity-and>
- <if-not-empty field="orderItemGroupOrders">
- <first-from-list list="orderItemGroupOrders" entry="orderItemGroupOrder"/>
- <entity-one entity-name="ProductGroupOrder" value-field="productGroupOrder">
- <field-map field-name="groupOrderId" from-field="orderItemGroupOrder.groupOrderId"/>
- </entity-one>
- <if-not-empty field="productGroupOrder">
- <if-compare field="productGroupOrder.statusId" operator="equals" value="GO_CREATED">
- <if-compare field="orderItem.statusId" operator="equals" value="ITEM_CANCELLED">
- <if-not-empty field="orderItem.cancelQuantity">
- <set field="cancelQuantity" from-field="orderItem.cancelQuantity"/>
- <else>
- <set field="cancelQuantity" from-field="orderItem.quantity"/>
- </else>
- </if-not-empty>
- <calculate field="productGroupOrder.soldOrderQty">
- <calcop operator="subtract" field="productGroupOrder.soldOrderQty">
- <calcop operator="get" field="cancelQuantity"/>
- </calcop>
- </calculate>
- </if-compare>
- <store-value value-field="productGroupOrder"/>
- <remove-value value-field="orderItemGroupOrder"/>
- </if-compare>
- </if-not-empty>
- </if-not-empty>
- </iterate>
- </simple-method>
-
- <simple-method method-name="checkProductGroupOrderExpired" short-description="Check ProductGroupOrder Expired">
- <entity-one entity-name="ProductGroupOrder" value-field="productGroupOrder"/>
- <if-not-empty field="productGroupOrder">
- <if-compare field="productGroupOrder.soldOrderQty" operator="greater-equals" value="${productGroupOrder.reqOrderQty}">
- <set field="newItemStatusId" value="ITEM_APPROVED"/>
- <set field="groupOrderStatusId" value="GO_SUCCESS"/>
- <else>
- <set field="newItemStatusId" value="ITEM_CANCELLED"/>
- <set field="groupOrderStatusId" value="GO_CANCELLED"/>
- </else>
- </if-compare>
-
- <set field="updateProductGroupOrderMap.groupOrderId" from-field="productGroupOrder.groupOrderId"/>
- <set field="updateProductGroupOrderMap.statusId" from-field="groupOrderStatusId"/>
- <call-service service-name="updateProductGroupOrder" in-map-name="updateProductGroupOrderMap"/>
-
- <entity-and entity-name="OrderItemGroupOrder" list="orderItemGroupOrders">
- <field-map field-name="groupOrderId" from-field="productGroupOrder.groupOrderId"/>
- </entity-and>
- <iterate list="orderItemGroupOrders" entry="orderItemGroupOrder">
- <set field="changeOrderItemStatusMap.orderId" from-field="orderItemGroupOrder.orderId"/>
- <set field="changeOrderItemStatusMap.orderItemSeqId" from-field="orderItemGroupOrder.orderItemSeqId"/>
- <set field="changeOrderItemStatusMap.statusId" from-field="newItemStatusId"/>
- <call-service service-name="changeOrderItemStatus" in-map-name="changeOrderItemStatusMap"/>
- </iterate>
- </if-not-empty>
- </simple-method>
-
- <simple-method method-name="setProductReviewStatus" short-description="change the product review Status">
- <set value="setProductReviewStatus" field="callingMethodName"/>
- <set value="UPDATE" field="checkAction"/>
- <call-simple-method method-name="checkProductRelatedPermission"/>
- <check-errors/>
-
- <entity-one entity-name="ProductReview" value-field="productReview"/>
- <if-not-empty field="productReview">
- <if-compare-field field="productReview.statusId" to-field="parameters.statusId" operator="not-equals">
- <entity-one entity-name="StatusValidChange" value-field="statusChange">
- <field-map field-name="statusId" from-field="productReview.statusId"/>
- <field-map field-name="statusIdTo" from-field="parameters.statusId"/>
- </entity-one>
- <if-empty field="statusChange">
- <set field="msg" value="Status is not a valid change: from ${productReview.statusId} to ${parameters.statusId}"/>
- <log level="error" message="${msg}"/>
- <add-error>
- <fail-property resource="ProductErrorUiLabels" property="ProductReviewErrorCouldNotChangeOrderStatusFromTo"/>
- </add-error>
- </if-empty>
- </if-compare-field>
- </if-not-empty>
- <check-errors/>
-
- <set field="productReview.statusId" from-field="parameters.statusId"/>
- <store-value value-field="productReview"/>
- <field-to-result field="productReview.productReviewId" result-name="productReviewId"/>
- </simple-method>
-</simple-methods>
diff --git a/applications/product/servicedef/services.xml b/applications/product/servicedef/services.xml
index 4e821b4..b44cba7 100644
--- a/applications/product/servicedef/services.xml
+++ b/applications/product/servicedef/services.xml
@@ -37,22 +37,22 @@ under the License.
<override name="description" allow-html="safe"/>
<override name="longDescription" allow-html="safe"/>
</service>
- <service name="createProduct" default-entity-name="Product" engine="simple"
- location="component://product/minilang/product/product/ProductServices.xml" invoke="createProduct" auth="true">
+ <service name="createProduct" default-entity-name="Product" engine="groovy"
+ location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="createProduct" auth="true">
<description>Create a Product</description>
<implements service="interfaceProduct"/>
<auto-attributes include="pk" mode="INOUT" optional="true"/>
<override name="productTypeId" optional="false"/>
<override name="internalName" optional="false"/>
</service>
- <service name="updateProduct" default-entity-name="Product" engine="simple"
- location="component://product/minilang/product/product/ProductServices.xml" invoke="updateProduct" auth="true">
+ <service name="updateProduct" default-entity-name="Product" engine="groovy"
+ location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="updateProduct" auth="true">
<description>Update a Product</description>
<implements service="interfaceProduct"/>
<auto-attributes include="pk" mode="IN" optional="false"/>
</service>
- <service name="updateProductQuickAdminName" default-entity-name="Product" engine="simple"
- location="component://product/minilang/product/product/ProductServices.xml" invoke="updateProductQuickAdminName" auth="true">
+ <service name="updateProductQuickAdminName" default-entity-name="Product" engine="groovy"
+ location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="updateProductQuickAdminName" auth="true">
<description>Update a Product from Quick Admin</description>
<implements service="interfaceProduct"/>
<auto-attributes include="pk" mode="IN" optional="false"/>
@@ -63,8 +63,8 @@ under the License.
<implements service="interfaceProduct"/>
<auto-attributes include="pk" mode="IN" optional="false"/>
</service>
- <service name="duplicateProduct" engine="simple"
- location="component://product/minilang/product/product/ProductServices.xml" invoke="duplicateProduct" auth="true">
+ <service name="duplicateProduct" engine="groovy"
+ location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="duplicateProduct" auth="true">
<description>Duplicate a Product using a new productId</description>
<attribute name="productId" type="String" mode="IN" optional="false"/>
<attribute name="oldProductId" type="String" mode="IN" optional="false"/>
@@ -89,8 +89,8 @@ under the License.
<attribute name="removeFeatureAppls" type="String" mode="IN" optional="true"/>
<attribute name="removeInventoryItems" type="String" mode="IN" optional="true"/>
</service>
- <service name="copyToProductVariants" engine="simple"
- location="component://product/minilang/product/product/ProductServices.xml" invoke="copyToProductVariants" auth="true">
+ <service name="copyToProductVariants" engine="groovy"
+ location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="copyToProductVariants" auth="true">
<description>Copy Virtual Product's data to the Variant Products</description>
<attribute name="virtualProductId" type="String" mode="IN" optional="false"/>
<attribute name="removeBefore" type="String" mode="IN" optional="true"/>
@@ -151,40 +151,40 @@ under the License.
<permission-service service-name="productGenericPermission" main-action="DELETE"/>
<auto-attributes include="pk" mode="IN" optional="false"/>
</service>
- <service name="deleteProductKeywords" engine="simple"
- location="component://product/minilang/product/product/ProductServices.xml" invoke="deleteProductKeywords" auth="true">
+ <service name="deleteProductKeywords" engine="groovy"
+ location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="deleteProductKeywords" auth="true">
<description>Delete all the keywords of a product</description>
<permission-service service-name="productGenericPermission" main-action="DELETE"/>
<attribute name="productId" type="String" mode="IN" optional="false"/>
</service>
- <service name="indexProductKeywords" engine="simple"
- location="component://product/minilang/product/product/ProductServices.xml" invoke="indexProductKeywords" auth="false">
+ <service name="indexProductKeywords" engine="groovy"
+ location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="indexProductKeywords" auth="false">
<description>Index the Keywords for a Product</description>
<attribute name="productId" type="String" mode="IN" optional="false"/>
<attribute name="productInstance" type="org.apache.ofbiz.entity.GenericValue" mode="IN" optional="true"/>
</service>
- <service name="forceIndexProductKeywords" engine="simple"
- location="component://product/minilang/product/product/ProductServices.xml" invoke="forceIndexProductKeywords" auth="true">
+ <service name="forceIndexProductKeywords" engine="groovy"
+ location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="forceIndexProductKeywords" auth="true">
<description>Induce all the keywords of a product, ignoring the flag in the Product.autoCreateKeywords flag</description>
<permission-service service-name="productGenericPermission" main-action="CREATE"/>
<attribute name="productId" type="String" mode="IN" optional="false"/>
</service>
- <service name="discontinueProductSales" engine="simple"
- location="component://product/minilang/product/product/ProductServices.xml" invoke="discontinueProductSales" auth="false">
+ <service name="discontinueProductSales" engine="groovy"
+ location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="discontinueProductSales" auth="false">
<description>Discontinue Product Sales</description>
<attribute name="productId" type="String" mode="IN" optional="false"/>
</service>
- <service name="countProductView" engine="simple"
- location="component://product/minilang/product/product/ProductServices.xml" invoke="countProductView" auth="false">
+ <service name="countProductView" engine="groovy"
+ location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="countProductView" auth="false">
<description>count Product View</description>
<attribute name="productId" type="String" mode="IN" optional="false"/>
<attribute name="weight" type="Long" mode="IN" optional="true"/>
</service>
- <service name="createProductReview" engine="simple"
- location="component://product/minilang/product/product/ProductServices.xml" invoke="createProductReview" auth="true">
+ <service name="createProductReview" engine="groovy"
+ location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="createProductReview" auth="true">
<description>Create a product review entity</description>
<auto-attributes entity-name="ProductReview" mode="IN" include="nonpk" optional="true"/>
<attribute name="productReviewId" type="String" mode="OUT" optional="false"/>
@@ -192,8 +192,8 @@ under the License.
<override name="productId" optional="false"/>
<override name="productRating" optional="false"/>
</service>
- <service name="updateProductReview" engine="simple" default-entity-name="ProductReview"
- location="component://product/minilang/product/product/ProductServices.xml" invoke="updateProductReview" auth="true">
+ <service name="updateProductReview" engine="groovy" default-entity-name="ProductReview"
+ location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="updateProductReview" auth="true">
<description>Updates a product review record</description>
<required-permissions join-type="OR">
<check-permission permission="CATALOG_UPDATE"/>
@@ -202,8 +202,8 @@ under the License.
<auto-attributes mode="IN" include="pk" optional="false"/>
<auto-attributes mode="IN" include="nonpk" optional="true"/>
</service>
- <service name="setProductReviewStatus" engine="simple"
- location="component://product/minilang/product/product/ProductServices.xml" invoke="setProductReviewStatus" auth="true">
+ <service name="setProductReviewStatus" engine="groovy"
+ location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="setProductReviewStatus" auth="true">
<description>Updates a product review record</description>
<required-permissions join-type="OR">
<check-permission permission="CATALOG_UPDATE"/>
@@ -784,8 +784,8 @@ under the License.
<attribute name="fromDate" type="Timestamp" mode="IN" optional="false"/>
</service>
- <service name="addPartyToProduct" engine="simple"
- location="component://product/minilang/product/product/ProductServices.xml" invoke="addPartyToProduct" auth="true">
+ <service name="addPartyToProduct" engine="groovy"
+ location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="addPartyToProduct" auth="true">
<description>Add Party To Product</description>
<attribute name="productId" type="String" mode="IN" optional="false"/>
<attribute name="partyId" type="String" mode="IN" optional="false"/>
@@ -795,8 +795,8 @@ under the License.
<attribute name="sequenceNum" type="Long" mode="IN" optional="true"/>
<attribute name="comments" type="String" mode="IN" optional="true"/>
</service>
- <service name="updatePartyToProduct" engine="simple"
- location="component://product/minilang/product/product/ProductServices.xml" invoke="updatePartyToProduct" auth="true">
+ <service name="updatePartyToProduct" engine="groovy"
+ location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="updatePartyToProduct" auth="true">
<description>Update Party To Product</description>
<attribute name="productId" type="String" mode="IN" optional="false"/>
<attribute name="partyId" type="String" mode="IN" optional="false"/>
@@ -806,8 +806,8 @@ under the License.
<attribute name="sequenceNum" type="Long" mode="IN" optional="true"/>
<attribute name="comments" type="String" mode="IN" optional="true"/>
</service>
- <service name="removePartyFromProduct" engine="simple"
- location="component://product/minilang/product/product/ProductServices.xml" invoke="removePartyFromProduct" auth="true">
+ <service name="removePartyFromProduct" engine="groovy"
+ location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="removePartyFromProduct" auth="true">
<description>Remove Party From Product</description>
<attribute name="productId" type="String" mode="IN" optional="false"/>
<attribute name="partyId" type="String" mode="IN" optional="false"/>
@@ -1240,16 +1240,16 @@ under the License.
</service>
<!-- Permission Services -->
- <service name="productGenericPermission" engine="simple"
- location="component://product/minilang/product/product/ProductServices.xml" invoke="productGenericPermission">
+ <service name="productGenericPermission" engine="groovy"
+ location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="productGenericPermission">
<implements service="permissionInterface"/>
</service>
<service name="productCategoryGenericPermission" engine="groovy"
location="component://product/groovyScripts/product/category/CategoryServices.groovy" invoke="productCategoryGenericPermission">
<implements service="permissionInterface"/>
</service>
- <service name="productPriceGenericPermission" engine="simple"
- location="component://product/minilang/product/product/ProductServices.xml" invoke="productPriceGenericPermission">
+ <service name="productPriceGenericPermission" engine="groovy"
+ location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="productPriceGenericPermission">
<implements service="permissionInterface"/>
</service>
<service name="checkCategoryPermissionWithViewPurchaseAllow" engine="groovy"
@@ -1285,20 +1285,20 @@ under the License.
</service>
<!-- ProductCategoryGlAccount Services -->
- <service name="createProductCategoryGlAccount" default-entity-name="ProductCategoryGlAccount" engine="simple"
- location="component://product/minilang/product/product/ProductServices.xml" invoke="createProductCategoryGlAccount" auth="true">
+ <service name="createProductCategoryGlAccount" default-entity-name="ProductCategoryGlAccount" engine="groovy"
+ location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="createProductCategoryGlAccount" auth="true">
<description>Create a ProductCategoryGlAccount</description>
<auto-attributes include="pk" mode="IN" optional="false"/>
<auto-attributes include="nonpk" mode="IN" optional="false"/>
</service>
- <service name="updateProductCategoryGlAccount" default-entity-name="ProductCategoryGlAccount" engine="simple"
- location="component://product/minilang/product/product/ProductServices.xml" invoke="updateProductCategoryGlAccount" auth="true">
+ <service name="updateProductCategoryGlAccount" default-entity-name="ProductCategoryGlAccount" engine="groovy"
+ location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="updateProductCategoryGlAccount" auth="true">
<description>Update a ProductCategoryGlAccount</description>
<auto-attributes include="pk" mode="IN" optional="false"/>
<auto-attributes include="nonpk" mode="IN" optional="false"/>
</service>
- <service name="deleteProductCategoryGlAccount" default-entity-name="ProductCategoryGlAccount" engine="simple"
- location="component://product/minilang/product/product/ProductServices.xml" invoke="deleteProductCategoryGlAccount" auth="true">
+ <service name="deleteProductCategoryGlAccount" default-entity-name="ProductCategoryGlAccount" engine="groovy"
+ location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="deleteProductCategoryGlAccount" auth="true">
<description>Delete a ProductCategoryGlAccount</description>
<auto-attributes include="pk" mode="IN" optional="false"/>
</service>
@@ -1608,48 +1608,48 @@ under the License.
</service>
<!-- Product GroupOrder Services -->
- <service name="createProductGroupOrder" default-entity-name="ProductGroupOrder" engine="simple"
- location="component://product/minilang/product/product/ProductServices.xml" invoke="createProductGroupOrder" auth="true">
+ <service name="createProductGroupOrder" default-entity-name="ProductGroupOrder" engine="groovy"
+ location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="createProductGroupOrder" auth="true">
<description>Create ProductGroupOrder</description>
<auto-attributes include="pk" mode="OUT" optional="false"/>
<auto-attributes include="nonpk" mode="IN" optional="true"/>
</service>
- <service name="updateProductGroupOrder" default-entity-name="ProductGroupOrder" engine="simple"
- location="component://product/minilang/product/product/ProductServices.xml" invoke="updateProductGroupOrder" auth="true">
+ <service name="updateProductGroupOrder" default-entity-name="ProductGroupOrder" engine="groovy"
+ location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="updateProductGroupOrder" auth="true">
<description>Update ProductGroupOrder</description>
<auto-attributes include="pk" mode="IN" optional="false"/>
<auto-attributes include="nonpk" mode="IN" optional="true"/>
</service>
- <service name="deleteProductGroupOrder" default-entity-name="ProductGroupOrder" engine="simple"
- location="component://product/minilang/product/product/ProductServices.xml" invoke="deleteProductGroupOrder" auth="true">
+ <service name="deleteProductGroupOrder" default-entity-name="ProductGroupOrder" engine="groovy"
+ location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="deleteProductGroupOrder" auth="true">
<description>Delete ProductGroupOrder</description>
<auto-attributes include="pk" mode="IN" optional="false"/>
</service>
- <service name="createJobForProductGroupOrder" default-entity-name="ProductGroupOrder" engine="simple"
- location="component://product/minilang/product/product/ProductServices.xml" invoke="createJobForProductGroupOrder" auth="true">
+ <service name="createJobForProductGroupOrder" default-entity-name="ProductGroupOrder" engine="groovy"
+ location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="createJobForProductGroupOrder" auth="true">
<description>Create Job For ProductGroupOrder</description>
<auto-attributes include="pk" mode="IN" optional="false"/>
<auto-attributes include="nonpk" mode="IN" optional="true"/>
</service>
- <service name="checkOrderItemForProductGroupOrder" engine="simple"
- location="component://product/minilang/product/product/ProductServices.xml" invoke="checkOrderItemForProductGroupOrder" auth="true">
+ <service name="checkOrderItemForProductGroupOrder" engine="groovy"
+ location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="checkOrderItemForProductGroupOrder" auth="true">
<description>Check OrderItem For ProductGroupOrder</description>
<attribute name="orderId" mode="IN" type="String" optional="false"/>
</service>
- <service name="cancleOrderItemGroupOrder" engine="simple"
- location="component://product/minilang/product/product/ProductServices.xml" invoke="cancleOrderItemGroupOrder" auth="true">
+ <service name="cancleOrderItemGroupOrder" engine="groovy"
+ location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="cancleOrderItemGroupOrder" auth="true">
<description>Cancle OrderItemGroupOrder</description>
<attribute name="orderId" mode="IN" type="String" optional="false"/>
<attribute name="orderItemSeqId" type="String" mode="IN" optional="true"/>
</service>
- <service name="checkProductGroupOrderExpired" engine="simple"
- location="component://product/minilang/product/product/ProductServices.xml" invoke="checkProductGroupOrderExpired" auth="true">
+ <service name="checkProductGroupOrderExpired" engine="groovy"
+ location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="checkProductGroupOrderExpired" auth="true">
<description>Check ProductGroupOrder Expired</description>
<attribute name="groupOrderId" mode="IN" type="String" optional="false"/>
</service>
Re: [ofbiz-framework] branch trunk updated: Improved: Convert
ProductServices.xml mini lang to groovy (OFBIZ-10231)
Posted by Michael Brohl <mi...@ecomify.de>.
Hi Nicolas,
great, thanks for reviewing and improving the code!
Please go ahead and commit,
thanks,
Michael Brohl
ecomify GmbH - www.ecomify.de
Am 27.02.20 um 16:22 schrieb Nicolas Malin:
> Michael, I have lot of improvement to share with your commit (groovy
> syntax, code simplification)
>
> Do you prefer keep the hand, or I commit directly ?
>
> You can find some diff on my starting work here [1]
>
> Cheers,
>
> Nicolas
>
> [1]
> https://github.com/nmalin/ApacheOFBiz/compare/ProductService.groovy?expand=1
>
> On 21/02/2020 17:51, mbrohl@apache.org wrote:
>> 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-framework.git
>>
>>
>> The following commit(s) were added to refs/heads/trunk by this push:
>> new e0a26fc Improved: Convert ProductServices.xml mini lang to groovy (OFBIZ-10231)
>> e0a26fc is described below
>>
>> commit e0a26fce43eec7c84d87c0d5055ff0a87f2af796
>> Author: Michael Brohl <mi...@ecomify.de>
>> AuthorDate: Fri Feb 21 16:59:37 2020 +0100
>>
>> Improved: Convert ProductServices.xml mini lang to groovy
>> (OFBIZ-10231)
>>
>> Thanks Dennis Balkir for reporting and Sebastian Berg for the implementation.
Re: [ofbiz-framework] branch trunk updated: Improved: Convert
ProductServices.xml mini lang to groovy (OFBIZ-10231)
Posted by Nicolas Malin <ni...@nereide.fr>.
Michael, I have lot of improvement to share with your commit (groovy
syntax, code simplification)
Do you prefer keep the hand, or I commit directly ?
You can find some diff on my starting work here [1]
Cheers,
Nicolas
[1]
https://github.com/nmalin/ApacheOFBiz/compare/ProductService.groovy?expand=1
On 21/02/2020 17:51, mbrohl@apache.org wrote:
> 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-framework.git
>
>
> The following commit(s) were added to refs/heads/trunk by this push:
> new e0a26fc Improved: Convert ProductServices.xml mini lang to groovy (OFBIZ-10231)
> e0a26fc is described below
>
> commit e0a26fce43eec7c84d87c0d5055ff0a87f2af796
> Author: Michael Brohl <mi...@ecomify.de>
> AuthorDate: Fri Feb 21 16:59:37 2020 +0100
>
> Improved: Convert ProductServices.xml mini lang to groovy
> (OFBIZ-10231)
>
> Thanks Dennis Balkir for reporting and Sebastian Berg for the implementation.
> ---
> .../product/product/ProductServices.groovy | 1095 ++++++++++++++++++++
> .../minilang/product/product/ProductServices.xml | 1051 -------------------
> applications/product/servicedef/services.xml | 112 +-
> 3 files changed, 1151 insertions(+), 1107 deletions(-)
>
> diff --git a/applications/product/groovyScripts/product/product/ProductServices.groovy b/applications/product/groovyScripts/product/product/ProductServices.groovy
> new file mode 100644
> index 0000000..b4be894
> --- /dev/null
> +++ b/applications/product/groovyScripts/product/product/ProductServices.groovy
> @@ -0,0 +1,1095 @@
> +/*
> + * 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 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.serialize.XmlSerializer
> +import org.apache.ofbiz.entity.util.EntityUtil
> +import org.apache.ofbiz.product.product.KeywordIndex
> +import org.apache.ofbiz.product.product.ProductWorker
> +import org.apache.ofbiz.service.ServiceUtil
> +
> +
> +
> + module = "ProductServices.groovy" // this is used for logging
> +
> + /**
> + * Create a Product
> + */
> + def createProduct() {
> + Map result = success()
> + if (!(security.hasEntityPermission("CATALOG", "_CREATE", parameters.userLogin)
> + || security.hasEntityPermission("CATALOG_ROLE", "_CREATE", parameters.userLogin))) {
> + return error(UtilProperties.getMessage("ProductUiLabels", "ProductCatalogCreatePermissionError", parameters.locale))
> + }
> +
> + GenericValue newEntity = makeValue("Product")
> + newEntity.setNonPKFields(parameters)
> +
> + newEntity.productId = parameters.productId
> +
> + if (UtilValidate.isEmpty(newEntity.productId)) {
> + newEntity.productId = delegator.getNextSeqId("Product")
> + } else {
> + String errorMessage = UtilValidate.checkValidDatabaseId(newEntity.productId)
> + if(errorMessage != null) {
> + logError(errorMessage)
> + return error(errorMessage)
> + }
> + GenericValue dummyProduct = findOne("Product", ["productId": parameters.productId], false)
> + if (UtilValidate.isNotEmpty(dummyProduct)) {
> + errorMessage = UtilProperties.getMessage("CommonErrorUiLabels", CommonErrorDuplicateKey, parameters.locale)
> + logError(errorMessage)
> + return error(errorMessage)
> + }
> + }
> + result.productId = newEntity.productId
> +
> + Timestamp nowTimestamp = UtilDateTime.nowTimestamp()
> +
> + newEntity.createdDate = nowTimestamp
> + newEntity.lastModifiedDate = nowTimestamp
> + newEntity.lastModifiedByUserLogin = userLogin.userLoginId
> + newEntity.createdByUserLogin = userLogin.userLoginId
> +
> + if (UtilValidate.isEmpty(newEntity.isVariant)) {
> + newEntity.isVariant = "N"
> + }
> + if (UtilValidate.isEmpty(newEntity.isVirtual)) {
> + newEntity.isVirtual = "N"
> + }
> + if (UtilValidate.isEmpty(newEntity.billOfMaterialLevel)) {
> + newEntity.billOfMaterialLevel = (Long) 0
> + }
> +
> + newEntity.create()
> +
> + /*
> + * if setting the primaryProductCategoryId create a member entity too
> + * THIS IS REMOVED BECAUSE IT CAUSES PROBLEMS FOR WORKING ON PRODUCTION SITES
> + * <if-not-empty field="newEntity.primaryProductCategoryId">
> + * <make-value entity-name="ProductCategoryMember" value-field="newMember"/>
> + * <set from-field="productId" map-name="newEntity" to-field-name="productId" to-map-name="newMember"/>
> + * <set from-field="primaryProductCategoryId" map-name="newEntity" to-field-name="productCategoryId" to-map-name="newMember"/>
> + * <now-timestamp field="nowStamp"/>
> + * <set from-field="nowStamp" field="newMember.fromDate"/>
> + * <create-value value-field="newMember"/>
> + * </if-not-empty>
> + */
> +
> + // if the user has the role limited position, add this product to the limit category/ies
> +
> +
> + if (security.hasEntityPermission("CATALOG_ROLE","_CREATE", parameters.userLogin)) {
> + List productCategoryRoles = from("ProductCategoryRole").where("partyId": userLogin.partyId, "roleTypeId": "LTD_ADMIN").queryList()
> +
> + for (GenericValue productCategoryRole : productCategoryRoles) {
> + // add this new product to the category
> + GenericValue newLimitMember = makeValue("ProductCategoryMember")
> + newLimitMember.productId = newEntity.productId
> + newLimitMember.productCateogryId = productCategoryRole.productCategoryId
> + newLimitMember.fromDate = nowTimestamp
> + newLimitMember.create()
> + }
> + }
> +
> + return result
> +}
> +
> +/**
> + * Update a product
> + */
> +def updateProduct() {
> + Map res = checkProductRelatedPermission("updateProduct", "UPDATE")
> + if (!ServiceUtil.isSuccess(res)) {
> + return res
> + }
> + GenericValue lookedUpValue = findOne("Product", ["productId": parameters.productId], false)
> + // save this value before overwriting it so we can compare it later
> + Map saveIdMap = ["primaryProductCategoryId": lookedUpValue.primaryProductCategoryId]
> +
> + lookedUpValue.setNonPKFields(parameters)
> + lookedUpValue.lastModifiedDate = UtilDateTime.nowTimestamp()
> + lookedUpValue.lastModifiedByUserLogin = userLogin.userLoginId
> + lookedUpValue.store()
> +
> + return success()
> + }
> +
> + /**
> + * Update a Product Name from quick admin
> + */
> +def updateProductQuickAdminName() {
> + Map res = checkProductRelatedPermission("updateQuickAdminName", "UPDATE")
> +
> + if (!ServiceUtil.isSuccess(res)) {
> + return res
> + }
> +
> + GenericValue lookedUpValue = findOne("Product", ["productId": parameters.productId], false)
> + lookedUpValue.productName = parameters.productName
> + if ("Y".equals(lookedUpValue.isVirtual)) {
> + lookedUpValue.internalName = lookedUpValue.productName
> + }
> +
> + lookedUpValue.lastModifiedDate = UtilDateTime.nowTimestamp();
> + lookedUpValue.lastModifiedByUserLogin = userLogin.userLoginId
> +
> + lookedUpValue.store()
> +
> + if ("Y".equals(lookedUpValue.isVirtual)) {
> + // get all variant products, to update their productNames
> + Map variantProductAssocMap = ["productId": parameters.productId, "productAssocTypeId": "PRODUCT_VARIANT"]
> +
> + // get all productAssocs, then get the actual product to update
> + List variantProductAssocs = from("ProductAssoc").where(variantProductAssocMap).queryList()
> + variantProductAssocs = EntityUtil.filterByDate(variantProductAssocs)
> + for(GenericValue variantProductAssoc : variantProductAssocs) {
> + GenericValue variantProduct = null
> + variantProduct = findOne("Product", ["productId": variantProductAssoc.productIdTo], false)
> +
> + variantProduct.productName = parameters.productName
> + variantProduct.lastModifiedDate = UtilDateTime.nowTimestamp()
> + variantProduct.lastModifiedByUserLogin = userLogin.userLoginId
> + variantProduct.store()
> + }
> + }
> + return success()
> +}
> +
> +/**
> + * Duplicate a Product
> + */
> +def duplicateProduct() {
> + String callingMethodName = "duplicateProduct"
> + String checkAction = "CREATE"
> + Map res = checkProductRelatedPermission(callingMethodName, checkAction)
> + if (!ServiceUtil.isSuccess(res)) {
> + return res
> + }
> + checkAction = "DELETE"
> + res = checkProductRelatedPermission(callingMethodName, checkAction)
> + if (!ServiceUtil.isSuccess(res)) {
> + return res
> + }
> + GenericValue dummyProduct = findOne("Product", ["productId": parameters.productId], false)
> + if (UtilValidate.isNotEmpty(dummyProduct)) {
> + String errorMessage = UtilProperties.getMessage("CommonErrorUiLabels", CommonErrorDuplicateKey, parameters.locale)
> + logError(errorMessage)
> + return error(errorMessage)
> + }
> +
> + // look up the old product and clone it
> + GenericValue oldProduct = findOne("Product", ["productId": parameters.oldProductId], false)
> + GenericValue newProduct = oldProduct.clone()
> +
> + // set the productId, and write it to the datasource
> + newProduct.productId = parameters.productId
> +
> + // if requested, set the new internalName field
> + if (UtilValidate.isNotEmpty(parameters.newInternalName)) {
> + newProduct.internalName = parameters.newInternalName
> + }
> +
> + // if requested, set the new productName field
> + if (UtilValidate.isNotEmpty(parameters.newProductName)) {
> + newProduct.productName = parameters.newProductName
> + }
> +
> + // if requested, set the new description field
> + if (UtilValidate.isNotEmpty(parameters.newDescription)) {
> + newProduct.description = parameters.newDescription
> + }
> +
> + // if requested, set the new longDescription field
> + if (UtilValidate.isNotEmpty(parameters.newLongDescription)) {
> + newProduct.longDescription = parameters.newLongDescription
> + }
> +
> + newProduct.create()
> +
> + // set up entity filter
> + Map productFindContext = ["productId": parameters.oldProductId]
> + Map reverseProductFindContext = ["productIdTo": parameters.oldProductId]
> +
> + // if requested, duplicate related data as well
> + if (UtilValidate.isNotEmpty(parameters.duplicatePrices)) {
> + List foundValues = from("ProductPrice").where(productFindContext).queryList()
> + for (GenericValue foundValue : foundValues) {
> + GenericValue newTempValue = foundValue.clone()
> + newTempValue.productId = parameters.productId
> + newTempValue.create()
> + }
> + }
> + if (UtilValidate.isNotEmpty(parameters.duplicateIDs)) {
> + List foundValues = from("GoodIdentification").where(productFindContext).queryList()
> + for (GenericValue foundValue : foundValues) {
> + GenericValue newTempValue = foundValue.clone()
> + newTempValue.productId = parameters.productId
> + newTempValue.create()
> + }
> + }
> + if (UtilValidate.isNotEmpty(parameters.duplicateContent)) {
> + List foundValues = from("ProductContent").where(productFindContext).queryList()
> + for (GenericValue foundValue : foundValues) {
> + GenericValue newTempValue = foundValue.clone()
> + newTempValue.productId = parameters.productId
> + newTempValue.create()
> + }
> + }
> + if (UtilValidate.isNotEmpty(parameters.duplicateCategoryMembers)) {
> + List foundValues = from("ProductCategoryMember").where(productFindContext).queryList()
> + for (GenericValue foundValue : foundValues) {
> + GenericValue newTempValue = foundValue.clone()
> + newTempValue.productId = parameters.productId
> +
> + newTempValue.create()
> + }
> + }
> + if (UtilValidate.isNotEmpty(parameters.duplicateAssocs)) {
> + List foundValues = from("ProductAssoc").where(productFindContext).queryList()
> + for (GenericValue foundValue : foundValues) {
> + GenericValue newTempValue = foundValue.clone()
> + newTempValue.productId = parameters.productId
> + newTempValue.create()
> + }
> +
> + // small difference here, also do the reverse assocs...
> + foundValues = from("ProductAssoc").where("productIdTo": parameters.oldProductId).queryList()
> + for (GenericValue foundValue : foundValues) {
> + GenericValue newTempValue = foundValue.clone()
> + newTempValue.productIdTo = parameters.productId
> + newTempValue.create()
> + }
> + }
> + if (UtilValidate.isNotEmpty(parameters.duplicateAttributes)) {
> + List foundValues = from("ProductAttribute").where(productFindContext).queryList()
> + for (GenericValue foundValue : foundValues) {
> + GenericValue newTempValue = foundValue.clone()
> + newTempValue.productId = parameters.productId
> + newTempValue.create()
> + }
> + }
> + if (UtilValidate.isNotEmpty(parameters.duplicateFeatureAppls)) {
> + List foundValues = from("ProductFeatureAppl").where(productFindContext).queryList()
> + for (GenericValue foundValue : foundValues) {
> + GenericValue newTempValue = foundValue.clone()
> + newTempValue.productId = parameters.productId
> + newTempValue.create()
> + }
> + }
> + if (UtilValidate.isNotEmpty(parameters.duplicateInventoryItems)) {
> + List foundValues = from("InventoryItem").where(productFindContext).queryList()
> + for (GenericValue foundValue : foundValues) {
> + /*
> + * NOTE: new inventory items should always be created calling the
> + * createInventoryItem service because in this way we are sure
> + * that all the relevant fields are filled with default values.
> + * However, the code here should work fine because all the values
> + * for the new inventory item are inerited from the existing item.
> + * TODO: is this code correct? What is the meaning of duplicating inventory items?
> + * What about the InventoryItemDetail entries?
> + */
> + GenericValue newTempValue = foundValue.clone()
> + newTempValue.productId = parameters.productId
> + // this one is slightly different because it needs a new sequenced inventoryItemId
> + newTempValue.inventoryItemId = delegator.getNextSeqId("InventoryItem")
> + newTempValue.create()
> + }
> + }
> +
> + // if requested, remove related data as well
> + if (UtilValidate.isNotEmpty(parameters.removePrices)) {
> + delegator.removeByAnd("ProductPrice", productFindContext)
> + }
> + if (UtilValidate.isNotEmpty(parameters.removeIDs)) {
> + delegator.removeByAnd("GoodIdentification", productFindContext)
> + }
> + if (UtilValidate.isNotEmpty(parameters.removeContent)) {
> + delegator.removeByAnd("ProductContent", productFindContext)
> + }
> + if (UtilValidate.isNotEmpty(parameters.removeCategoryMembers)) {
> + delegator.removeByAnd("ProductCategoryMember", productFindContext)
> + }
> + if (UtilValidate.isNotEmpty(parameters.removeAssocs)) {
> + delegator.removeByAnd("ProductAssoc", productFindContext)
> + // small difference here, also do the reverse assocs...
> + delegator.removeByAnd("ProductAssoc", reverseProductFindContext)
> + }
> + if (UtilValidate.isNotEmpty(parameters.removeAttributes)) {
> + delegator.removeByAnd("ProductAttribute", productFindContext)
> + }
> + if (UtilValidate.isNotEmpty(parameters.removeFeatureAppls)) {
> + delegator.removeByAnd("ProductFeatureAppl", productFindContext)
> + }
> + if (UtilValidate.isNotEmpty(parameters.removeInventoryItems)) {
> + delegator.removeByAnd("InventoryItem", productFindContext)
> + }
> + return success()
> +}
> +
> +// Product Keyword Services
> +
> +/**
> + * induce all the keywords of a product
> + */
> +def forceIndexProductKeywords() {
> + GenericValue product = findOne("Product", [productId: parameters.productId], false)
> + KeywordIndex.forceIndexKeywords(product)
> + return success()
> +}
> +
> +/**
> + * delete all the keywords of a produc
> + */
> +def deleteProductKeywords() {
> + GenericValue product = findOne("Product", [productId: parameters.productId], false)
> + delegator.removeRelated("ProductKeyword", product)
> + return success()
> +}
> +
> +/**
> + * Index the Keywords for a Product
> + */
> +def indexProductKeywords() {
> + //this service is meant to be called from an entity ECA for entities that include a productId
> + //if it is the Product entity itself triggering this action, then a [productInstance] parameter
> + //will be passed and we can save a few cycles looking that up
> + GenericValue productInstance = parameters.productInstance
> + if (productInstance == null) {
> + Map findProductMap = [productId: parameters.productId]
> + productInstance = findOne("Product", findProductMap, false)
> + }
> + //induce keywords if autoCreateKeywords is empty or Y
> + if (UtilValidate.isEmpty(productInstance.autoCreateKeywords) || "Y".equals(productInstance.autoCreateKeywords)) {
> + KeywordIndex.indexKeywords(productInstance)
> + }
> + return success()
> +}
> +
> +/**
> + * Discontinue Product Sales
> + * set sales discontinuation date to now
> + */
> +def discontinueProductSales() {
> + // set sales discontinuation date to now
> + Timestamp nowTimestamp = UtilDateTime.nowTimestamp()
> + GenericValue product = findOne("Product", parameters, false)
> + product.salesDiscontinuationDate = nowTimestamp
> + product.store()
> +
> + // expire product from all categories
> + List productCategoryMembers = delegator.getRelated("ProductCategoryMember", null, null, product, false)
> + for (GenericValue productCategoryMember : productCategoryMembers) {
> + if (UtilValidate.isEmpty(productCategoryMember.thruDate)) {
> + productCategoryMember.thruDate = UtilDateTime.nowTimestamp()
> + productCategoryMember.store()
> + }
> + }
> + // expire product from all associations going to it
> + List assocProductAssocs = delegator.getRelated("AssocProductAssoc", null, null, product, false)
> + for (GenericValue assocProductAssoc : assocProductAssocs) {
> + if (UtilValidate.isEmpty(assocProductAssoc.thruDate)) {
> + assocProductAssoc.thruDate = UtilDateTime.nowTimestamp()
> + assocProductAssoc.store()
> + }
> + }
> + return success()
> +}
> +
> +
> +def countProductView() {
> + if (UtilValidate.isEmpty(parameters.weight)) {
> + parameters.weight = (Long) 1
> + }
> + GenericValue productCalculatedInfo = findOne("ProductCalculatedInfo", ["productId": parameters.productId], false)
> + if (UtilValidate.isEmpty(productCalculatedInfo)) {
> + // go ahead and create it
> + productCalculatedInfo = makeValue("ProductCalculatedInfo")
> + productCalculatedInfo.productId = parameters.productId
> + productCalculatedInfo.totalTimesViewed = parameters.weight
> + productCalculatedInfo.create()
> + } else {
> + productCalculatedInfo.totalTimesViewed = productCalculatedInfo.totalTimesViewed + parameters.weight
> + productCalculatedInfo.store()
> + }
> +
> + // do the same for the virtual product...
> + GenericValue product = findOne("Product", ["productId": parameters.productId], true)
> + ProductWorker productWorker = new ProductWorker()
> + String virtualProductId = productWorker.getVariantVirtualId(product)
> + if (UtilValidate.isNotEmpty(virtualProductId)) {
> + Map callSubMap = ["productId": virtualProductId, "weight": parameters.weight]
> + run service: "countProductView", with: callSubMap
> + }
> + return success()
> +
> +}
> +
> +/**
> + * Create a ProductReview
> + */
> +def createProductReview() {
> + GenericValue newEntity = makeValue("ProductReview", parameters)
> + newEntity.userLoginId = userLogin.userLoginId
> + newEntity.statusId = "PRR_PENDING"
> +
> + // code to check for auto-approved reviews (store setting)
> + GenericValue productStore = findOne("ProductStore", ["productStoreId": parameters.productStoreId], false)
> +
> + if (!UtilValidate.isEmpty(productStore)) {
> + if ("Y".equals(productStore.autoApproveReviews)) {
> + newEntity.statusId = "PRR_APPROVED"
> + }
> + }
> +
> + // create the new ProductReview
> + newEntity.productReviewId = delegator.getNextSeqId("ProductReview")
> + Map result = success()
> + result.productReviewId = newEntity.productReviewId
> +
> + if (UtilValidate.isEmpty(newEntity.postedDateTime)) {
> + newEntity.postedDateTime = UtilDateTime.nowTimestamp()
> + }
> +
> + newEntity.create()
> +
> + String productId = newEntity.productId
> + String successMessage = UtilProperties.getMessage("ProductUiLabels", "ProductCreateProductReviewSuccess", parameters.locale)
> + updateProductWithReviewRatingAvg(productId)
> +
> + return result
> +}
> +
> +/**
> + * Update ProductReview
> + */
> +def updateProductReview() {
> + Map res = checkProductRelatedPermission("updateProductReview", "UPDATE")
> + if (!ServiceUtil.isSuccess(res)) {
> + return res
> + }
> +
> + GenericValue lookupPKMap = makeValue("ProductReview")
> + lookupPKMap.setPKFields(parameters)
> + GenericValue lookedUpValue = findOne("ProductReview", lookupPKMap, false)
> + lookupPKMap.setNonPKFields(parameters)
> + lookupPKMap.store()
> +
> + String productId = lookedUpValue.productId
> + updateProductWithReviewRatingAvg(productId)
> +
> + return success()
> +}
> +
> +/**
> + * change the product review Status
> + */
> +def setProductReviewStatus(){
> + Map res = checkProductRelatedPermission("setProductReviewStatus", "UPDATE")
> + if (!ServiceUtil.isSuccess(res)) {
> + return res
> + }
> +
> + GenericValue productReview = findOne("ProductReview", parameters, false)
> + if (UtilValidate.isNotEmpty(productReview)) {
> + if (!productReview.statusId.equals(parameters.statusId)) {
> + GenericValue statusChange = from("StatusValidChange")
> + .where("statusId", productReview.statusId, "statusIdTo", parameters.statusId)
> + .queryOne()
> + if (UtilValidate.isEmpty(statusChange)) {
> + String msg = "Status is not a valid change: from " + productReview.statusId + " to " + parameters.statusId
> + logError(msg)
> + String errorMessage = UtilProperties.getMessage("ProductErrorUiLabels", ProductReviewErrorCouldNotChangeOrderStatusFromTo, parameters.locale)
> + logError(errorMessage)
> + return error(errorMessage)
> + }
> + }
> + }
> +
> + productReview.statusId = parameters.statusId
> + productReview.store()
> + Map result = success()
> + result.productReviewId = productReview.productReviewId
> +
> + return result
> +}
> +
> +/**
> + * Update Product with new Review Rating Avg
> + * this method is meant to be called in-line and depends in a productId parameter
> + */
> +def updateProductWithReviewRatingAvg(String productId) {
> + ProductWorker productWorker = new ProductWorker()
> + BigDecimal averageCustomerRating = productWorker.getAverageProductRating(delegator, productId)
> + logInfo("Got new average customer rating "+ averageCustomerRating)
> +
> + if (averageCustomerRating == 0) {
> + return success()
> + }
> +
> + // update the review average on the ProductCalculatedInfo entity
> + GenericValue productCalculatedInfo = findOne("ProductCalculatedInfo", parameters, false)
> + if (UtilValidate.isEmpty(productCalculatedInfo)) {
> + // go ahead and create it
> + productCalculatedInfo = makeValue("ProductCalculatedInfo")
> + productCalculatedInfo.productId = productId
> + productCalculatedInfo.averageCustomerRating = averageCustomerRating
> + productCalculatedInfo.create()
> + } else {
> + productCalculatedInfo.averageCustomerRating = averageCustomerRating
> + productCalculatedInfo.store()
> + }
> +
> + return success()
> +}
> +
> +/**
> + * Updates the Product's Variants
> + */
> +def copyToProductVariants() {
> + String callingMethodName = "copyToProductVariants"
> + String checkAction = "CREATE"
> + Map res = checkProductRelatedPermission(callingMethodName, checkAction)
> + if (!ServiceUtil.isSuccess(res)) {
> + return res
> + }
> + checkAction = "DELETE"
> + res = checkProductRelatedPermission(callingMethodName, checkAction)
> + if (!ServiceUtil.isSuccess(res)) {
> + return res
> + }
> +
> + Map productFindContext = ["productId": parameters.virtualProductId]
> + GenericValue oldProduct = findOne("Product", productFindContext, false)
> +
> + Map variantsFindContext = ["productId": parameters.virtualProductId, "productAssocTypeId": "PRODUCT_VARIANT"]
> +
> + List variants = from("ProductAssoc").where(variantsFindContext).filterByDate().queryList()
> + List foundVariantValues = []
> + List foundValues = []
> + for (GenericValue newProduct : variants) {
> + Map productVariantContext = ["productId": newProduct.productIdTo]
> + // if requested, duplicate related data
> + if (UtilValidate.isNotEmpty(parameters.duplicatePrices)) {
> + if (UtilValidate.isNotEmpty(parameters.removeBefore)) {
> + foundVariantValues = from("ProductPrice").where(productVariantContext).queryList()
> + for (GenericValue foundVariantValue : foundVariantValues) {
> + foundVariantValue.remove()
> + }
> + }
> + foundValues = from("ProductPrice").where(productFindContext).queryList()
> + for (GenericValue foundValue : foundValues) {
> + GenericValue newTempValue = foundValue.clone()
> + newTempValue.productId = newProduct.productIdTo
> + newTempValue.create()
> + }
> + }
> + if (UtilValidate.isNotEmpty(parameters.duplicateIDs)) {
> + if (UtilValidate.isNotEmpty(parameters.removeBefore)) {
> + foundVariantValues = from("GoodIdentification").where(productVariantContext).queryList()
> + for (GenericValue foundVariantValue : foundVariantValues) {
> + foundVariantValue.remove()
> + }
> + }
> + foundValues = from("GoodIdentification").where(productFindContext).queryList()
> + for (GenericValue foundValue : foundValues) {
> + GenericValue newTempValue = foundValue.clone()
> + newTempValue.productId = newProduct.productIdTo
> + newTempValue.create()
> + }
> +
> + }
> + if (UtilValidate.isNotEmpty(parameters.duplicateContent)) {
> + if (UtilValidate.isNotEmpty(parameters.removeBefore)) {
> + foundVariantValues = from("ProductContent").where(productVariantContext).queryList()
> + for (GenericValue foundVariantValue : foundVariantValues) {
> + foundVariantValue.remove()
> + }
> + }
> + foundValues = from("ProductContent").where(productFindContext).queryList()
> + for (GenericValue foundValue : foundValues) {
> + GenericValue newTempValue = foundValue.clone()
> + newTempValue.productId = newProduct.productIdTo
> + newTempValue.create()
> + }
> + }
> + if (UtilValidate.isNotEmpty(parameters.duplicateCategoryMembers)) {
> + if (UtilValidate.isNotEmpty(parameters.removeBefore)) {
> + foundVariantValues = from("ProductCategoryMember").where(productVariantContext).queryList()
> + for (GenericValue foundVariantValue : foundVariantValues) {
> + foundVariantValue.remove()
> + }
> + }
> + foundValues = from("ProductCategoryMember").where(productFindContext).queryList()
> + for (GenericValue foundValue : foundValues) {
> + GenericValue newTempValue = foundValue.clone()
> + newTempValue.productId = newProduct.productIdTo
> + newTempValue.create()
> + }
> + }
> + if (UtilValidate.isNotEmpty(parameters.duplicateAttributes)) {
> + if (UtilValidate.isNotEmpty(parameters.removeBefore)) {
> + foundVariantValues = from("ProductAttribute").where(productVariantContext).queryList()
> + for (GenericValue foundVariantValue : foundVariantValues) {
> + foundVariantValue.remove()
> + }
> + }
> + foundValues = from("ProductAttribute").where(productFindContext).queryList()
> + for (GenericValue foundValue : foundValues) {
> + GenericValue newTempValue = foundValue.clone()
> + newTempValue.productId = newProduct.productIdTo
> + newTempValue.create()
> + }
> + }
> + if (UtilValidate.isNotEmpty(parameters.duplicateFacilities)) {
> + if (UtilValidate.isNotEmpty(parameters.removeBefore)) {
> + foundVariantValues = from("ProductFacility").where(productVariantContext).queryList()
> + for (GenericValue foundVariantValue : foundVariantValues) {
> + foundVariantValue.remove()
> + }
> + }
> + foundValues = from("ProductFacility").where(productFindContext).queryList()
> + for (GenericValue foundValue : foundValues) {
> + GenericValue newTempValue = foundValue.clone()
> + newTempValue.productId = newProduct.productIdTo
> + newTempValue.create()
> + }
> + }
> + if (UtilValidate.isNotEmpty(parameters.duplicateLocations)) {
> + if (UtilValidate.isNotEmpty(parameters.removeBefore)) {
> + foundVariantValues = from("ProductFacilityLocation").where(productVariantContext).queryList()
> + for (GenericValue foundVariantValue : foundVariantValues) {
> + foundVariantValue.remove()
> + }
> + }
> + foundValues = from("ProductFacilityLocation").where(productFindContext).queryList()
> + for (GenericValue foundValue : foundValues) {
> + GenericValue newTempValue = foundValue.clone()
> + newTempValue.productId = newProduct.productIdTo
> + newTempValue.create()
> + }
> + }
> + }
> + return success()
> +}
> +
> +/**
> + * Check Product Related Permission
> + * a method to centralize product security code, meant to be called in-line with
> + * call-simple-method, and the checkAction and callingMethodName attributes should be in the method context
> + */
> +def checkProductRelatedPermission (String callingMethodName, String checkAction){
> + if (UtilValidate.isEmpty(callingMethodName)) {
> + callingMethodName = UtilProperties.getMessage("CommonUiLabels", "CommonPermissionThisOperation", parameters.locale)
> + }
> + if (UtilValidate.isEmpty(checkAction)) {
> + checkAction = "UPDATE"
> + }
> + List roleCategories = []
> + // find all role-categories that this product is a member of
> + if (!security.hasEntityPermission("CATALOG", "_${checkAction}", parameters.userLogin)) {
> + Map lookupRoleCategoriesMap = ["productId": parameters.productId, "partyId": userLogin.partyId, "roleTypeId": "LTD_ADMIN"]
> + roleCategories = from("ProductCategoryMemberAndRole").where(lookupRoleCategoriesMap).filterByDate("roleFromDate", "roleThruDate").queryList()
> + }
> +
> + if (! ((security.hasEntityPermission("CATALOG", "_${checkAction}", parameters.userLogin))
> + || (security.hasEntityPermission("CATALOG_ROLE", "_${checkAction}", parameters.userLogin) && !UtilValidate.isEmpty(roleCategories))
> + || (!UtilValidate.isEmpty(parameters.alternatePermissionRoot) && security.hasEntityPermission(parameters.alternatePermissionRoot, checkAction, parameters.userLogin)))) {
> + String checkActionLabel = "ProductCatalog" + checkAction.charAt(0) + checkAction.substring(1).toLowerCase() + "PermissionError"
> + String resourceDescription = callingMethodName
> +
> + String errorMessage = UtilProperties.getMessage("ProductUiLabels", checkActionLabel, parameters.locale)
> + logError(errorMessage)
> + return error(errorMessage)
> + }
> + return success()
> +}
> +
> +/**
> + * Main permission logic
> + */
> +def productGenericPermission(){
> + String mainAction = parameters.mainAction
> + Map result = success()
> + if (UtilValidate.isEmpty(mainAction)) {
> + String errorMessage = UtilProperties.getMessage("ProductUiLabels", "ProductMissingMainActionInPermissionService", parameters.locale)
> + logError(errorMessage)
> + return error(errorMessage)
> + }
> + Map res = checkProductRelatedPermission(parameters.resourceDescription, parameters.mainAction)
> + if (!ServiceUtil.isSuccess(res)) {
> + String failMessage = UtilProperties.getMessage("ProductUiLabels", "ProductPermissionError", parameters.locale)
> + Boolean hasPermission = false
> + result = fail(failMessage)
> + result.hasPermission = hasPermission
> + } else {
> + Boolean hasPermission = true
> + result.hasPermission = hasPermission
> + }
> + return result
> +}
> +
> +/**
> + * product price permission logic
> + */
> +def productPriceGenericPermission(){
> + String mainAction = parameters.mainAction
> + if (UtilValidate.isEmpty(mainAction)) {
> + String errorMessage = UtilProperties.getMessage("ProductUiLabels", "ProductMissingMainActionInPermissionService", parameters.locale)
> + logError(errorMessage)
> + return error(errorMessage)
> + }
> + Map result = success()
> + if (!security.hasEntityPermission("CATALOG_PRICE_MAINT", null, parameters.userLogin)) {
> + String errorMessage = UtilProperties.getMessage("ProductUiLabels", "ProductPriceMaintPermissionError", parameters.locale)
> + logError(errorMessage)
> + result = error(errorMessage)
> + }
> + Map res = checkProductRelatedPermission(null, null)
> + if (ServiceUtil.isSuccess(result) && ServiceUtil.isSuccess(res)) {
> + result.hasPermission = true
> + } else {
> + String failMessage = UtilProperties.getMessage("ProductUiLabels", "ProductPermissionError", parameters.locale)
> + result = fail(failMessage)
> + result.hasPermission = false
> + }
> + return result
> +}
> +
> +/**
> + * ================================================================
> + * ProductRole Services
> + * ================================================================
> + */
> +
> +
> +/**
> + * Add Party to Product
> + */
> +def addPartyToProduct(){
> + Map result = checkProductRelatedPermission("addPartyToProduct", "CREATE")
> + if (!ServiceUtil.isSuccess(result)) {
> + return result
> + }
> + GenericValue newEntity = makeValue("ProductRole", parameters)
> +
> + if (UtilValidate.isEmpty(newEntity.fromDate)) {
> + newEntity.fromDate = UtilDateTime.nowTimestamp()
> + }
> + newEntity.create()
> + return success()
> +}
> +
> +/**
> + * Update Party to Product
> + */
> +def updatePartyToProduct(){
> + Map result = checkProductRelatedPermission("updatePartyToProduct", "UPDATE")
> + if (!ServiceUtil.isSuccess(result)) {
> + return result
> + }
> + GenericValue lookupPKMap = makeValue("ProductRole")
> + lookupPKMap.setPKFields(parameters)
> + GenericValue lookedUpValue = findOne("ProductRole", lookupPKMap, false)
> + lookedUpValue.setNonPKFields(parameters)
> + lookedUpValue.store()
> + return success()
> +}
> +
> +/**
> + * Remove Party From Product
> + */
> +def removePartyFromProduct(){
> + Map res = checkProductRelatedPermission("removePartyFromProduct", "DELETE")
> + if (!ServiceUtil.isSuccess(res)) {
> + return res
> + }
> + Map lookupPKMap = makeValue("ProductRole")
> + lookupPKMap.setPKFields(parameters)
> + GenericValue lookedUpValue = findOne("ProductRole", lookupPKMap, false)
> + lookedUpValue.remove()
> +
> + return success()
> +}
> +
> +// ProductCategoryGlAccount methods
> + /**
> + * Create a ProductCategoryGlAccount
> + */
> +def createProductCategoryGlAccount(){
> + Map res = checkProductRelatedPermission("createProductCategoryGlAccount", "CREATE")
> + if (!ServiceUtil.isSuccess(res)) {
> + return res
> + }
> +
> + GenericValue newEntity = makeValue("ProductCategoryGlAccount", parameters)
> + newEntity.create()
> +
> + return success()
> +}
> +
> +/**
> + * Update a ProductCategoryGlAccount
> + */
> +def updateProductCategoryGlAccount(){
> + Map res = checkProductRelatedPermission("updateProductCategoryGlAccount", "UPDATE")
> + if (!ServiceUtil.isSuccess(res)) {
> + return res
> + }
> +
> + GenericValue lookedUpValue = findOne("ProductCategoryGlAccount", parameters, false)
> + lookedUpValue.setNonPKFields(parameters)
> + lookedUpValue.store()
> +
> + return success()
> +}
> +
> +/**
> + * Delete a ProductCategoryGlAccount
> + */
> +def deleteProductCategoryGlAccount(){
> + Map res = checkProductRelatedPermission("deleteProductCategorGLAccount", "DELETE")
> + if (!ServiceUtil.isSuccess(res)) {
> + return res
> + }
> + GenericValue lookedUpValue = findOne("ProductCategoryGlAccount", parameters, false)
> + lookedUpValue.remove()
> +
> + return success()
> +}
> +
> +// Product GroupOrder Services -->
> +
> +/**
> + * Create ProductGroupOrder
> + */
> +def createProductGroupOrder(){
> + GenericValue newEntity = makeValue("ProductGroupOrder")
> + delegator.setNextSubSeqId(newEntity, "groupOrderId", 5, 1)
> + Map result = success()
> + result.groupOrderId = newEntity.groupOrderId
> + newEntity.setNonPKFields(parameters)
> + newEntity.create()
> +
> + return result
> +}
> +
> +/**
> + * Update ProductGroupOrder
> + */
> +def updateProductGroupOrder(){
> + GenericValue productGroupOrder = findOne("ProductGroupOrder", ["groupOrderId": parameters.groupOrderId], false)
> + productGroupOrder.setNonPKFields(parameters)
> + productGroupOrder.store()
> +
> + if ("GO_CREATED".equals(productGroupOrder.statusId)) {
> + GenericValue jobSandbox = findOne("JobSandbox", ["jobId": productGroupOrder.jobId], false)
> + if (UtilValidate.isNotEmpty(jobSandbox)) {
> + jobSandbox.runTime = parameters.thruDate
> + jobSandbox.store()
> + }
> + }
> + return success()
> +}
> +
> +/**
> + * Delete ProductGroupOrder
> + */
> +def deleteProductGroupOrder(){
> + List orderItemGroupOrders = from("OrderItemGroupOrder").where("groupOrderId": parameters.groupOrderId).queryList()
> + for (GenericValue orderItemGroupOrder : orderItemGroupOrders) {
> + orderItemGroupOrder.remove()
> + }
> + GenericValue productGroupOrder = findOne("ProductGroupOrder", ["groupOrderId": parameters.groupOrderId], false)
> + if (UtilValidate.isEmpty(productGroupOrder)) {
> + return error("Entity value not found with name: " + productGroupOrder)
> + }
> + productGroupOrder.remove()
> +
> + GenericValue jobSandbox = findOne("JobSandbox", ["jobId": productGroupOrder.jobId], false)
> + if (UtilValidate.isEmpty(jobSandbox)) {
> + return error("Entity value not found with name: " + jobSandbox)
> + }
> + jobSandbox.remove()
> +
> + List jobSandboxList = from("JobSandbox").where("runtimeDataId": jobSandbox.runtimeDataId).queryList()
> + for (GenericValue jobSandboxRelatedRuntimeData : jobSandboxList) {
> + jobSandboxRelatedRuntimeData.remove()
> + }
> +
> + GenericValue runtimeData = findOne("RuntimeData", ["runtimeDataId": jobSandbox.runtimeDataId], false)
> + if (UtilValidate.isEmpty(runtimeData)) {
> + return error("Entity value not found with name: " + runtimeData)
> + }
> + runtimeData.remove()
> +
> + return success()
> +}
> +
> +/**
> + * Create ProductGroupOrder
> + */
> +def createJobForProductGroupOrder(){
> + GenericValue productGroupOrder = findOne("ProductGroupOrder", ["groupOrderId": parameters.groupOrderId], false)
> + if (UtilValidate.isEmpty(productGroupOrder.jobId)) {
> + // Create RuntimeData For ProductGroupOrder
> + Map runtimeDataMap = ["groupOrderId": parameters.groupOrderId]
> + XmlSerializer xmlSerializer = new XmlSerializer()
> + String runtimeInfo = xmlSerializer.serialize(runtimeDataMap)
> +
> + GenericValue runtimeData = makeValue("RuntimeData")
> + runtimeData.runtimeDataId = delegator.getNextSeqId("RuntimeData")
> + String runtimeDataId = runtimeData.runtimeDataId
> + runtimeData.runtimeInfo = runtimeInfo
> + runtimeData.create()
> +
> + // Create Job For ProductGroupOrder
> + // FIXME: Jobs should not be manually created
> + GenericValue jobSandbox = makeValue("JobSandbox")
> + jobSandbox.jobId = delegator.getNextSeqId("JobSandbox")
> + String jobId = jobSandbox.jobId
> + jobSandbox.jobName = "Check ProductGroupOrder Expired"
> + jobSandbox.runTime = parameters.thruDate
> + jobSandbox.poolId = "pool"
> + jobSandbox.statusId = "SERVICE_PENDING"
> + jobSandbox.serviceName = "checkProductGroupOrderExpired"
> + jobSandbox.runAsUser = "system"
> + jobSandbox.runtimeDataId = runtimeDataId
> + jobSandbox.maxRecurrenceCount = (Long) 1
> + jobSandbox.priority = (Long) 50
> + jobSandbox.create()
> +
> + productGroupOrder.jobId = jobId
> + productGroupOrder.store()
> + }
> + return success()
> +}
> +
> +/**
> + * Check OrderItem For ProductGroupOrder
> + */
> +def checkOrderItemForProductGroupOrder(){
> + List orderItems = from("OrderItem").where("orderId": parameters.orderId).queryList()
> + for (GenericValue orderItem : orderItems) {
> + String productId = orderItem.productId
> + GenericValue product = findOne("Product", ["productId": orderItem.productId], false)
> + if ("Y".equals(product.isVariant)) {
> + List variantProductAssocs = from("ProductAssoc").where("productIdTo": orderItem.productId, "productAssocTypeId": "PRODUCT_VARIANT").queryList()
> + variantProductAssocs = EntityUtil.filterByDate(variantProductAssocs)
> + GenericValue variantProductAssoc = variantProductAssocs.get(0)
> + productId = variantProductAssoc.productId
> + }
> + List productGroupOrders = from("ProductGroupOrder").where("productId": productId).queryList()
> + if (UtilValidate.isNotEmpty(productGroupOrders)) {
> + productGroupOrders = EntityUtil.filterByDate(productGroupOrders)
> + GenericValue productGroupOrder = productGroupOrders.get(0)
> + if (UtilValidate.isEmpty(productGroupOrder.soldOrderQty)) {
> + productGroupOrder.soldOrderQty = orderItem.quantity
> + } else {
> + productGroupOrder.soldOrderQty = productGroupOrder.soldOrderQty + orderItem.quantity
> + }
> + productGroupOrder.store()
> +
> + Map createOrderItemGroupOrderMap = ["orderId": orderItem.orderId, "orderItemSeqId": orderItem.orderItemSeqId, "groupOrderId": productGroupOrder.groupOrderId]
> +
> + run service: "createOrderItemGroupOrder", with: createOrderItemGroupOrderMap
> + }
> + }
> + return success()
> +}
> +
> +/**
> + * Cancle OrderItemGroupOrder
> + */
> +def cancleOrderItemGroupOrder(){
> + List orderItems = []
> + if (UtilValidate.isNotEmpty(parameters.orderItemSeqId)) {
> + orderItems = from("OrderItem")
> + .where("orderId", parameters.orderId, "orderItemSeqId", parameters.orderItemSeqId)
> + .queryList()
> + } else {
> + orderItems = from("OrderItem")
> + .where("orderId", parameters.orderId)
> + .queryList()
> + }
> + for(GenericValue orderItem : orderItems) {
> + List orderItemGroupOrders = from("OrderItemGroupOrder")
> + .where("orderId", orderItem.orderId, "orderItemSeqId", orderItem.orderItemSeqId)
> + .queryList()
> + if (UtilValidate.isNotEmpty(orderItemGroupOrders)) {
> + GenericValue orderItemGroupOrder = orderItemGroupOrders.get(0)
> + GenericValue productGroupOrder = findOne("ProductGroupOrder", [groupOrderId: orderItemGroupOrder.groupOrderId], false)
> +
> + if (UtilValidate.isNotEmpty(productGroupOrder)) {
> + if ("GO_CREATED".equals(productGroupOrder.statusId)) {
> + if ("ITEM_CANCELLED".equals(orderItem.statusId)) {
> + BigDecimal cancelQuantity
> + if (UtilValidate.isNotEmpty(orderItem.cancelQuantity)) {
> + cancelQuantity = orderItem.cancelQuantity
> + } else {
> + cancelQuantity = orderItem.quantity
> + }
> + productGroupOrder.soldOrderQty = productGroupOrder.soldOrderQty - cancelQuantity
> + }
> + productGroupOrder.store()
> + orderItemGroupOrder.remove()
> + }
> + }
> + }
> + }
> + return success()
> +}
> +
> +/**
> + * Check ProductGroupOrder Expired
> + */
> +def checkProductGroupOrderExpired(){
> + GenericValue productGroupOrder = findOne("ProductGroupOrder", parameters, false)
> + if (UtilValidate.isNotEmpty(productGroupOrder)) {
> + String groupOrderStatusId
> + String newItemStatusId
> + if (productGroupOrder.soldOrderQty >= productGroupOrder.reqOrderQty) {
> + newItemStatusId = "ITEM_APPROVED"
> + groupOrderStatusId = "GO_SUCCESS"
> + } else {
> + newItemStatusId = "ITEM_CANCELLED"
> + groupOrderStatusId = "GO_CANCELLED"
> + }
> + Map updateProductGroupOrderMap = [:]
> + updateProductGroupOrderMap.groupOrderId = productGroupOrder.groupOrderId
> + updateProductGroupOrderMap.statusId = groupOrderStatusId
> + run service: "updateProductGroupOrder", with: updateProductGroupOrderMap
> +
> + List orderItemGroupOrders = from("OrderItemGroupOrder")
> + .where("groupOrderId", productGroupOrder.groupOrderId)
> + .queryList()
> + for(GenericValue orderItemGroupOrder : orderItemGroupOrders) {
> + Map changeOrderItemStatusMap = ["orderId": orderItemGroupOrder.orderId, "orderItemSeqId": orderItemGroupOrder.orderItemSeqId, "statusId": newItemStatusId]
> + run service: "changeOrderItemStatus", with: changeOrderItemStatusMap
> + }
> + return success()
> + }
> +}
> +
> diff --git a/applications/product/minilang/product/product/ProductServices.xml b/applications/product/minilang/product/product/ProductServices.xml
> deleted file mode 100644
> index b331b32..0000000
> --- a/applications/product/minilang/product/product/ProductServices.xml
> +++ /dev/null
> @@ -1,1051 +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="createProduct" short-description="Create a Product">
> - <check-permission permission="CATALOG" action="_CREATE">
> - <alt-permission permission="CATALOG_ROLE" action="_CREATE"/>
> - <fail-property resource="ProductUiLabels" property="ProductCatalogCreatePermissionError"/>
> - </check-permission>
> - <check-errors/>
> -
> - <make-value entity-name="Product" value-field="newEntity"/>
> - <set-nonpk-fields map="parameters" value-field="newEntity"/>
> -
> - <set from-field="parameters.productId" field="newEntity.productId"/>
> - <if-empty field="newEntity.productId">
> - <sequenced-id sequence-name="Product" field="newEntity.productId"/>
> - <else>
> - <check-id field="newEntity.productId"/>
> - <check-errors />
> - <entity-one entity-name="Product" value-field="dummyProduct"><field-map field-name="productId" from-field="parameters.productId"/></entity-one>
> - <if-not-empty field="dummyProduct">
> - <add-error ><fail-property resource="CommonErrorUiLabels" property="CommonErrorDuplicateKey" /></add-error>
> - </if-not-empty>
> - <check-errors />
> - </else>
> - </if-empty>
> - <field-to-result field="newEntity.productId" result-name="productId"/>
> -
> - <now-timestamp field="nowTimestamp"/>
> - <set from-field="nowTimestamp" field="newEntity.createdDate"/>
> - <set from-field="nowTimestamp" field="newEntity.lastModifiedDate"/>
> - <set from-field="userLogin.userLoginId" field="newEntity.lastModifiedByUserLogin"/>
> - <set from-field="userLogin.userLoginId" field="newEntity.createdByUserLogin"/>
> - <if-empty field="newEntity.isVariant">
> - <set field="newEntity.isVariant" value="N"/>
> - </if-empty>
> - <if-empty field="newEntity.isVirtual">
> - <set field="newEntity.isVirtual" value="N"/>
> - </if-empty>
> - <if-empty field="newEntity.billOfMaterialLevel">
> - <set field="newEntity.billOfMaterialLevel" value="0" type="Long"/>
> - </if-empty>
> -
> - <create-value value-field="newEntity"/>
> -
> - <!-- if setting the primaryProductCategoryId create a member entity too -->
> - <!-- THIS IS REMOVED BECAUSE IT CAUSES PROBLEMS FOR WORKING ON PRODUCTION SITES
> - <if-not-empty field="newEntity.primaryProductCategoryId">
> - <make-value entity-name="ProductCategoryMember" value-field="newMember"/>
> - <set from-field="productId" map-name="newEntity" to-field-name="productId" to-map-name="newMember"/>
> - <set from-field="primaryProductCategoryId" map-name="newEntity" to-field-name="productCategoryId" to-map-name="newMember"/>
> - <now-timestamp field="nowStamp"/>
> - <set from-field="nowStamp" field="newMember.fromDate"/>
> - <create-value value-field="newMember"/>
> - </if-not-empty>
> - -->
> -
> - <!-- if the user has the role limited position, add this product to the limit category/ies -->
> - <if-has-permission permission="CATALOG_ROLE" action="_CREATE">
> - <entity-and entity-name="ProductCategoryRole" list="productCategoryRoles" filter-by-date="true">
> - <field-map field-name="partyId" from-field="userLogin.partyId"/>
> - <field-map field-name="roleTypeId" value="LTD_ADMIN"/>
> - </entity-and>
> -
> - <iterate list="productCategoryRoles" entry="productCategoryRole">
> - <!-- add this new product to the category -->
> - <make-value entity-name="ProductCategoryMember" value-field="newLimitMember"/>
> - <set from-field="newEntity.productId" field="newLimitMember.productId"/>
> - <set from-field="productCategoryRole.productCategoryId" field="newLimitMember.productCategoryId"/>
> - <set from-field="nowTimestamp" field="newLimitMember.fromDate"/>
> - <create-value value-field="newLimitMember"/>
> - </iterate>
> - </if-has-permission>
> - </simple-method>
> - <simple-method method-name="updateProduct" short-description="Update a Product">
> - <set value="updateProduct" field="callingMethodName"/>
> - <set value="UPDATE" field="checkAction"/>
> - <call-simple-method method-name="checkProductRelatedPermission"/>
> - <check-errors/>
> -
> - <entity-one entity-name="Product" value-field="lookedUpValue"/>
> - <!-- save this value before overwriting it so we can compare it later -->
> - <set from-field="lookedUpValue.primaryProductCategoryId" field="saveIdMap.primaryProductCategoryId"/>
> - <set-nonpk-fields map="parameters" value-field="lookedUpValue"/>
> -
> - <now-timestamp field="lookedUpValue.lastModifiedDate"/>
> - <set from-field="userLogin.userLoginId" field="lookedUpValue.lastModifiedByUserLogin"/>
> -
> - <store-value value-field="lookedUpValue"/>
> -
> - <!-- if setting the primaryParentCategoryId, create a rollup entity too -->
> - <!-- THIS IS REMOVED BECAUSE IT CAUSES PROBLEMS FOR WORKING ON PRODUCTION SITES
> - <if-not-empty field="lookedUpValue.primaryProductCategoryId">
> - <if-compare-field to-field="saveIdMap.primaryProductCategoryId" field="lookedUpValue.primaryProductCategoryId" operator="equals">
> - <make-value entity-name="ProductCategoryMember" value-field="newMember"/>
> - <set from-field="productId" map-name="newEntity" to-field-name="productId" to-map-name="newMember"/>
> - <set from-field="primaryProductCategoryId" map-name="newEntity" to-field-name="productCategoryId" to-map-name="newMember"/>
> - <now-timestamp field="newMember.fromDate"/>
> - <create-value value-field="newMember"/>
> - </if-compare-field>
> - </if-not-empty>
> - -->
> - </simple-method>
> -
> - <!-- update the name of a product - handles real , virtual and variant products -->
> - <simple-method method-name="updateProductQuickAdminName" short-description="Update a Product Name from quick admin">
> - <set value="updateProductQuickAdminName" field="callingMethodName"/>
> - <set value="UPDATE" field="checkAction"/>
> - <call-simple-method method-name="checkProductRelatedPermission"/>
> - <check-errors/>
> -
> - <entity-one entity-name="Product" value-field="lookedUpValue"/>
> - <set from-field="parameters.productName" field="lookedUpValue.productName"/>
> - <if-compare field="lookedUpValue.isVirtual" operator="equals" value="Y">
> - <set from-field="lookedUpValue.productName" field="lookedUpValue.internalName"/>
> - </if-compare>
> -
> - <now-timestamp field="lookedUpValue.lastModifiedDate"/>
> - <set from-field="userLogin.userLoginId" field="lookedUpValue.lastModifiedByUserLogin"/>
> -
> - <store-value value-field="lookedUpValue"/>
> -
> - <if-compare field="lookedUpValue.isVirtual" operator="equals" value="Y">
> - <!-- get all variant products, to update their productNames -->
> - <set from-field="parameters.productId" field="variantProductAssocMap.productId"/>
> - <set value="PRODUCT_VARIANT" field="variantProductAssocMap.productAssocTypeId"/>
> -
> - <!-- get all productAssocs, then get the actual product to update -->
> - <find-by-and entity-name="ProductAssoc" map="variantProductAssocMap" list="variantProductAssocs"/>
> - <filter-list-by-date list="variantProductAssocs"/>
> - <iterate list="variantProductAssocs" entry="variantProductAssoc">
> - <clear-field field="variantProduct"/>
> - <entity-one entity-name="Product" value-field="variantProduct" auto-field-map="false">
> - <field-map field-name="productId" from-field="variantProductAssoc.productIdTo"/>
> - </entity-one>
> -
> - <set from-field="parameters.productName" field="variantProduct.productName"/>
> - <now-timestamp field="variantProduct.lastModifiedDate"/>
> - <set from-field="userLogin.userLoginId" field="variantProduct.lastModifiedByUserLogin"/>
> - <store-value value-field="variantProduct"/>
> - </iterate>
> - </if-compare>
> - </simple-method>
> -
> - <simple-method method-name="duplicateProduct" short-description="Duplicate a Product">
> - <set value="duplicateProduct" field="callingMethodName"/>
> - <set value="CREATE" field="checkAction"/>
> - <call-simple-method method-name="checkProductRelatedPermission"/>
> - <set value="DELETE" field="checkAction"/>
> - <call-simple-method method-name="checkProductRelatedPermission"/>
> - <entity-one entity-name="Product" value-field="dummyProduct">
> - <field-map field-name="productId" from-field="parameters.productId"/>
> - </entity-one>
> - <if-not-empty field="dummyProduct">
> - <add-error ><fail-property resource="CommonErrorUiLabels" property="CommonErrorDuplicateKey" /></add-error>
> - </if-not-empty>
> - <check-errors/>
> -
> - <!-- look up the old product and clone it -->
> - <entity-one entity-name="Product" value-field="oldProduct" auto-field-map="false">
> - <field-map field-name="productId" from-field="parameters.oldProductId"/>
> - </entity-one>
> - <clone-value value-field="oldProduct" new-value-field="newProduct"/>
> -
> - <!-- set the productId, and write it to the datasource -->
> - <set from-field="parameters.productId" field="newProduct.productId"/>
> -
> - <!-- if requested, set the new internalName field -->
> - <if-not-empty field="parameters.newInternalName">
> - <set from-field="parameters.newInternalName" field="newProduct.internalName"/>
> - </if-not-empty>
> -
> - <!-- if requested, set the new productName field -->
> - <if-not-empty field="parameters.newProductName">
> - <set from-field="parameters.newProductName" field="newProduct.productName"/>
> - </if-not-empty>
> -
> - <!-- if requested, set the new description field -->
> - <if-not-empty field="parameters.newDescription">
> - <set from-field="parameters.newDescription" field="newProduct.description"/>
> - </if-not-empty>
> -
> - <!-- if requested, set the new longDescription field -->
> - <if-not-empty field="parameters.newLongDescription">
> - <set from-field="parameters.newLongDescription" field="newProduct.longDescription"/>
> - </if-not-empty>
> -
> - <create-value value-field="newProduct"/>
> -
> - <!-- set up entity filter -->
> - <set field="productFindContext.productId" from-field="parameters.oldProductId"/>
> - <set field="reverseProductFindContext.productIdTo" from-field="parameters.oldProductId"/>
> -
> - <!-- if requested, duplicate related data as well -->
> - <if-not-empty field="parameters.duplicatePrices">
> - <find-by-and entity-name="ProductPrice" map="productFindContext" list="foundValues"/>
> - <iterate list="foundValues" entry="foundValue">
> - <clone-value value-field="foundValue" new-value-field="newTempValue"/>
> - <set from-field="parameters.productId" field="newTempValue.productId"/>
> - <create-value value-field="newTempValue"/>
> - </iterate>
> - </if-not-empty>
> - <if-not-empty field="parameters.duplicateIDs">
> - <find-by-and entity-name="GoodIdentification" map="productFindContext" list="foundValues"/>
> - <iterate list="foundValues" entry="foundValue">
> - <clone-value value-field="foundValue" new-value-field="newTempValue"/>
> - <set from-field="parameters.productId" field="newTempValue.productId"/>
> - <create-value value-field="newTempValue"/>
> - </iterate>
> - </if-not-empty>
> - <if-not-empty field="parameters.duplicateContent">
> - <find-by-and entity-name="ProductContent" map="productFindContext" list="foundValues"/>
> - <iterate list="foundValues" entry="foundValue">
> - <clone-value value-field="foundValue" new-value-field="newTempValue"/>
> - <set from-field="parameters.productId" field="newTempValue.productId"/>
> - <create-value value-field="newTempValue"/>
> - </iterate>
> - </if-not-empty>
> - <if-not-empty field="parameters.duplicateCategoryMembers">
> - <find-by-and entity-name="ProductCategoryMember" map="productFindContext" list="foundValues"/>
> - <iterate list="foundValues" entry="foundValue">
> - <clone-value value-field="foundValue" new-value-field="newTempValue"/>
> - <set from-field="parameters.productId" field="newTempValue.productId"/>
> - <!-- Clone Content -->
> - <sequenced-id sequence-name="Content" field="newContentId"/>
> - <entity-one entity-name="Content" value-field="oldContent" use-cache="false">
> - <field-map field-name="contentId" value="${newTempValue.contentId}"/>
> - </entity-one>
> - <if-not-empty field="oldContent">
> - <clone-value value-field="oldContent" new-value-field="clonedContent"/>
> - <set from-field="newContentId" field="clonedContent.contentId"/>
> - <set from-field="newContentId" field="newTempValue.contentId"/>
> - <!-- Clone DataResource -->
> - <entity-one entity-name="DataResource" value-field="oldDataResource" use-cache="false">
> - <field-map field-name="dataResourceId" value="${clonedContent.dataResourceId}"/>
> - </entity-one>
> - <if-not-empty field="oldDataResource">
> - <sequenced-id sequence-name="DataResource" field="newDataResourceId"/>
> - <clone-value new-value-field="clonedDataresource" value-field="oldDataResource"/>
> - <set from-field="newDataResourceId" field="clonedDataresource.dataResourceId"/>
> - <!-- set new data resource id in cloned content -->
> - <set from-field="newDataResourceId" field="clonedContent.dataResourceId"/>
> - <create-value value-field="clonedDataresource"/>
> - <!-- Clone Electronic Text if exists -->
> - <get-related-one value-field="oldDataResource" relation-name="ElectronicText" to-value-field="oldElectronicText"/>
> - <if-not-empty field="oldElectronicText">
> - <clone-value value-field="oldElectronicText" new-value-field="clonedElectronicText"/>
> - <set from-field="newDataResourceId" field="clonedElectronicText.dataResourceId"/>
> - <create-value value-field="clonedElectronicText"/>
> - </if-not-empty>
> - </if-not-empty>
> - <create-value value-field="clonedContent"/>
> - </if-not-empty>
> - <!-- End Clone Contet -->
> - <create-value value-field="newTempValue"/>
> - </iterate>
> - </if-not-empty>
> - <if-not-empty field="parameters.duplicateAssocs">
> - <find-by-and entity-name="ProductAssoc" map="productFindContext" list="foundValues"/>
> - <iterate list="foundValues" entry="foundValue">
> - <clone-value value-field="foundValue" new-value-field="newTempValue"/>
> - <set from-field="parameters.productId" field="newTempValue.productId"/>
> - <create-value value-field="newTempValue"/>
> - </iterate>
> -
> - <!-- small difference here, also do the reverse assocs... -->
> - <entity-and entity-name="ProductAssoc" list="foundValues">
> - <field-map field-name="productIdTo" from-field="parameters.oldProductId"/>
> - </entity-and>
> - <iterate list="foundValues" entry="foundValue">
> - <clone-value value-field="foundValue" new-value-field="newTempValue"/>
> - <set from-field="parameters.productId" field="newTempValue.productIdTo"/>
> - <create-value value-field="newTempValue"/>
> - </iterate>
> - </if-not-empty>
> - <if-not-empty field="parameters.duplicateAttributes">
> - <find-by-and entity-name="ProductAttribute" map="productFindContext" list="foundValues"/>
> - <iterate list="foundValues" entry="foundValue">
> - <clone-value value-field="foundValue" new-value-field="newTempValue"/>
> - <set from-field="parameters.productId" field="newTempValue.productId"/>
> - <create-value value-field="newTempValue"/>
> - </iterate>
> - </if-not-empty>
> - <if-not-empty field="parameters.duplicateFeatureAppls">
> - <find-by-and entity-name="ProductFeatureAppl" map="productFindContext" list="foundValues"/>
> - <iterate list="foundValues" entry="foundValue">
> - <clone-value value-field="foundValue" new-value-field="newTempValue"/>
> - <set from-field="parameters.productId" field="newTempValue.productId"/>
> - <create-value value-field="newTempValue"/>
> - </iterate>
> - </if-not-empty>
> - <if-not-empty field="parameters.duplicateInventoryItems">
> - <find-by-and entity-name="InventoryItem" map="productFindContext" list="foundValues"/>
> - <iterate list="foundValues" entry="foundValue">
> - <!--
> - NOTE: new inventory items should always be created calling the
> - createInventoryItem service because in this way we are sure
> - that all the relevant fields are filled with default values.
> - However, the code here should work fine because all the values
> - for the new inventory item are inerited from the existing item.
> - TODO: is this code correct? What is the meaning of duplicating inventory items?
> - What about the InventoryItemDetail entries?
> - -->
> - <clone-value value-field="foundValue" new-value-field="newTempValue"/>
> - <set from-field="parameters.productId" field="newTempValue.productId"/>
> - <!-- this one is slightly different because it needs a new sequenced inventoryItemId -->
> - <sequenced-id sequence-name="InventoryItem" field="newTempValue.inventoryItemId"/>
> - <create-value value-field="newTempValue"/>
> - </iterate>
> - </if-not-empty>
> -
> - <!-- if requested, remove related data as well -->
> - <if-not-empty field="parameters.removePrices">
> - <remove-by-and entity-name="ProductPrice" map="productFindContext"/>
> - </if-not-empty>
> - <if-not-empty field="parameters.removeIDs">
> - <remove-by-and entity-name="GoodIdentification" map="productFindContext"/>
> - </if-not-empty>
> - <if-not-empty field="parameters.removeContent">
> - <remove-by-and entity-name="ProductContent" map="productFindContext"/>
> - </if-not-empty>
> - <if-not-empty field="parameters.removeCategoryMembers">
> - <remove-by-and entity-name="ProductCategoryMember" map="productFindContext"/>
> - </if-not-empty>
> - <if-not-empty field="parameters.removeAssocs">
> - <remove-by-and entity-name="ProductAssoc" map="productFindContext"/>
> - <!-- small difference here, also do the reverse assocs... -->
> - <remove-by-and entity-name="ProductAssoc" map="reverseProductFindContext"/>
> - </if-not-empty>
> - <if-not-empty field="parameters.removeAttributes">
> - <remove-by-and entity-name="ProductAttribute" map="productFindContext"/>
> - </if-not-empty>
> - <if-not-empty field="parameters.removeFeatureAppls">
> - <remove-by-and entity-name="ProductFeatureAppl" map="productFindContext"/>
> - </if-not-empty>
> - <if-not-empty field="parameters.removeInventoryItems">
> - <remove-by-and entity-name="InventoryItem" map="productFindContext"/>
> - </if-not-empty>
> - </simple-method>
> -
> - <!-- Product Keyword Services -->
> - <simple-method method-name="forceIndexProductKeywords" short-description="induce all the keywords of a product">
> - <entity-one entity-name="Product" value-field="product"/>
> - <call-class-method class-name="org.apache.ofbiz.product.product.KeywordIndex" method-name="forceIndexKeywords">
> - <field field="product" type="org.apache.ofbiz.entity.GenericValue"/>
> - </call-class-method>
> - </simple-method>
> - <simple-method method-name="deleteProductKeywords" short-description="delete all the keywords of a product">
> - <entity-one entity-name="Product" value-field="product"/>
> - <remove-related value-field="product" relation-name="ProductKeyword"/>
> - </simple-method>
> -
> - <simple-method method-name="indexProductKeywords" short-description="Index the Keywords for a Product" login-required="false">
> - <!-- this service is meant to be called from an entity ECA for entities that include a productId -->
> - <!-- if it is the Product entity itself triggering this action, then a [productInstance] parameter
> - will be passed and we can save a few cycles looking that up -->
> - <set from-field="parameters.productInstance" field="productInstance"/>
> - <if-empty field="productInstance">
> - <set from-field="parameters.productId" field="findProductMap.productId"/>
> - <find-by-primary-key entity-name="Product" map="findProductMap" value-field="productInstance"/>
> - </if-empty>
> -
> - <!-- induce keywords if autoCreateKeywords is emtpy or Y-->
> - <if>
> - <condition>
> - <or>
> - <if-empty field="productInstance.autoCreateKeywords"/>
> - <if-compare field="productInstance.autoCreateKeywords" operator="equals" value="Y"/>
> - </or>
> - </condition>
> - <then>
> - <call-class-method class-name="org.apache.ofbiz.product.product.KeywordIndex" method-name="indexKeywords">
> - <field field="productInstance" type="org.apache.ofbiz.entity.GenericValue"/>
> - </call-class-method>
> - </then>
> - </if>
> - </simple-method>
> -
> - <simple-method method-name="discontinueProductSales" short-description="Discontinue Product Sales" login-required="false">
> - <!-- set sales discontinuation date to now -->
> - <now-timestamp field="nowTimestamp"/>
> - <entity-one entity-name="Product" value-field="product"/>
> - <set from-field="nowTimestamp" field="product.salesDiscontinuationDate"/>
> - <store-value value-field="product"/>
> - <!-- expire product from all categories -->
> - <get-related value-field="product" relation-name="ProductCategoryMember" list="productCategoryMembers"/>
> - <iterate list="productCategoryMembers" entry="productCategoryMember">
> - <if-empty field="productCategoryMember.thruDate">
> - <set from-field="nowTimestamp" field="productCategoryMember.thruDate"/>
> - <store-value value-field="productCategoryMember"/>
> - </if-empty>
> - </iterate>
> - <!-- expire product from all associations going to it -->
> - <get-related value-field="product" relation-name="AssocProductAssoc" list="assocProductAssocs"/>
> - <iterate list="assocProductAssocs" entry="assocProductAssoc">
> - <if-empty field="assocProductAssoc.thruDate">
> - <set from-field="nowTimestamp" field="assocProductAssoc.thruDate"/>
> - <store-value value-field="assocProductAssoc"/>
> - </if-empty>
> - </iterate>
> - </simple-method>
> -
> - <simple-method method-name="countProductView" short-description="Count Product View" login-required="false">
> - <if-empty field="parameters.weight">
> - <calculate field="parameters.weight" type="Long"><number value="1"/></calculate>
> - </if-empty>
> - <entity-one entity-name="ProductCalculatedInfo" value-field="productCalculatedInfo"/>
> - <if-empty field="productCalculatedInfo">
> - <!-- go ahead and create it -->
> - <make-value entity-name="ProductCalculatedInfo" value-field="productCalculatedInfo"/>
> - <set from-field="parameters.productId" field="productCalculatedInfo.productId"/>
> - <set from-field="parameters.weight" field="productCalculatedInfo.totalTimesViewed"/>
> - <create-value value-field="productCalculatedInfo"/>
> - <else>
> - <calculate field="productCalculatedInfo.totalTimesViewed" type="Long">
> - <calcop operator="add" field="productCalculatedInfo.totalTimesViewed">
> - <calcop operator="get" field="parameters.weight"></calcop>
> - </calcop>
> - </calculate>
> - <store-value value-field="productCalculatedInfo"/>
> - </else>
> - </if-empty>
> -
> - <!-- do the same for the virtual product... -->
> - <entity-one entity-name="Product" value-field="product" use-cache="true"/>
> - <call-class-method class-name="org.apache.ofbiz.product.product.ProductWorker" method-name="getVariantVirtualId" ret-field="virtualProductId">
> - <field field="product" type="GenericValue"/>
> - </call-class-method>
> - <if-not-empty field="virtualProductId">
> - <set from-field="virtualProductId" field="callSubMap.productId"/>
> - <set from-field="parameters.weight" field="callSubMap.weight"/>
> - <call-service service-name="countProductView" in-map-name="callSubMap"></call-service>
> - </if-not-empty>
> - </simple-method>
> -
> - <simple-method method-name="createProductReview" short-description="Create a ProductReview" login-required="false">
> - <make-value entity-name="ProductReview" value-field="newEntity"/>
> - <set-nonpk-fields map="parameters" value-field="newEntity"/>
> - <set from-field="userLogin.userLoginId" field="newEntity.userLoginId"/>
> - <set value="PRR_PENDING" field="newEntity.statusId"/>
> -
> - <!-- code to check for auto-approved reviews (store setting) -->
> - <entity-one entity-name="ProductStore" value-field="productStore"/>
> -
> - <if-not-empty field="productStore">
> - <if-compare field="productStore.autoApproveReviews" operator="equals" value="Y">
> - <set value="PRR_APPROVED" field="newEntity.statusId"/>
> - </if-compare>
> - </if-not-empty>
> -
> - <!-- auto approve the review if it is just a rating and has no review text -->
> - <if-empty field="parameters.productReview">
> - <set value="PRR_APPROVED" field="newEntity.statusId"/>
> - </if-empty>
> -
> - <!-- create the new ProductReview -->
> - <sequenced-id sequence-name="ProductReview" field="newEntity.productReviewId"/>
> - <field-to-result field="newEntity.productReviewId" result-name="productReviewId"/>
> -
> - <if-empty field="newEntity.postedDateTime">
> - <now-timestamp field="newEntity.postedDateTime"/>
> - </if-empty>
> -
> - <create-value value-field="newEntity"/>
> -
> - <set from-field="newEntity.productId" field="productId"/>
> - <property-to-field resource="ProductUiLabels" property="ProductCreateProductReviewSuccess" field="successMessage"/>
> - <call-simple-method method-name="updateProductWithReviewRatingAvg"/>
> - </simple-method>
> - <simple-method method-name="updateProductReview" short-description="Update ProductReview">
> - <set value="updateProductReview" field="callingMethodName"/>
> - <set value="UPDATE" field="checkAction"/>
> - <call-simple-method method-name="checkProductRelatedPermission"/>
> - <check-errors/>
> -
> - <make-value entity-name="ProductReview" value-field="lookupPKMap"/>
> - <set-pk-fields map="parameters" value-field="lookupPKMap"/>
> - <find-by-primary-key map="lookupPKMap" value-field="lookedUpValue"/>
> - <set-nonpk-fields map="parameters" value-field="lookedUpValue"/>
> - <store-value value-field="lookedUpValue"/>
> -
> - <set from-field="lookedUpValue.productId" field="productId"/>
> - <call-simple-method method-name="updateProductWithReviewRatingAvg"/>
> - </simple-method>
> - <simple-method method-name="updateProductWithReviewRatingAvg" short-description="Update Product with new Review Rating Avg" login-required="false">
> - <!-- this method is meant to be called in-line and depends in a productId parameter -->
> - <call-class-method class-name="org.apache.ofbiz.product.product.ProductWorker" method-name="getAverageProductRating" ret-field="averageCustomerRating">
> - <field field="delegator" type="org.apache.ofbiz.entity.Delegator"/>
> - <field field="productId" type="java.lang.String"/>
> - </call-class-method>
> - <log level="info" message="Got new average customer rating ${averageCustomerRating}"/>
> - <if-compare field="averageCustomerRating" operator="equals" value="0" type="BigDecimal">
> - <return/>
> - </if-compare>
> - <!-- update the review average on the ProductCalculatedInfo entity -->
> - <entity-one entity-name="ProductCalculatedInfo" value-field="productCalculatedInfo"/>
> - <if-empty field="productCalculatedInfo">
> - <!-- go ahead and create it -->
> - <make-value entity-name="ProductCalculatedInfo" value-field="productCalculatedInfo"/>
> - <set from-field="productId" field="productCalculatedInfo.productId"/>
> - <set from-field="averageCustomerRating" field="productCalculatedInfo.averageCustomerRating"/>
> - <create-value value-field="productCalculatedInfo"/>
> - <else>
> - <set from-field="averageCustomerRating" field="productCalculatedInfo.averageCustomerRating"/>
> - <store-value value-field="productCalculatedInfo"/>
> - </else>
> - </if-empty>
> - </simple-method>
> - <simple-method method-name="copyToProductVariants" short-description="Updates the Product's Variants">
> - <set value="copyToProductVariants" field="callingMethodName"/>
> - <set value="CREATE" field="checkAction"/>
> - <call-simple-method method-name="checkProductRelatedPermission"/>
> - <set value="DELETE" field="checkAction"/>
> - <call-simple-method method-name="checkProductRelatedPermission"/>
> - <check-errors/>
> -
> - <set from-field="parameters.virtualProductId" field="productFindContext.productId"/>
> - <find-by-primary-key entity-name="Product" map="productFindContext" value-field="oldProduct"/>
> -
> - <set from-field="parameters.virtualProductId" field="variantsFindContext.productId"/>
> - <set value="PRODUCT_VARIANT" field="variantsFindContext.productAssocTypeId"/>
> - <find-by-and entity-name="ProductAssoc" map="variantsFindContext" list="variants"/>
> - <filter-list-by-date list="variants"/>
> - <iterate list="variants" entry="newProduct">
> - <set from-field="newProduct.productIdTo" field="productVariantContext.productId"/>
> - <!-- if requested, duplicate related data -->
> - <if-not-empty field="parameters.duplicatePrices">
> - <if-not-empty field="parameters.removeBefore">
> - <find-by-and entity-name="ProductPrice" map="productVariantContext" list="foundVariantValues"/>
> - <iterate list="foundVariantValues" entry="foundVariantValue">
> - <remove-value value-field="foundVariantValue"/>
> - </iterate>
> - </if-not-empty>
> - <find-by-and entity-name="ProductPrice" map="productFindContext" list="foundValues"/>
> - <iterate list="foundValues" entry="foundValue">
> - <clone-value value-field="foundValue" new-value-field="newTempValue"/>
> - <set from-field="newProduct.productIdTo" field="newTempValue.productId"/>
> - <create-value value-field="newTempValue"/>
> - </iterate>
> - </if-not-empty>
> - <if-not-empty field="parameters.duplicateIDs">
> - <if-not-empty field="parameters.removeBefore">
> - <find-by-and entity-name="GoodIdentification" map="productVariantContext" list="foundVariantValues"/>
> - <iterate list="foundVariantValues" entry="foundVariantValue">
> - <remove-value value-field="foundVariantValue"/>
> - </iterate>
> - </if-not-empty>
> - <find-by-and entity-name="GoodIdentification" map="productFindContext" list="foundValues"/>
> - <iterate list="foundValues" entry="foundValue">
> - <clone-value value-field="foundValue" new-value-field="newTempValue"/>
> - <set from-field="newProduct.productIdTo" field="newTempValue.productId"/>
> - <create-value value-field="newTempValue"/>
> - </iterate>
> - </if-not-empty>
> - <if-not-empty field="parameters.duplicateContent">
> - <if-not-empty field="parameters.removeBefore">
> - <find-by-and entity-name="ProductContent" map="productVariantContext" list="foundVariantValues"/>
> - <iterate list="foundVariantValues" entry="foundVariantValue">
> - <remove-value value-field="foundVariantValue"/>
> - </iterate>
> - </if-not-empty>
> - <find-by-and entity-name="ProductContent" map="productFindContext" list="foundValues"/>
> - <iterate list="foundValues" entry="foundValue">
> - <clone-value value-field="foundValue" new-value-field="newTempValue"/>
> - <set from-field="newProduct.productIdTo" field="newTempValue.productId"/>
> - <create-value value-field="newTempValue"/>
> - </iterate>
> - </if-not-empty>
> - <if-not-empty field="parameters.duplicateCategoryMembers">
> - <if-not-empty field="parameters.removeBefore">
> - <find-by-and entity-name="ProductCategoryMember" map="productVariantContext" list="foundVariantValues"/>
> - <iterate list="foundVariantValues" entry="foundVariantValue">
> - <remove-value value-field="foundVariantValue"/>
> - </iterate>
> - </if-not-empty>
> - <find-by-and entity-name="ProductCategoryMember" map="productFindContext" list="foundValues"/>
> - <iterate list="foundValues" entry="foundValue">
> - <clone-value value-field="foundValue" new-value-field="newTempValue"/>
> - <set from-field="newProduct.productIdTo" field="newTempValue.productId"/>
> - <create-value value-field="newTempValue"/>
> - </iterate>
> - </if-not-empty>
> - <if-not-empty field="parameters.duplicateAttributes">
> - <if-not-empty field="parameters.removeBefore">
> - <find-by-and entity-name="ProductAttribute" map="productVariantContext" list="foundVariantValues"/>
> - <iterate list="foundVariantValues" entry="foundVariantValue">
> - <remove-value value-field="foundVariantValue"/>
> - </iterate>
> - </if-not-empty>
> - <find-by-and entity-name="ProductAttribute" map="productFindContext" list="foundValues"/>
> - <iterate list="foundValues" entry="foundValue">
> - <clone-value value-field="foundValue" new-value-field="newTempValue"/>
> - <set from-field="newProduct.productIdTo" field="newTempValue.productId"/>
> - <create-value value-field="newTempValue"/>
> - </iterate>
> - </if-not-empty>
> - <if-not-empty field="parameters.duplicateFacilities">
> - <if-not-empty field="parameters.removeBefore">
> - <find-by-and entity-name="ProductFacility" map="productVariantContext" list="foundVariantValues"/>
> - <iterate list="foundVariantValues" entry="foundVariantValue">
> - <remove-value value-field="foundVariantValue"/>
> - </iterate>
> - </if-not-empty>
> - <find-by-and entity-name="ProductFacility" map="productFindContext" list="foundValues"/>
> - <iterate list="foundValues" entry="foundValue">
> - <clone-value value-field="foundValue" new-value-field="newTempValue"/>
> - <set from-field="newProduct.productIdTo" field="newTempValue.productId"/>
> - <create-value value-field="newTempValue"/>
> - </iterate>
> - </if-not-empty>
> - <if-not-empty field="parameters.duplicateLocations">
> - <if-not-empty field="parameters.removeBefore">
> - <find-by-and entity-name="ProductFacilityLocation" map="productVariantContext" list="foundVariantValues"/>
> - <iterate list="foundVariantValues" entry="foundVariantValue">
> - <remove-value value-field="foundVariantValue"/>
> - </iterate>
> - </if-not-empty>
> - <find-by-and entity-name="ProductFacilityLocation" map="productFindContext" list="foundValues"/>
> - <iterate list="foundValues" entry="foundValue">
> - <clone-value value-field="foundValue" new-value-field="newTempValue"/>
> - <set from-field="newProduct.productIdTo" field="newTempValue.productId"/>
> - <create-value value-field="newTempValue"/>
> - </iterate>
> - </if-not-empty>
> - </iterate>
> - </simple-method>
> -
> - <!-- a method to centralize product security code, meant to be called in-line with
> - call-simple-method, and the checkAction and callingMethodName attributes should be in the method context -->
> - <simple-method method-name="checkProductRelatedPermission" short-description="Check Product Related Permission">
> - <if-empty field="callingMethodName">
> - <property-to-field resource="CommonUiLabels" property="CommonPermissionThisOperation" field="callingMethodName"/>
> - </if-empty>
> - <if-empty field="checkAction">
> - <set value="UPDATE" field="checkAction"/>
> - </if-empty>
> -
> - <!-- find all role-categories that this product is a member of -->
> - <if>
> - <condition>
> - <not><if-has-permission permission="CATALOG" action="_${checkAction}"/></not>
> - </condition>
> - <then>
> - <set from-field="parameters.productId" field="lookupRoleCategoriesMap.productId"/>
> - <set from-field="userLogin.partyId" field="lookupRoleCategoriesMap.partyId"/>
> - <set value="LTD_ADMIN" field="lookupRoleCategoriesMap.roleTypeId"/>
> - <find-by-and entity-name="ProductCategoryMemberAndRole" map="lookupRoleCategoriesMap" list="roleCategories"/>
> - <filter-list-by-date list="roleCategories"/>
> - <filter-list-by-date list="roleCategories" from-field-name="roleFromDate" thru-field-name="roleThruDate"/>
> - </then>
> - </if>
> - <if>
> - <condition>
> - <not>
> - <or>
> - <if-has-permission permission="CATALOG" action="_${checkAction}"/>
> - <and>
> - <if-has-permission permission="CATALOG_ROLE" action="_${checkAction}"/>
> - <not><if-empty field="roleCategories"/></not>
> - </and>
> - <and>
> - <not><if-empty field="alternatePermissionRoot"/></not>
> - <if-has-permission permission="${alternatePermissionRoot}" action="_${checkAction}"/>
> - </and>
> - </or>
> - </not>
> - </condition>
> - <then>
> - <set field="checkActionLabel" value="${groovy: 'ProductCatalog' + checkAction.charAt(0) + checkAction.substring(1).toLowerCase() + 'PermissionError'}"/>
> - <set field="resourceDescription" from-field="callingMethodName"/>
> - <add-error>
> - <fail-property resource="ProductUiLabels" property="${checkActionLabel}"/>
> - </add-error>
> - </then>
> - </if>
> - </simple-method>
> - <simple-method method-name="productGenericPermission" short-description="Main permission logic">
> - <set field="mainAction" from-field="parameters.mainAction"/>
> - <if-empty field="mainAction">
> - <add-error>
> - <fail-property resource="ProductUiLabels" property="ProductMissingMainActionInPermissionService"/>
> - </add-error>
> - <check-errors/>
> - </if-empty>
> -
> - <set field="callingMethodName" from-field="parameters.resourceDescription"/>
> - <set field="checkAction" from-field="parameters.mainAction"/>
> - <call-simple-method method-name="checkProductRelatedPermission"/>
> -
> - <if-empty field="error_list">
> - <set field="hasPermission" type="Boolean" value="true"/>
> - <field-to-result field="hasPermission"/>
> -
> - <else>
> - <property-to-field resource="ProductUiLabels" property="ProductPermissionError" field="failMessage"/>
> - <set field="hasPermission" type="Boolean" value="false"/>
> - <field-to-result field="hasPermission"/>
> - <field-to-result field="failMessage"/>
> - </else>
> - </if-empty>
> - </simple-method>
> - <simple-method method-name="productPriceGenericPermission" short-description="product price permission logic">
> - <set field="mainAction" from-field="parameters.mainAction"/>
> - <if-empty field="mainAction">
> - <add-error>
> - <fail-property resource="ProductUiLabels" property="ProductMissingMainActionInPermissionService"/>
> - </add-error>
> - <check-errors/>
> - </if-empty>
> - <check-permission permission="CATALOG_PRICE_MAINT">
> - <fail-property resource="ProductUiLabels" property="ProductPriceMaintPermissionError"/>
> - </check-permission>
> - <call-simple-method method-name="checkProductRelatedPermission"/>
> - <if-empty field="error_list">
> - <set field="hasPermission" type="Boolean" value="true"/>
> - <field-to-result field="hasPermission"/>
> - <else>
> - <property-to-field resource="ProductUiLabels" property="ProductPermissionError" field="failMessage"/>
> - <set field="hasPermission" type="Boolean" value="false"/>
> - <field-to-result field="hasPermission"/>
> - <field-to-result field="failMessage"/>
> - </else>
> - </if-empty>
> - </simple-method>
> -
> - <!-- ================================================================ -->
> - <!-- ProductRole Services -->
> - <!-- ================================================================ -->
> -
> - <simple-method method-name="addPartyToProduct" short-description="Add Party to Product">
> - <set value="addPartyToProduct" field="callingMethodName"/>
> - <set value="CREATE" field="checkAction"/>
> - <call-simple-method method-name="checkProductRelatedPermission"/>
> - <check-errors/>
> -
> - <make-value entity-name="ProductRole" value-field="newEntity"/>
> - <set-pk-fields map="parameters" value-field="newEntity"/>
> - <set-nonpk-fields map="parameters" value-field="newEntity"/>
> -
> - <if-empty field="newEntity.fromDate">
> - <now-timestamp field="newEntity.fromDate"/>
> - </if-empty>
> -
> - <create-value value-field="newEntity"/>
> - </simple-method>
> - <simple-method method-name="updatePartyToProduct" short-description="Update Party to Product">
> - <set value="updatePartyToProduct" field="callingMethodName"/>
> - <set value="UPDATE" field="checkAction"/>
> - <call-simple-method method-name="checkProductRelatedPermission"/>
> - <check-errors/>
> -
> - <make-value entity-name="ProductRole" value-field="lookupPKMap"/>
> - <set-pk-fields map="parameters" value-field="lookupPKMap"/>
> - <find-by-primary-key entity-name="ProductRole" map="lookupPKMap" value-field="lookedUpValue"/>
> - <set-nonpk-fields map="parameters" value-field="lookedUpValue"/>
> - <store-value value-field="lookedUpValue"/>
> - </simple-method>
> - <simple-method method-name="removePartyFromProduct" short-description="Remove Party From Product">
> - <set value="removePartyFromProduct" field="callingMethodName"/>
> - <set value="DELETE" field="checkAction"/>
> - <call-simple-method method-name="checkProductRelatedPermission"/>
> - <check-errors/>
> -
> - <make-value entity-name="ProductRole" value-field="lookupPKMap"/>
> - <set-pk-fields map="parameters" value-field="lookupPKMap"/>
> - <find-by-primary-key entity-name="ProductRole" map="lookupPKMap" value-field="lookedUpValue"/>
> - <remove-value value-field="lookedUpValue"/>
> - </simple-method>
> -
> - <!-- ProductCategoryGlAccount methods -->
> - <simple-method method-name="createProductCategoryGlAccount" short-description="Create a ProductCategoryGlAccount">
> - <set value="createProductCategoryGlAccount" field="callingMethodName"/>
> - <set value="CREATE" field="checkAction"/>
> - <call-simple-method method-name="checkProductRelatedPermission"/>
> - <check-errors/>
> -
> - <make-value entity-name="ProductCategoryGlAccount" value-field="newEntity"/>
> - <set-nonpk-fields map="parameters" value-field="newEntity"/>
> - <set-pk-fields map="parameters" value-field="newEntity"/>
> - <create-value value-field="newEntity"/>
> - </simple-method>
> -
> - <simple-method method-name="updateProductCategoryGlAccount" short-description="Update a ProductCategoryGlAccount">
> - <set value="updateProductCategoryGlAccount" field="callingMethodName"/>
> - <set value="UPDATE" field="checkAction"/>
> - <call-simple-method method-name="checkProductRelatedPermission"/>
> - <check-errors/>
> -
> - <entity-one entity-name="ProductCategoryGlAccount" value-field="lookedUpValue"/>
> - <set-nonpk-fields map="parameters" value-field="lookedUpValue"/>
> - <store-value value-field="lookedUpValue"/>
> - </simple-method>
> -
> - <simple-method method-name="deleteProductCategoryGlAccount" short-description="Delete a ProductCategoryGlAccount">
> - <set value="deleteProductCategoryGlAccount" field="callingMethodName"/>
> - <set value="DELETE" field="checkAction"/>
> - <call-simple-method method-name="checkProductRelatedPermission"/>
> - <check-errors/>
> -
> - <entity-one entity-name="ProductCategoryGlAccount" value-field="lookedUpValue"/>
> - <remove-value value-field="lookedUpValue"/>
> - </simple-method>
> -
> - <!-- Product GroupOrder Services -->
> - <simple-method method-name="createProductGroupOrder" short-description="Create ProductGroupOrder">
> - <make-value entity-name="ProductGroupOrder" value-field="newEntity"/>
> - <make-next-seq-id value-field="newEntity" seq-field-name="groupOrderId"/>
> - <field-to-result field="newEntity.groupOrderId" result-name="groupOrderId"/>
> - <set-nonpk-fields map="parameters" value-field="newEntity"/>
> - <create-value value-field="newEntity"/>
> - </simple-method>
> -
> - <simple-method method-name="updateProductGroupOrder" short-description="Update ProductGroupOrder">
> - <entity-one entity-name="ProductGroupOrder" value-field="productGroupOrder"/>
> - <set-nonpk-fields map="parameters" value-field="productGroupOrder"/>
> - <store-value value-field="productGroupOrder"/>
> -
> - <if-compare field="productGroupOrder.statusId" operator="equals" value="GO_CREATED">
> - <entity-one entity-name="JobSandbox" value-field="jobSandbox">
> - <field-map field-name="jobId" from-field="productGroupOrder.jobId"/>
> - </entity-one>
> - <if-not-empty field="jobSandbox">
> - <set field="jobSandbox.runTime" from-field="parameters.thruDate"/>
> - <store-value value-field="jobSandbox"/>
> - </if-not-empty>
> - </if-compare>
> - </simple-method>
> -
> - <simple-method method-name="deleteProductGroupOrder" short-description="Delete ProductGroupOrder">
> - <entity-and entity-name="OrderItemGroupOrder" list="orderItemGroupOrders">
> - <field-map field-name="groupOrderId" from-field="parameters.groupOrderId"/>
> - </entity-and>
> - <iterate list="orderItemGroupOrders" entry="orderItemGroupOrder">
> - <remove-value value-field="orderItemGroupOrder"/>
> - </iterate>
> -
> - <entity-one entity-name="ProductGroupOrder" value-field="productGroupOrder"/>
> - <remove-value value-field="productGroupOrder"/>
> -
> - <entity-one entity-name="JobSandbox" value-field="jobSandbox">
> - <field-map field-name="jobId" from-field="productGroupOrder.jobId"/>
> - </entity-one>
> - <remove-value value-field="jobSandbox"/>
> -
> - <entity-and entity-name="JobSandbox" list="jobSandboxList">
> - <field-map field-name="runtimeDataId" from-field="jobSandbox.runtimeDataId"/>
> - </entity-and>
> - <iterate list="jobSandboxList" entry="jobSandboxRelatedRuntimeData">
> - <remove-value value-field="jobSandboxRelatedRuntimeData"/>
> - </iterate>
> -
> - <entity-one entity-name="RuntimeData" value-field="runtimeData">
> - <field-map field-name="runtimeDataId" from-field="jobSandbox.runtimeDataId"/>
> - </entity-one>
> - <remove-value value-field="runtimeData"/>
> - </simple-method>
> -
> - <simple-method method-name="createJobForProductGroupOrder" short-description="Create ProductGroupOrder">
> - <entity-one entity-name="ProductGroupOrder" value-field="productGroupOrder"/>
> - <if-empty field="productGroupOrder.jobId">
> - <!-- Create RuntimeData For ProductGroupOrder -->
> - <set field="runtimeDataMap.groupOrderId" from-field="parameters.groupOrderId"/>
> - <call-class-method class-name="org.apache.ofbiz.entity.serialize.XmlSerializer" method-name="serialize" ret-field="runtimeInfo">
> - <field field="runtimeDataMap" type="Object"/>
> - </call-class-method>
> - <make-value entity-name="RuntimeData" value-field="runtimeData"/>
> - <sequenced-id sequence-name="RuntimeData" field="runtimeData.runtimeDataId"/>
> - <set field="runtimeDataId" from-field="runtimeData.runtimeDataId"/>
> - <set field="runtimeData.runtimeInfo" from-field="runtimeInfo"/>
> - <create-value value-field="runtimeData"/>
> -
> - <!-- Create Job For ProductGroupOrder -->
> - <!-- FIXME: Jobs should not be manually created -->
> - <make-value entity-name="JobSandbox" value-field="jobSandbox"/>
> - <sequenced-id sequence-name="JobSandbox" field="jobSandbox.jobId"/>
> - <set field="jobId" from-field="jobSandbox.jobId"/>
> - <set field="jobSandbox.jobName" value="Check ProductGroupOrder Expired"/>
> - <set field="jobSandbox.runTime" from-field="parameters.thruDate"/>
> - <set field="jobSandbox.poolId" value="pool"/>
> - <set field="jobSandbox.statusId" value="SERVICE_PENDING"/>
> - <set field="jobSandbox.serviceName" value="checkProductGroupOrderExpired"/>
> - <set field="jobSandbox.runAsUser" value="system"/>
> - <set field="jobSandbox.runtimeDataId" from-field="runtimeDataId"/>
> - <set field="jobSandbox.maxRecurrenceCount" value="1" type="Long"/>
> - <set field="jobSandbox.priority" value="50" type="Long"/>
> - <create-value value-field="jobSandbox"/>
> -
> - <set field="productGroupOrder.jobId" from-field="jobId"/>
> - <store-value value-field="productGroupOrder"/>
> - </if-empty>
> - </simple-method>
> -
> - <simple-method method-name="checkOrderItemForProductGroupOrder" short-description="Check OrderItem For ProductGroupOrder">
> - <entity-and entity-name="OrderItem" list="orderItems">
> - <field-map field-name="orderId" from-field="parameters.orderId"/>
> - </entity-and>
> - <iterate list="orderItems" entry="orderItem">
> - <set field="productId" from-field="orderItem.productId"/>
> - <entity-one entity-name="Product" value-field="product">
> - <field-map field-name="productId" from-field="orderItem.productId"/>
> - </entity-one>
> - <if-compare field="product.isVariant" operator="equals" value="Y">
> - <entity-and entity-name="ProductAssoc" list="variantProductAssocs" filter-by-date="true">
> - <field-map field-name="productIdTo" from-field="orderItem.productId"/>
> - <field-map field-name="productAssocTypeId" value="PRODUCT_VARIANT"/>
> - </entity-and>
> - <first-from-list list="variantProductAssocs" entry="variantProductAssoc"/>
> - <set field="productId" from-field="variantProductAssoc.productId"/>
> - </if-compare>
> -
> - <entity-and entity-name="ProductGroupOrder" list="productGroupOrders" filter-by-date="true">
> - <field-map field-name="productId" from-field="productId"/>
> - </entity-and>
> - <if-not-empty field="productGroupOrders">
> - <first-from-list list="productGroupOrders" entry="productGroupOrder"/>
> - <calculate field="productGroupOrder.soldOrderQty">
> - <calcop operator="add" field="productGroupOrder.soldOrderQty">
> - <calcop operator="get" field="orderItem.quantity"/>
> - </calcop>
> - </calculate>
> - <store-value value-field="productGroupOrder"/>
> -
> - <set field="createOrderItemGroupOrderMap.orderId" from-field="orderItem.orderId"/>
> - <set field="createOrderItemGroupOrderMap.orderItemSeqId" from-field="orderItem.orderItemSeqId"/>
> - <set field="createOrderItemGroupOrderMap.groupOrderId" from-field="productGroupOrder.groupOrderId"/>
> - <call-service service-name="createOrderItemGroupOrder" in-map-name="createOrderItemGroupOrderMap"/>
> - </if-not-empty>
> - </iterate>
> - </simple-method>
> -
> - <simple-method method-name="cancleOrderItemGroupOrder" short-description="Cancle OrderItemGroupOrder">
> - <if-not-empty field="parameters.orderItemSeqId">
> - <entity-and entity-name="OrderItem" list="orderItems">
> - <field-map field-name="orderId" from-field="parameters.orderId"/>
> - <field-map field-name="orderItemSeqId" from-field="parameters.orderItemSeqId" />
> - </entity-and>
> - <else>
> - <entity-and entity-name="OrderItem" list="orderItems">
> - <field-map field-name="orderId" from-field="parameters.orderId"/>
> - </entity-and>
> - </else>
> - </if-not-empty>
> - <iterate list="orderItems" entry="orderItem">
> - <entity-and entity-name="OrderItemGroupOrder" list="orderItemGroupOrders">
> - <field-map field-name="orderId" from-field="orderItem.orderId"/>
> - <field-map field-name="orderItemSeqId" from-field="orderItem.orderItemSeqId"/>
> - </entity-and>
> - <if-not-empty field="orderItemGroupOrders">
> - <first-from-list list="orderItemGroupOrders" entry="orderItemGroupOrder"/>
> - <entity-one entity-name="ProductGroupOrder" value-field="productGroupOrder">
> - <field-map field-name="groupOrderId" from-field="orderItemGroupOrder.groupOrderId"/>
> - </entity-one>
> - <if-not-empty field="productGroupOrder">
> - <if-compare field="productGroupOrder.statusId" operator="equals" value="GO_CREATED">
> - <if-compare field="orderItem.statusId" operator="equals" value="ITEM_CANCELLED">
> - <if-not-empty field="orderItem.cancelQuantity">
> - <set field="cancelQuantity" from-field="orderItem.cancelQuantity"/>
> - <else>
> - <set field="cancelQuantity" from-field="orderItem.quantity"/>
> - </else>
> - </if-not-empty>
> - <calculate field="productGroupOrder.soldOrderQty">
> - <calcop operator="subtract" field="productGroupOrder.soldOrderQty">
> - <calcop operator="get" field="cancelQuantity"/>
> - </calcop>
> - </calculate>
> - </if-compare>
> - <store-value value-field="productGroupOrder"/>
> - <remove-value value-field="orderItemGroupOrder"/>
> - </if-compare>
> - </if-not-empty>
> - </if-not-empty>
> - </iterate>
> - </simple-method>
> -
> - <simple-method method-name="checkProductGroupOrderExpired" short-description="Check ProductGroupOrder Expired">
> - <entity-one entity-name="ProductGroupOrder" value-field="productGroupOrder"/>
> - <if-not-empty field="productGroupOrder">
> - <if-compare field="productGroupOrder.soldOrderQty" operator="greater-equals" value="${productGroupOrder.reqOrderQty}">
> - <set field="newItemStatusId" value="ITEM_APPROVED"/>
> - <set field="groupOrderStatusId" value="GO_SUCCESS"/>
> - <else>
> - <set field="newItemStatusId" value="ITEM_CANCELLED"/>
> - <set field="groupOrderStatusId" value="GO_CANCELLED"/>
> - </else>
> - </if-compare>
> -
> - <set field="updateProductGroupOrderMap.groupOrderId" from-field="productGroupOrder.groupOrderId"/>
> - <set field="updateProductGroupOrderMap.statusId" from-field="groupOrderStatusId"/>
> - <call-service service-name="updateProductGroupOrder" in-map-name="updateProductGroupOrderMap"/>
> -
> - <entity-and entity-name="OrderItemGroupOrder" list="orderItemGroupOrders">
> - <field-map field-name="groupOrderId" from-field="productGroupOrder.groupOrderId"/>
> - </entity-and>
> - <iterate list="orderItemGroupOrders" entry="orderItemGroupOrder">
> - <set field="changeOrderItemStatusMap.orderId" from-field="orderItemGroupOrder.orderId"/>
> - <set field="changeOrderItemStatusMap.orderItemSeqId" from-field="orderItemGroupOrder.orderItemSeqId"/>
> - <set field="changeOrderItemStatusMap.statusId" from-field="newItemStatusId"/>
> - <call-service service-name="changeOrderItemStatus" in-map-name="changeOrderItemStatusMap"/>
> - </iterate>
> - </if-not-empty>
> - </simple-method>
> -
> - <simple-method method-name="setProductReviewStatus" short-description="change the product review Status">
> - <set value="setProductReviewStatus" field="callingMethodName"/>
> - <set value="UPDATE" field="checkAction"/>
> - <call-simple-method method-name="checkProductRelatedPermission"/>
> - <check-errors/>
> -
> - <entity-one entity-name="ProductReview" value-field="productReview"/>
> - <if-not-empty field="productReview">
> - <if-compare-field field="productReview.statusId" to-field="parameters.statusId" operator="not-equals">
> - <entity-one entity-name="StatusValidChange" value-field="statusChange">
> - <field-map field-name="statusId" from-field="productReview.statusId"/>
> - <field-map field-name="statusIdTo" from-field="parameters.statusId"/>
> - </entity-one>
> - <if-empty field="statusChange">
> - <set field="msg" value="Status is not a valid change: from ${productReview.statusId} to ${parameters.statusId}"/>
> - <log level="error" message="${msg}"/>
> - <add-error>
> - <fail-property resource="ProductErrorUiLabels" property="ProductReviewErrorCouldNotChangeOrderStatusFromTo"/>
> - </add-error>
> - </if-empty>
> - </if-compare-field>
> - </if-not-empty>
> - <check-errors/>
> -
> - <set field="productReview.statusId" from-field="parameters.statusId"/>
> - <store-value value-field="productReview"/>
> - <field-to-result field="productReview.productReviewId" result-name="productReviewId"/>
> - </simple-method>
> -</simple-methods>
> diff --git a/applications/product/servicedef/services.xml b/applications/product/servicedef/services.xml
> index 4e821b4..b44cba7 100644
> --- a/applications/product/servicedef/services.xml
> +++ b/applications/product/servicedef/services.xml
> @@ -37,22 +37,22 @@ under the License.
> <override name="description" allow-html="safe"/>
> <override name="longDescription" allow-html="safe"/>
> </service>
> - <service name="createProduct" default-entity-name="Product" engine="simple"
> - location="component://product/minilang/product/product/ProductServices.xml" invoke="createProduct" auth="true">
> + <service name="createProduct" default-entity-name="Product" engine="groovy"
> + location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="createProduct" auth="true">
> <description>Create a Product</description>
> <implements service="interfaceProduct"/>
> <auto-attributes include="pk" mode="INOUT" optional="true"/>
> <override name="productTypeId" optional="false"/>
> <override name="internalName" optional="false"/>
> </service>
> - <service name="updateProduct" default-entity-name="Product" engine="simple"
> - location="component://product/minilang/product/product/ProductServices.xml" invoke="updateProduct" auth="true">
> + <service name="updateProduct" default-entity-name="Product" engine="groovy"
> + location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="updateProduct" auth="true">
> <description>Update a Product</description>
> <implements service="interfaceProduct"/>
> <auto-attributes include="pk" mode="IN" optional="false"/>
> </service>
> - <service name="updateProductQuickAdminName" default-entity-name="Product" engine="simple"
> - location="component://product/minilang/product/product/ProductServices.xml" invoke="updateProductQuickAdminName" auth="true">
> + <service name="updateProductQuickAdminName" default-entity-name="Product" engine="groovy"
> + location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="updateProductQuickAdminName" auth="true">
> <description>Update a Product from Quick Admin</description>
> <implements service="interfaceProduct"/>
> <auto-attributes include="pk" mode="IN" optional="false"/>
> @@ -63,8 +63,8 @@ under the License.
> <implements service="interfaceProduct"/>
> <auto-attributes include="pk" mode="IN" optional="false"/>
> </service>
> - <service name="duplicateProduct" engine="simple"
> - location="component://product/minilang/product/product/ProductServices.xml" invoke="duplicateProduct" auth="true">
> + <service name="duplicateProduct" engine="groovy"
> + location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="duplicateProduct" auth="true">
> <description>Duplicate a Product using a new productId</description>
> <attribute name="productId" type="String" mode="IN" optional="false"/>
> <attribute name="oldProductId" type="String" mode="IN" optional="false"/>
> @@ -89,8 +89,8 @@ under the License.
> <attribute name="removeFeatureAppls" type="String" mode="IN" optional="true"/>
> <attribute name="removeInventoryItems" type="String" mode="IN" optional="true"/>
> </service>
> - <service name="copyToProductVariants" engine="simple"
> - location="component://product/minilang/product/product/ProductServices.xml" invoke="copyToProductVariants" auth="true">
> + <service name="copyToProductVariants" engine="groovy"
> + location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="copyToProductVariants" auth="true">
> <description>Copy Virtual Product's data to the Variant Products</description>
> <attribute name="virtualProductId" type="String" mode="IN" optional="false"/>
> <attribute name="removeBefore" type="String" mode="IN" optional="true"/>
> @@ -151,40 +151,40 @@ under the License.
> <permission-service service-name="productGenericPermission" main-action="DELETE"/>
> <auto-attributes include="pk" mode="IN" optional="false"/>
> </service>
> - <service name="deleteProductKeywords" engine="simple"
> - location="component://product/minilang/product/product/ProductServices.xml" invoke="deleteProductKeywords" auth="true">
> + <service name="deleteProductKeywords" engine="groovy"
> + location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="deleteProductKeywords" auth="true">
> <description>Delete all the keywords of a product</description>
> <permission-service service-name="productGenericPermission" main-action="DELETE"/>
> <attribute name="productId" type="String" mode="IN" optional="false"/>
> </service>
> - <service name="indexProductKeywords" engine="simple"
> - location="component://product/minilang/product/product/ProductServices.xml" invoke="indexProductKeywords" auth="false">
> + <service name="indexProductKeywords" engine="groovy"
> + location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="indexProductKeywords" auth="false">
> <description>Index the Keywords for a Product</description>
> <attribute name="productId" type="String" mode="IN" optional="false"/>
> <attribute name="productInstance" type="org.apache.ofbiz.entity.GenericValue" mode="IN" optional="true"/>
> </service>
> - <service name="forceIndexProductKeywords" engine="simple"
> - location="component://product/minilang/product/product/ProductServices.xml" invoke="forceIndexProductKeywords" auth="true">
> + <service name="forceIndexProductKeywords" engine="groovy"
> + location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="forceIndexProductKeywords" auth="true">
> <description>Induce all the keywords of a product, ignoring the flag in the Product.autoCreateKeywords flag</description>
> <permission-service service-name="productGenericPermission" main-action="CREATE"/>
> <attribute name="productId" type="String" mode="IN" optional="false"/>
> </service>
>
> - <service name="discontinueProductSales" engine="simple"
> - location="component://product/minilang/product/product/ProductServices.xml" invoke="discontinueProductSales" auth="false">
> + <service name="discontinueProductSales" engine="groovy"
> + location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="discontinueProductSales" auth="false">
> <description>Discontinue Product Sales</description>
> <attribute name="productId" type="String" mode="IN" optional="false"/>
> </service>
>
> - <service name="countProductView" engine="simple"
> - location="component://product/minilang/product/product/ProductServices.xml" invoke="countProductView" auth="false">
> + <service name="countProductView" engine="groovy"
> + location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="countProductView" auth="false">
> <description>count Product View</description>
> <attribute name="productId" type="String" mode="IN" optional="false"/>
> <attribute name="weight" type="Long" mode="IN" optional="true"/>
> </service>
>
> - <service name="createProductReview" engine="simple"
> - location="component://product/minilang/product/product/ProductServices.xml" invoke="createProductReview" auth="true">
> + <service name="createProductReview" engine="groovy"
> + location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="createProductReview" auth="true">
> <description>Create a product review entity</description>
> <auto-attributes entity-name="ProductReview" mode="IN" include="nonpk" optional="true"/>
> <attribute name="productReviewId" type="String" mode="OUT" optional="false"/>
> @@ -192,8 +192,8 @@ under the License.
> <override name="productId" optional="false"/>
> <override name="productRating" optional="false"/>
> </service>
> - <service name="updateProductReview" engine="simple" default-entity-name="ProductReview"
> - location="component://product/minilang/product/product/ProductServices.xml" invoke="updateProductReview" auth="true">
> + <service name="updateProductReview" engine="groovy" default-entity-name="ProductReview"
> + location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="updateProductReview" auth="true">
> <description>Updates a product review record</description>
> <required-permissions join-type="OR">
> <check-permission permission="CATALOG_UPDATE"/>
> @@ -202,8 +202,8 @@ under the License.
> <auto-attributes mode="IN" include="pk" optional="false"/>
> <auto-attributes mode="IN" include="nonpk" optional="true"/>
> </service>
> - <service name="setProductReviewStatus" engine="simple"
> - location="component://product/minilang/product/product/ProductServices.xml" invoke="setProductReviewStatus" auth="true">
> + <service name="setProductReviewStatus" engine="groovy"
> + location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="setProductReviewStatus" auth="true">
> <description>Updates a product review record</description>
> <required-permissions join-type="OR">
> <check-permission permission="CATALOG_UPDATE"/>
> @@ -784,8 +784,8 @@ under the License.
> <attribute name="fromDate" type="Timestamp" mode="IN" optional="false"/>
> </service>
>
> - <service name="addPartyToProduct" engine="simple"
> - location="component://product/minilang/product/product/ProductServices.xml" invoke="addPartyToProduct" auth="true">
> + <service name="addPartyToProduct" engine="groovy"
> + location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="addPartyToProduct" auth="true">
> <description>Add Party To Product</description>
> <attribute name="productId" type="String" mode="IN" optional="false"/>
> <attribute name="partyId" type="String" mode="IN" optional="false"/>
> @@ -795,8 +795,8 @@ under the License.
> <attribute name="sequenceNum" type="Long" mode="IN" optional="true"/>
> <attribute name="comments" type="String" mode="IN" optional="true"/>
> </service>
> - <service name="updatePartyToProduct" engine="simple"
> - location="component://product/minilang/product/product/ProductServices.xml" invoke="updatePartyToProduct" auth="true">
> + <service name="updatePartyToProduct" engine="groovy"
> + location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="updatePartyToProduct" auth="true">
> <description>Update Party To Product</description>
> <attribute name="productId" type="String" mode="IN" optional="false"/>
> <attribute name="partyId" type="String" mode="IN" optional="false"/>
> @@ -806,8 +806,8 @@ under the License.
> <attribute name="sequenceNum" type="Long" mode="IN" optional="true"/>
> <attribute name="comments" type="String" mode="IN" optional="true"/>
> </service>
> - <service name="removePartyFromProduct" engine="simple"
> - location="component://product/minilang/product/product/ProductServices.xml" invoke="removePartyFromProduct" auth="true">
> + <service name="removePartyFromProduct" engine="groovy"
> + location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="removePartyFromProduct" auth="true">
> <description>Remove Party From Product</description>
> <attribute name="productId" type="String" mode="IN" optional="false"/>
> <attribute name="partyId" type="String" mode="IN" optional="false"/>
> @@ -1240,16 +1240,16 @@ under the License.
> </service>
>
> <!-- Permission Services -->
> - <service name="productGenericPermission" engine="simple"
> - location="component://product/minilang/product/product/ProductServices.xml" invoke="productGenericPermission">
> + <service name="productGenericPermission" engine="groovy"
> + location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="productGenericPermission">
> <implements service="permissionInterface"/>
> </service>
> <service name="productCategoryGenericPermission" engine="groovy"
> location="component://product/groovyScripts/product/category/CategoryServices.groovy" invoke="productCategoryGenericPermission">
> <implements service="permissionInterface"/>
> </service>
> - <service name="productPriceGenericPermission" engine="simple"
> - location="component://product/minilang/product/product/ProductServices.xml" invoke="productPriceGenericPermission">
> + <service name="productPriceGenericPermission" engine="groovy"
> + location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="productPriceGenericPermission">
> <implements service="permissionInterface"/>
> </service>
> <service name="checkCategoryPermissionWithViewPurchaseAllow" engine="groovy"
> @@ -1285,20 +1285,20 @@ under the License.
> </service>
>
> <!-- ProductCategoryGlAccount Services -->
> - <service name="createProductCategoryGlAccount" default-entity-name="ProductCategoryGlAccount" engine="simple"
> - location="component://product/minilang/product/product/ProductServices.xml" invoke="createProductCategoryGlAccount" auth="true">
> + <service name="createProductCategoryGlAccount" default-entity-name="ProductCategoryGlAccount" engine="groovy"
> + location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="createProductCategoryGlAccount" auth="true">
> <description>Create a ProductCategoryGlAccount</description>
> <auto-attributes include="pk" mode="IN" optional="false"/>
> <auto-attributes include="nonpk" mode="IN" optional="false"/>
> </service>
> - <service name="updateProductCategoryGlAccount" default-entity-name="ProductCategoryGlAccount" engine="simple"
> - location="component://product/minilang/product/product/ProductServices.xml" invoke="updateProductCategoryGlAccount" auth="true">
> + <service name="updateProductCategoryGlAccount" default-entity-name="ProductCategoryGlAccount" engine="groovy"
> + location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="updateProductCategoryGlAccount" auth="true">
> <description>Update a ProductCategoryGlAccount</description>
> <auto-attributes include="pk" mode="IN" optional="false"/>
> <auto-attributes include="nonpk" mode="IN" optional="false"/>
> </service>
> - <service name="deleteProductCategoryGlAccount" default-entity-name="ProductCategoryGlAccount" engine="simple"
> - location="component://product/minilang/product/product/ProductServices.xml" invoke="deleteProductCategoryGlAccount" auth="true">
> + <service name="deleteProductCategoryGlAccount" default-entity-name="ProductCategoryGlAccount" engine="groovy"
> + location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="deleteProductCategoryGlAccount" auth="true">
> <description>Delete a ProductCategoryGlAccount</description>
> <auto-attributes include="pk" mode="IN" optional="false"/>
> </service>
> @@ -1608,48 +1608,48 @@ under the License.
> </service>
>
> <!-- Product GroupOrder Services -->
> - <service name="createProductGroupOrder" default-entity-name="ProductGroupOrder" engine="simple"
> - location="component://product/minilang/product/product/ProductServices.xml" invoke="createProductGroupOrder" auth="true">
> + <service name="createProductGroupOrder" default-entity-name="ProductGroupOrder" engine="groovy"
> + location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="createProductGroupOrder" auth="true">
> <description>Create ProductGroupOrder</description>
> <auto-attributes include="pk" mode="OUT" optional="false"/>
> <auto-attributes include="nonpk" mode="IN" optional="true"/>
> </service>
>
> - <service name="updateProductGroupOrder" default-entity-name="ProductGroupOrder" engine="simple"
> - location="component://product/minilang/product/product/ProductServices.xml" invoke="updateProductGroupOrder" auth="true">
> + <service name="updateProductGroupOrder" default-entity-name="ProductGroupOrder" engine="groovy"
> + location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="updateProductGroupOrder" auth="true">
> <description>Update ProductGroupOrder</description>
> <auto-attributes include="pk" mode="IN" optional="false"/>
> <auto-attributes include="nonpk" mode="IN" optional="true"/>
> </service>
>
> - <service name="deleteProductGroupOrder" default-entity-name="ProductGroupOrder" engine="simple"
> - location="component://product/minilang/product/product/ProductServices.xml" invoke="deleteProductGroupOrder" auth="true">
> + <service name="deleteProductGroupOrder" default-entity-name="ProductGroupOrder" engine="groovy"
> + location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="deleteProductGroupOrder" auth="true">
> <description>Delete ProductGroupOrder</description>
> <auto-attributes include="pk" mode="IN" optional="false"/>
> </service>
>
> - <service name="createJobForProductGroupOrder" default-entity-name="ProductGroupOrder" engine="simple"
> - location="component://product/minilang/product/product/ProductServices.xml" invoke="createJobForProductGroupOrder" auth="true">
> + <service name="createJobForProductGroupOrder" default-entity-name="ProductGroupOrder" engine="groovy"
> + location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="createJobForProductGroupOrder" auth="true">
> <description>Create Job For ProductGroupOrder</description>
> <auto-attributes include="pk" mode="IN" optional="false"/>
> <auto-attributes include="nonpk" mode="IN" optional="true"/>
> </service>
>
> - <service name="checkOrderItemForProductGroupOrder" engine="simple"
> - location="component://product/minilang/product/product/ProductServices.xml" invoke="checkOrderItemForProductGroupOrder" auth="true">
> + <service name="checkOrderItemForProductGroupOrder" engine="groovy"
> + location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="checkOrderItemForProductGroupOrder" auth="true">
> <description>Check OrderItem For ProductGroupOrder</description>
> <attribute name="orderId" mode="IN" type="String" optional="false"/>
> </service>
>
> - <service name="cancleOrderItemGroupOrder" engine="simple"
> - location="component://product/minilang/product/product/ProductServices.xml" invoke="cancleOrderItemGroupOrder" auth="true">
> + <service name="cancleOrderItemGroupOrder" engine="groovy"
> + location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="cancleOrderItemGroupOrder" auth="true">
> <description>Cancle OrderItemGroupOrder</description>
> <attribute name="orderId" mode="IN" type="String" optional="false"/>
> <attribute name="orderItemSeqId" type="String" mode="IN" optional="true"/>
> </service>
>
> - <service name="checkProductGroupOrderExpired" engine="simple"
> - location="component://product/minilang/product/product/ProductServices.xml" invoke="checkProductGroupOrderExpired" auth="true">
> + <service name="checkProductGroupOrderExpired" engine="groovy"
> + location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="checkProductGroupOrderExpired" auth="true">
> <description>Check ProductGroupOrder Expired</description>
> <attribute name="groupOrderId" mode="IN" type="String" optional="false"/>
> </service>
>