You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ofbiz.apache.org by nm...@apache.org on 2020/03/04 20:57:39 UTC

[ofbiz-framework] branch trunk updated (2b98727 -> 51837c8)

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

nmalin pushed a change to branch trunk
in repository https://gitbox.apache.org/repos/asf/ofbiz-framework.git.


    from 2b98727  Improved: Convert party/LookupServices.xml mini-lang to groovyDSL (OFBIZ-11362)
     new 80026c7  Improved: Convert ProductServices.xml mini lang to groovy (OFBIZ-10231)
     new 51837c8  Fixed: Convert ProductServices.xml mini lang to groovy: productPriceGenericPermission failed (OFBIZ-10231)

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../product/product/ProductServices.groovy         |   17 +-
 .../product/feature/ProductFeatureServices.xml     |    5 -
 .../product/inventory/InventoryServices.xml        |    9 +-
 .../minilang/product/price/PriceServices.xml       |    9 -
 .../minilang/product/product/ProductServices.xml   | 1051 --------------------
 applications/product/servicedef/services.xml       |   12 +
 .../product/servicedef/services_feature.xml        |    1 +
 7 files changed, 31 insertions(+), 1073 deletions(-)
 delete mode 100644 applications/product/minilang/product/product/ProductServices.xml


[ofbiz-framework] 01/02: Improved: Convert ProductServices.xml mini lang to groovy (OFBIZ-10231)

Posted by nm...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 80026c76c066f589a652bd00bb7699108cd66163
Author: Nicolas Malin <ni...@nereide.fr>
AuthorDate: Wed Mar 4 21:44:11 2020 +0100

    Improved: Convert ProductServices.xml mini lang to groovy
    (OFBIZ-10231)
    
    After groovy conversion the method checkProductRelatedPermission
    has been broken for other minilang method that didn't converted.
    
    To solve this problem, I created new permission service checkProductRelatedPermission
    and convert all previous minilang method call by a permission service on the related service call
---
 .../product/product/ProductServices.groovy         |   13 +-
 .../product/feature/ProductFeatureServices.xml     |    5 -
 .../product/inventory/InventoryServices.xml        |    9 +-
 .../minilang/product/price/PriceServices.xml       |    9 -
 .../minilang/product/product/ProductServices.xml   | 1051 --------------------
 applications/product/servicedef/services.xml       |   12 +
 .../product/servicedef/services_feature.xml        |    1 +
 7 files changed, 29 insertions(+), 1071 deletions(-)

diff --git a/applications/product/groovyScripts/product/product/ProductServices.groovy b/applications/product/groovyScripts/product/product/ProductServices.groovy
index 706e6eb..3182f16 100644
--- a/applications/product/groovyScripts/product/product/ProductServices.groovy
+++ b/applications/product/groovyScripts/product/product/ProductServices.groovy
@@ -527,7 +527,6 @@ def copyToProductVariants() {
 /**
  * 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 (!callingMethodName) {
@@ -538,7 +537,7 @@ def checkProductRelatedPermission(String callingMethodName, String checkAction)
     }
     List roleCategories = []
     // find all role-categories that this product is a member of
-    if (!security.hasEntityPermission("CATALOG", "_${checkAction}", parameters.userLogin)) {
+    if (parameters.productId && !security.hasEntityPermission("CATALOG", "_${checkAction}", parameters.userLogin)) {
         Map lookupRoleCategoriesMap = [productId : parameters.productId,
                                        partyId   : userLogin.partyId,
                                        roleTypeId: "LTD_ADMIN"]
@@ -560,6 +559,16 @@ def checkProductRelatedPermission(String callingMethodName, String checkAction)
 }
 
 /**
+ * call checkProductRelatedPermission function with support permission service interface
+ */
+def checkProductRelatedPermissionService() {
+    parameters.alternatePermissionRoot = parameters.altPermission
+    Map result = checkProductRelatedPermission(parameters.resourceDescription, parameters.mainAction)
+    result.hasPermission = ServiceUtil.isSuccess(result)
+    return result
+}
+
+/**
  * Main permission logic
  */
 def productGenericPermission() {
diff --git a/applications/product/minilang/product/feature/ProductFeatureServices.xml b/applications/product/minilang/product/feature/ProductFeatureServices.xml
index 835d6bc..6810a6a 100644
--- a/applications/product/minilang/product/feature/ProductFeatureServices.xml
+++ b/applications/product/minilang/product/feature/ProductFeatureServices.xml
@@ -22,11 +22,6 @@ under the License.
         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="applyFeatureToProductFromTypeAndCode" short-description="Apply Feature to Product using Feature Type and ID Code">
-        <set field="callingMethodName" value="applyFeatureToProductFromTypeAndCode"/>
-        <set field="checkAction" value="CREATE"/>
-        <call-simple-method method-name="checkProductRelatedPermission" xml-resource="component://product/minilang/product/product/ProductServices.xml"/>
-        <check-errors/>
-
         <!-- find the ProductFeatures by type and id code -->
         <entity-and entity-name="ProductFeature" list="productFeatures">
             <field-map field-name="productFeatureTypeId" from-field="parameters.productFeatureTypeId"/>
diff --git a/applications/product/minilang/product/inventory/InventoryServices.xml b/applications/product/minilang/product/inventory/InventoryServices.xml
index 56d15a3..64a01d5 100644
--- a/applications/product/minilang/product/inventory/InventoryServices.xml
+++ b/applications/product/minilang/product/inventory/InventoryServices.xml
@@ -90,10 +90,11 @@ under the License.
         <if-empty field="resourceDescription">
             <property-to-field resource="CommonUiLabels" property="CommonPermissionThisOperation" field="resourceDescription"/>
         </if-empty>
-        <set field="callingMethodName" from-field="resourceDescription"/>
-        <set field="checkAction" from-field="mainAction"/>
-        <set field="alternatePermissionRoot" value="FACILITY"/>
-        <call-simple-method method-name="checkProductRelatedPermission" xml-resource="component://product/minilang/product/product/ProductServices.xml"/>
+        <set field="parameters.altPermission" value="FACILITY"/>
+        <call-service service-name="checkProductRelatedPermission" in-map-name="parameters">
+            <result-to-result result-name="hasPermission"/>
+        </call-service>
+        <check-errors/>
         <if-empty field="error_list">
             <set field="hasPermission" type="Boolean" value="true"/>
             <field-to-result field="hasPermission"/>
diff --git a/applications/product/minilang/product/price/PriceServices.xml b/applications/product/minilang/product/price/PriceServices.xml
index b9ecfa7..cf67c4a 100644
--- a/applications/product/minilang/product/price/PriceServices.xml
+++ b/applications/product/minilang/product/price/PriceServices.xml
@@ -22,9 +22,6 @@ under the License.
         xmlns="http://ofbiz.apache.org/Simple-Method" xsi:schemaLocation="http://ofbiz.apache.org/Simple-Method http://ofbiz.apache.org/dtds/simple-methods.xsd">
     <!-- ProductPrice methods -->
     <simple-method method-name="createProductPrice" short-description="Create a Product Price">
-        <set field="callingMethodName" value="createProductPrice"/>
-        <set field="checkAction" value="CREATE"/>
-        <call-simple-method method-name="checkProductRelatedPermission" xml-resource="component://product/minilang/product/product/ProductServices.xml"/>
         <check-permission permission="CATALOG_PRICE_MAINT">
             <fail-property resource="ProductUiLabels" property="ProductPriceMaintPermissionError"/>
         </check-permission>
@@ -51,9 +48,6 @@ under the License.
         <create-value value-field="newEntity"/>
     </simple-method>
     <simple-method method-name="updateProductPrice" short-description="Update an ProductPrice">
-        <set field="callingMethodName" value="updateProductPrice"/>
-        <set field="checkAction" value="UPDATE"/>
-        <call-simple-method method-name="checkProductRelatedPermission" xml-resource="component://product/minilang/product/product/ProductServices.xml"/>
         <check-permission permission="CATALOG_PRICE_MAINT">
             <fail-property resource="ProductUiLabels" property="ProductPriceMaintPermissionError"/>
         </check-permission>
@@ -75,9 +69,6 @@ under the License.
         <store-value value-field="lookedUpValue"/>
     </simple-method>
     <simple-method method-name="deleteProductPrice" short-description="Delete an ProductPrice">
-        <set field="callingMethodName" value="deleteProductPrice"/>
-        <set field="checkAction" value="DELETE"/>
-        <call-simple-method method-name="checkProductRelatedPermission" xml-resource="component://product/minilang/product/product/ProductServices.xml"/>
         <check-permission permission="CATALOG_PRICE_MAINT">
             <fail-property resource="ProductUiLabels" property="ProductPriceMaintPermissionError"/>
         </check-permission>
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 249750a..999e479 100644
--- a/applications/product/servicedef/services.xml
+++ b/applications/product/servicedef/services.xml
@@ -255,6 +255,7 @@ under the License.
 
             If taxAuthGeoId or taxAuthPartyId empty then the taxInPrice field will be ignored.
         </description>
+        <permission-service service-name="checkProductRelatedPermission" main-action="CREATE"/>
         <auto-attributes include="pk" mode="IN" optional="false"/>
         <auto-attributes include="nonpk" mode="IN" optional="true">
             <exclude field-name="priceWithoutTax"/>
@@ -271,6 +272,7 @@ under the License.
     <service name="updateProductPrice" default-entity-name="ProductPrice" engine="simple"
                 location="component://product/minilang/product/price/PriceServices.xml" invoke="updateProductPrice" auth="true">
         <description>Update an ProductPrice</description>
+        <permission-service service-name="checkProductRelatedPermission" main-action="UPDATE"/>
         <auto-attributes include="pk" mode="IN" optional="false"/>
         <auto-attributes include="nonpk" mode="IN" optional="true">
             <exclude field-name="priceWithoutTax"/>
@@ -287,6 +289,7 @@ under the License.
     <service name="deleteProductPrice" default-entity-name="ProductPrice" engine="simple"
                 location="component://product/minilang/product/price/PriceServices.xml" invoke="deleteProductPrice" auth="true">
         <description>Delete an ProductPrice</description>
+        <permission-service service-name="checkProductRelatedPermission" main-action="DELETE"/>
         <auto-attributes include="pk" mode="IN" optional="false"/>
         <attribute name="oldPrice" type="BigDecimal" mode="OUT" optional="false"/>
     </service>
@@ -1834,4 +1837,13 @@ under the License.
         <description>Expire a ProdCatalogInvFacility Record</description>
         <auto-attributes mode="IN" include="pk"/>
     </service>
+
+    <!--Permission services -->
+
+    <service name="checkProductRelatedPermission" engine="groovy"
+             location="component://product/groovyScripts/product/product/ProductServices.groovy" invoke="checkProductRelatedPermissionService">
+        <description>Check Product Related Permission, a service to centralize product security code</description>
+        <implements service="permissionInterface"/>
+        <attribute name="productId" mode="IN" type="String" optional="true"/>
+    </service>
 </services>
diff --git a/applications/product/servicedef/services_feature.xml b/applications/product/servicedef/services_feature.xml
index a6c7395..f7e2320 100644
--- a/applications/product/servicedef/services_feature.xml
+++ b/applications/product/servicedef/services_feature.xml
@@ -80,6 +80,7 @@ under the License.
     <service name="applyFeatureToProductFromTypeAndCode" engine="simple"
                 location="component://product/minilang/product/feature/ProductFeatureServices.xml" invoke="applyFeatureToProductFromTypeAndCode" auth="true">
         <description>Apply a ProductFeature to a Product</description>
+        <permission-service service-name="checkProductRelatedPermission" main-action="CREATE"/>
         <attribute name="productId" type="String" mode="IN" optional="false"/>
         <attribute name="productFeatureTypeId" type="String" mode="IN" optional="false"/>
         <attribute name="idCode" type="String" mode="IN" optional="false"/>


[ofbiz-framework] 02/02: Fixed: Convert ProductServices.xml mini lang to groovy: productPriceGenericPermission failed (OFBIZ-10231)

Posted by nm...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 51837c8cbfe57344858ccdfb8a6fcc380318ab19
Author: Nicolas Malin <ni...@nereide.fr>
AuthorDate: Wed Mar 4 21:46:11 2020 +0100

    Fixed: Convert ProductServices.xml mini lang to groovy: productPriceGenericPermission failed
    (OFBIZ-10231)
    
    After groovy conversion the method productPriceGenericPermission failed to validate a user
    
    Problem due to bad call on hasEntityPermission instead of hasPermission
---
 .../product/groovyScripts/product/product/ProductServices.groovy      | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/applications/product/groovyScripts/product/product/ProductServices.groovy b/applications/product/groovyScripts/product/product/ProductServices.groovy
index 3182f16..21daa81 100644
--- a/applications/product/groovyScripts/product/product/ProductServices.groovy
+++ b/applications/product/groovyScripts/product/product/ProductServices.groovy
@@ -598,11 +598,11 @@ def productPriceGenericPermission() {
     }
 
     Map result = success()
-    if (!security.hasEntityPermission("CATALOG_PRICE_MAINT", null, parameters.userLogin)) {
+    if (!security.hasPermission("CATALOG_PRICE_MAINT", parameters.userLogin)) {
         result = error(UtilProperties.getMessage("ProductUiLabels",
                 "ProductPriceMaintPermissionError", parameters.locale))
     }
-    result.hasPermission = ServiceUtil.isSuccess(result) && checkProductRelatedPermission(null, null)
+    result.hasPermission = ServiceUtil.isSuccess(result) && checkProductRelatedPermission(parameters.resourceDescription, mainAction)
     if (!result.hasPermission) {
         result = fail(UtilProperties.getMessage("ProductUiLabels", "ProductPermissionError", parameters.locale))
     }