svn commit: r1735737 - in /ofbiz/trunk/framework/service: servicedef/services_test_se.xml src/org/ofbiz/service/engine/EntityAutoEngine.java src/org/ofbiz/service/test/ServiceEntityAutoTests.java

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

svn commit: r1735737 - in /ofbiz/trunk/framework/service: servicedef/services_test_se.xml src/org/ofbiz/service/engine/EntityAutoEngine.java src/org/ofbiz/service/test/ServiceEntityAutoTests.java

nmalin
Author: nmalin
Date: Sat Mar 19 08:04:16 2016
New Revision: 1735737

URL: http://svn.apache.org/viewvc?rev=1735737&view=rev
Log:
Related to the thread http://ofbiz.135035.n4.nabble.com/entity-auto-and-cancel-operation-td4676929.html on the dev mailing list and the issue OFBIZ-6892, this commit manage the action expire on entity-auto engine.
When you call expire on a entity :
 * if a date field is present on service attribute, OFBiz try to expire it with the given value or with now date
 * else try to expire thruDate field
 * else try to expire *ThruDate or thru*Date field
With the new action I refactoring the code to separate each action on dedicate function.
the unit test associate to expire action was updated.

Modified:
    ofbiz/trunk/framework/service/servicedef/services_test_se.xml
    ofbiz/trunk/framework/service/src/org/ofbiz/service/engine/EntityAutoEngine.java
    ofbiz/trunk/framework/service/src/org/ofbiz/service/test/ServiceEntityAutoTests.java

Modified: ofbiz/trunk/framework/service/servicedef/services_test_se.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/service/servicedef/services_test_se.xml?rev=1735737&r1=1735736&r2=1735737&view=diff
==============================================================================
--- ofbiz/trunk/framework/service/servicedef/services_test_se.xml (original)
+++ ofbiz/trunk/framework/service/servicedef/services_test_se.xml Sat Mar 19 08:04:16 2016
@@ -91,6 +91,15 @@ under the License.
         engine="entity-auto" default-entity-name="Testing" invoke="delete">
         <auto-attributes include="pk" mode="IN" optional="false"/>
     </service>
+    <service name="testEntityAutoExpireTestingNodeMember" auth="false"
+        engine="entity-auto" default-entity-name="TestingNodeMember" invoke="expire">
+        <auto-attributes include="pk" mode="IN" optional="false"/>
+    </service>
+    <service name="testEntityAutoExpireTestFieldType" auth="false"
+        engine="entity-auto" default-entity-name="TestFieldType" invoke="expire">
+        <auto-attributes include="pk" mode="IN" optional="false"/>
+        <attribute name="dateTimeField" mode="IN" type="Timestamp" optional="true"/>
+    </service>
 
     <!-- lock wait timeout retry testing services - a scenario that we can't do automatically with the single service because the parent owns the tx we have to have end before it will succeed -->
     <service name="testServiceLockWaitTimeoutRetryCantRecover" engine="java" auth="false" transaction-timeout="2"

Modified: ofbiz/trunk/framework/service/src/org/ofbiz/service/engine/EntityAutoEngine.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/service/src/org/ofbiz/service/engine/EntityAutoEngine.java?rev=1735737&r1=1735736&r2=1735737&view=diff
==============================================================================
--- ofbiz/trunk/framework/service/src/org/ofbiz/service/engine/EntityAutoEngine.java (original)
+++ ofbiz/trunk/framework/service/src/org/ofbiz/service/engine/EntityAutoEngine.java Sat Mar 19 08:04:16 2016
@@ -21,6 +21,7 @@ package org.ofbiz.service.engine;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.LinkedList;
+import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 
@@ -48,6 +49,7 @@ public final class EntityAutoEngine exte
 
     public static final String module = EntityAutoEngine.class.getName();
     public static final String resource = "ServiceErrorUiLabels";
+    private static final List<String> availableInvokeActionNames = UtilMisc.toList("create", "update", "delete", "expire");
 
     public EntityAutoEngine(ServiceDispatcher dispatcher) {
         super(dispatcher);
@@ -69,12 +71,10 @@ public final class EntityAutoEngine exte
         // static java service methods should be: public Map<String, Object> methodName(DispatchContext dctx, Map<String, Object> context)
         DispatchContext dctx = dispatcher.getLocalContext(localName);
         Locale locale = (Locale) parameters.get("locale");
-        Map<String, Object> localContext = new HashMap<String, Object>();
-        localContext.put("parameters", parameters);
         Map<String, Object> result = ServiceUtil.returnSuccess();
 
         // check the package and method names
-        if (modelService.invoke == null || (!"create".equals(modelService.invoke) && !"update".equals(modelService.invoke) && !"delete".equals(modelService.invoke))) {
+        if (modelService.invoke == null || !availableInvokeActionNames.contains(modelService.invoke)) {
             throw new GenericServiceException("In Service [" + modelService.name + "] the invoke value must be create, update, or delete for entity-auto engine");
         }
 
@@ -108,280 +108,401 @@ public final class EntityAutoEngine exte
                 }
             }
 
-            if ("create".equals(modelService.invoke)) {
-                GenericValue newEntity = dctx.getDelegator().makeValue(modelEntity.getEntityName());
+            switch (modelService.invoke) {
+            case "create":
+                result = invokeCreate(dctx, parameters, modelService, modelEntity, allPksInOnly, pkFieldNameOutOnly);
+                break;
+            case "update":
+                result = invokeUpdate(dctx, parameters, modelService, modelEntity, allPksInOnly);
+                break;
+            case "delete":
+                result = invokeDelete(dctx, parameters, modelService, modelEntity, allPksInOnly);
+                break;
+            case "expire":
+                result = invokeExpire(dctx, parameters, modelService, modelEntity, allPksInOnly);
+                if (ServiceUtil.isSuccess(result)) {
+                    result = invokeUpdate(dctx, parameters, modelService, modelEntity, allPksInOnly);
+                }
+                break;
+            default:
+                break;
+            }
+            GenericValue crudValue = (GenericValue) result.get("crudValue");
+            if (crudValue != null) {
+                result.remove("crudValue");
+                result.putAll(modelService.makeValid(crudValue, "OUT"));
+            }
+        } catch (GeneralException e) {
+            Debug.logError(e, "Error doing entity-auto operation for entity [" + modelEntity.getEntityName() + "] in service [" + modelService.name + "]: " + e.toString(), module);
+            return ServiceUtil.returnError(UtilProperties.getMessage(resource, "ServiceEntityAutoOperation", UtilMisc.toMap("entityName", modelEntity.getEntityName(), "serviceName", modelService.name,"errorString", e.toString()), locale));
+        }
 
-                boolean isSinglePk = modelEntity.getPksSize() == 1;
-                boolean isDoublePk = modelEntity.getPksSize() == 2;
-                Iterator<ModelField> pksIter = modelEntity.getPksIterator();
-
-                ModelField singlePkModeField = isSinglePk ? pksIter.next() : null;
-                ModelParam singlePkModelParam = isSinglePk ? modelService.getParam(singlePkModeField.getName()) : null;
-                boolean isSinglePkIn = isSinglePk ? singlePkModelParam.isIn() : false;
-                boolean isSinglePkOut = isSinglePk ? singlePkModelParam.isOut() : false;
-
-                ModelParam doublePkPrimaryInParam = null;
-                ModelParam doublePkSecondaryOutParam = null;
-                ModelField doublePkSecondaryOutField = null;
-                if (isDoublePk) {
-                    ModelField firstPkField = pksIter.next();
-                    ModelParam firstPkParam = modelService.getParam(firstPkField.getName());
-                    ModelField secondPkField = pksIter.next();
-                    ModelParam secondPkParam = modelService.getParam(secondPkField.getName());
-                    if (firstPkParam.isIn() && secondPkParam.isOut()) {
-                        doublePkPrimaryInParam = firstPkParam;
-                        doublePkSecondaryOutParam = secondPkParam;
-                        doublePkSecondaryOutField = secondPkField;
-                    } else if (firstPkParam.isOut() && secondPkParam.isIn()) {
-                        doublePkPrimaryInParam = secondPkParam;
-                        doublePkSecondaryOutParam = firstPkParam;
-                        doublePkSecondaryOutField = firstPkField;
-                    } else {
-                        // we don't have an IN and an OUT... so do nothing and leave them null
-                    }
-                }
+        return result;
+    }
 
-                if (isSinglePk && isSinglePkOut && !isSinglePkIn) {
-                    /*
-                     **** primary sequenced primary key ****
-                     *
-                    <auto-attributes include="pk" mode="OUT" optional="false"/>
-                     *
-                    <make-value entity-name="Example" value-name="newEntity"/>
-                    <sequenced-id-to-env sequence-name="Example" env-name="newEntity.exampleId"/> <!-- get the next sequenced ID -->
-                    <field-to-result field-name="newEntity.exampleId" result-name="exampleId"/>
-                    <set-nonpk-fields map-name="parameters" value-name="newEntity"/>
-                    <create-value value-name="newEntity"/>
-                     *
-                     */
-
-                    String sequencedId = dctx.getDelegator().getNextSeqId(modelEntity.getEntityName());
-                    newEntity.set(singlePkModeField.getName(), sequencedId);
-                } else if (isSinglePk && isSinglePkOut && isSinglePkIn) {
-                    /*
-                     **** primary sequenced key with optional override passed in ****
-                     *
-                    <auto-attributes include="pk" mode="INOUT" optional="true"/>
-                     *
-                    <make-value value-name="newEntity" entity-name="Product"/>
-                    <set-nonpk-fields map-name="parameters" value-name="newEntity"/>
-                    <set from-field="parameters.productId" field="newEntity.productId"/>
-                    <if-empty field="newEntity.productId">
-                        <sequenced-id-to-env sequence-name="Product" env-name="newEntity.productId"/>
-                    <else>
-                        <check-id field-name="productId" map-name="newEntity"/>
-                        <check-errors/>
-                    </else>
-                    </if-empty>
-                    <field-to-result field-name="productId" map-name="newEntity" result-name="productId"/>
-                    <create-value value-name="newEntity"/>
-                     *
-                     */
-
-                    Object pkValue = parameters.get(singlePkModelParam.name);
-                    if (UtilValidate.isEmpty(pkValue)) {
-                        pkValue = dctx.getDelegator().getNextSeqId(modelEntity.getEntityName());
-                    } else {
-                        // pkValue passed in, check and if there are problems return an error
+    private static Map<String, Object> invokeCreate(DispatchContext dctx, Map<String, Object> parameters, ModelService modelService, ModelEntity modelEntity, boolean allPksInOnly, List<String> pkFieldNameOutOnly)
+            throws GeneralException {
+        Locale locale = (Locale) parameters.get("locale");
+        Map<String, Object> result = ServiceUtil.returnSuccess();
 
-                        if (pkValue instanceof String) {
-                            StringBuffer errorDetails = new StringBuffer();
-                            if (!UtilValidate.isValidDatabaseId((String) pkValue, errorDetails)) {
-                                return ServiceUtil.returnError(UtilProperties.getMessage(resource, "ServiceParameterValueNotValid", UtilMisc.toMap("parameterName", singlePkModelParam.name,"errorDetails", errorDetails), locale));
-                            }
-                        }
-                    }
-                    newEntity.set(singlePkModeField.getName(), pkValue);
-                } else if (isDoublePk && doublePkPrimaryInParam != null && doublePkSecondaryOutParam != null) {
-                    /*
-                     **** secondary sequenced primary key ****
-                     *
-                    <auto-attributes include="pk" mode="IN" optional="false"/>
-                    <override name="exampleItemSeqId" mode="OUT"/> <!-- make this OUT rather than IN, we will automatically generate the next sub-sequence ID -->
-                     *
-                    <make-value entity-name="ExampleItem" value-name="newEntity"/>
-                    <set-pk-fields map-name="parameters" value-name="newEntity"/>
-                    <make-next-seq-id value-name="newEntity" seq-field-name="exampleItemSeqId"/> <!-- this finds the next sub-sequence ID -->
-                    <field-to-result field-name="newEntity.exampleItemSeqId" result-name="exampleItemSeqId"/>
-                    <set-nonpk-fields map-name="parameters" value-name="newEntity"/>
-                    <create-value value-name="newEntity"/>
-                     */
-
-                    newEntity.setPKFields(parameters, true);
-                    dctx.getDelegator().setNextSubSeqId(newEntity, doublePkSecondaryOutField.getName(), 5, 1);
-                } else if (allPksInOnly) {
-                    /*
-                     **** plain specified primary key ****
-                     *
-                    <auto-attributes include="pk" mode="IN" optional="false"/>
-                     *
-                    <make-value entity-name="Example" value-name="newEntity"/>
-                    <set-pk-fields map-name="parameters" value-name="newEntity"/>
-                    <set-nonpk-fields map-name="parameters" value-name="newEntity"/>
-                    <create-value value-name="newEntity"/>
-                     *
-                     */
-                    newEntity.setPKFields(parameters, true);
-                    //with all pks present on parameters, check if the entity is not already exists.
-                    GenericValue lookedUpValue = PrimaryKeyFinder.runFind(modelEntity, parameters, dctx.getDelegator(), false, true, null, null);
-                    if (lookedUpValue != null) {
-                        return ServiceUtil.returnError(UtilProperties.getMessage(resource, "ServiceValueFound", UtilMisc.toMap("pkFields", newEntity.getPkShortValueString()), locale));
-                    }
-                } else {
-                    /* We haven't all Pk and their are 3 or more, now check if isn't a associate entity with own sequence
-                    <set-pk-fields map="parameters" value-field="newEntity"/>
-                    <sequenced-id sequence-name="ExempleItemAssoc" field="newEntity.exempleItemAssocId"/>
-                    <create-value value-field="newEntity"/>
-                     */
-                    if (pkFieldNameOutOnly != null && pkFieldNameOutOnly.size() == 1) {
-                        newEntity.setPKFields(parameters, true);
-                        String pkFieldName = pkFieldNameOutOnly.getFirst();
-                        //if it's a fromDate, don't update it now, it's will be done next step
-                        if (! "fromDate".equals(pkFieldName)) {
-                            String pkValue = dctx.getDelegator().getNextSeqId(modelEntity.getEntityName());
-                            newEntity.set(pkFieldName, pkValue);
-                        }
-                    } else {
-                        throw new GenericServiceException("In Service [" + modelService.name + "] which uses the entity-auto engine with the create invoke option: " +
-                                "could not find a valid combination of primary key settings to do a known create operation; options include: " +
-                                "1. a single OUT pk for primary auto-sequencing, " +
-                                "2. a single INOUT pk for primary auto-sequencing with optional override, " +
-                                "3. a 2-part pk with one part IN (existing primary pk) and one part OUT (the secondary pk to sub-sequence), " +
-                                "4. a N-part pk with N-1 part IN and one party OUT only (missing pk is a sub-sequence mainly for entity assoc), " +
-                                "5. all pk fields are IN for a manually specified primary key");
-                    }
-                }
+        GenericValue newEntity = dctx.getDelegator().makeValue(modelEntity.getEntityName());
+
+        boolean isSinglePk = modelEntity.getPksSize() == 1;
+        boolean isDoublePk = modelEntity.getPksSize() == 2;
+        Iterator<ModelField> pksIter = modelEntity.getPksIterator();
+
+        ModelField singlePkModeField = isSinglePk ? pksIter.next() : null;
+        ModelParam singlePkModelParam = isSinglePk ? modelService.getParam(singlePkModeField.getName()) : null;
+        boolean isSinglePkIn = isSinglePk ? singlePkModelParam.isIn() : false;
+        boolean isSinglePkOut = isSinglePk ? singlePkModelParam.isOut() : false;
+
+        ModelParam doublePkPrimaryInParam = null;
+        ModelParam doublePkSecondaryOutParam = null;
+        ModelField doublePkSecondaryOutField = null;
+        if (isDoublePk) {
+            ModelField firstPkField = pksIter.next();
+            ModelParam firstPkParam = modelService.getParam(firstPkField.getName());
+            ModelField secondPkField = pksIter.next();
+            ModelParam secondPkParam = modelService.getParam(secondPkField.getName());
+            if (firstPkParam.isIn() && secondPkParam.isOut()) {
+                doublePkPrimaryInParam = firstPkParam;
+                doublePkSecondaryOutParam = secondPkParam;
+                doublePkSecondaryOutField = secondPkField;
+            } else if (firstPkParam.isOut() && secondPkParam.isIn()) {
+                doublePkPrimaryInParam = secondPkParam;
+                doublePkSecondaryOutParam = firstPkParam;
+                doublePkSecondaryOutField = firstPkField;
+            } else {
+                // we don't have an IN and an OUT... so do nothing and leave them null
+            }
+        }
+
+        if (isSinglePk && isSinglePkOut && !isSinglePkIn) {
+            /*
+             **** primary sequenced primary key ****
+             *
+            <auto-attributes include="pk" mode="OUT" optional="false"/>
+             *
+            <make-value entity-name="Example" value-name="newEntity"/>
+            <sequenced-id-to-env sequence-name="Example" env-name="newEntity.exampleId"/> <!-- get the next sequenced ID -->
+            <field-to-result field-name="newEntity.exampleId" result-name="exampleId"/>
+            <set-nonpk-fields map-name="parameters" value-name="newEntity"/>
+            <create-value value-name="newEntity"/>
+             *
+             */
+
+            String sequencedId = dctx.getDelegator().getNextSeqId(modelEntity.getEntityName());
+            newEntity.set(singlePkModeField.getName(), sequencedId);
+        } else if (isSinglePk && isSinglePkOut && isSinglePkIn) {
+            /*
+             **** primary sequenced key with optional override passed in ****
+             *
+            <auto-attributes include="pk" mode="INOUT" optional="true"/>
+             *
+            <make-value value-name="newEntity" entity-name="Product"/>
+            <set-nonpk-fields map-name="parameters" value-name="newEntity"/>
+            <set from-field="parameters.productId" field="newEntity.productId"/>
+            <if-empty field="newEntity.productId">
+                <sequenced-id-to-env sequence-name="Product" env-name="newEntity.productId"/>
+            <else>
+                <check-id field-name="productId" map-name="newEntity"/>
+                <check-errors/>
+            </else>
+            </if-empty>
+            <field-to-result field-name="productId" map-name="newEntity" result-name="productId"/>
+            <create-value value-name="newEntity"/>
+             *
+             */
 
-                // handle the case where there is a fromDate in the pk of the entity, and it is optional or undefined in the service def, populate automatically
-                ModelField fromDateField = modelEntity.getField("fromDate");
-                if (fromDateField != null && fromDateField.getIsPk()) {
-                    ModelParam fromDateParam = modelService.getParam("fromDate");
-                    if (fromDateParam == null || parameters.get("fromDate") == null) {
-                        newEntity.set("fromDate", UtilDateTime.nowTimestamp());
+            Object pkValue = parameters.get(singlePkModelParam.name);
+            if (UtilValidate.isEmpty(pkValue)) {
+                pkValue = dctx.getDelegator().getNextSeqId(modelEntity.getEntityName());
+            } else {
+                // pkValue passed in, check and if there are problems return an error
+
+                if (pkValue instanceof String) {
+                    StringBuffer errorDetails = new StringBuffer();
+                    if (!UtilValidate.isValidDatabaseId((String) pkValue, errorDetails)) {
+                        return ServiceUtil.returnError(UtilProperties.getMessage(resource, "ServiceParameterValueNotValid", UtilMisc.toMap("parameterName", singlePkModelParam.name,"errorDetails", errorDetails), locale));
                     }
                 }
+            }
+            newEntity.set(singlePkModeField.getName(), pkValue);
+        } else if (isDoublePk && doublePkPrimaryInParam != null && doublePkSecondaryOutParam != null) {
+            /*
+             **** secondary sequenced primary key ****
+             *
+            <auto-attributes include="pk" mode="IN" optional="false"/>
+            <override name="exampleItemSeqId" mode="OUT"/> <!-- make this OUT rather than IN, we will automatically generate the next sub-sequence ID -->
+             *
+            <make-value entity-name="ExampleItem" value-name="newEntity"/>
+            <set-pk-fields map-name="parameters" value-name="newEntity"/>
+            <make-next-seq-id value-name="newEntity" seq-field-name="exampleItemSeqId"/> <!-- this finds the next sub-sequence ID -->
+            <field-to-result field-name="newEntity.exampleItemSeqId" result-name="exampleItemSeqId"/>
+            <set-nonpk-fields map-name="parameters" value-name="newEntity"/>
+            <create-value value-name="newEntity"/>
+             */
 
-                newEntity.setNonPKFields(parameters, true);
-                if (modelEntity.getField("createdDate") != null) {
-                    newEntity.set("createdDate", UtilDateTime.nowTimestamp());
-                    if (modelEntity.getField("createdByUserLogin") != null) {
-                        GenericValue userLogin = (GenericValue) parameters.get("userLogin");
-                        if (userLogin != null) {
-                            newEntity.set("createdByUserLogin", userLogin.get("userLoginId"));
-                            if (modelEntity.getField("lastModifiedByUserLogin") != null) {
-                                newEntity.set("lastModifiedByUserLogin", userLogin.get("userLoginId"));
-                            }
-                        }
-                    }
-                    if (modelEntity.getField("lastModifiedDate") != null) {
-                        newEntity.set("lastModifiedDate", UtilDateTime.nowTimestamp());
+            newEntity.setPKFields(parameters, true);
+            dctx.getDelegator().setNextSubSeqId(newEntity, doublePkSecondaryOutField.getName(), 5, 1);
+        } else if (allPksInOnly) {
+            /*
+             **** plain specified primary key ****
+             *
+            <auto-attributes include="pk" mode="IN" optional="false"/>
+             *
+            <make-value entity-name="Example" value-name="newEntity"/>
+            <set-pk-fields map-name="parameters" value-name="newEntity"/>
+            <set-nonpk-fields map-name="parameters" value-name="newEntity"/>
+            <create-value value-name="newEntity"/>
+             *
+             */
+            newEntity.setPKFields(parameters, true);
+            //with all pks present on parameters, check if the entity is not already exists.
+            GenericValue lookedUpValue = PrimaryKeyFinder.runFind(modelEntity, parameters, dctx.getDelegator(), false, true, null, null);
+            if (lookedUpValue != null) {
+                return ServiceUtil.returnError(UtilProperties.getMessage(resource, "ServiceValueFound", UtilMisc.toMap("pkFields", newEntity.getPkShortValueString()), locale));
+            }
+        } else {
+            /* We haven't all Pk and their are 3 or more, now check if isn't a associate entity with own sequence
+            <set-pk-fields map="parameters" value-field="newEntity"/>
+            <sequenced-id sequence-name="ExempleItemAssoc" field="newEntity.exempleItemAssocId"/>
+            <create-value value-field="newEntity"/>
+             */
+            if (pkFieldNameOutOnly != null && pkFieldNameOutOnly.size() == 1) {
+                newEntity.setPKFields(parameters, true);
+                String pkFieldName = pkFieldNameOutOnly.get(0);
+                //if it's a fromDate, don't update it now, it's will be done next step
+                if (! "fromDate".equals(pkFieldName)) {
+                    String pkValue = dctx.getDelegator().getNextSeqId(modelEntity.getEntityName());
+                    newEntity.set(pkFieldName, pkValue);
+                }
+            } else {
+                throw new GenericServiceException("In Service [" + modelService.name + "] which uses the entity-auto engine with the create invoke option: " +
+                        "could not find a valid combination of primary key settings to do a known create operation; options include: " +
+                        "1. a single OUT pk for primary auto-sequencing, " +
+                        "2. a single INOUT pk for primary auto-sequencing with optional override, " +
+                        "3. a 2-part pk with one part IN (existing primary pk) and one part OUT (the secondary pk to sub-sequence), " +
+                        "4. a N-part pk with N-1 part IN and one party OUT only (missing pk is a sub-sequence mainly for entity assoc), " +
+                        "5. all pk fields are IN for a manually specified primary key");
+            }
+        }
+
+        // handle the case where there is a fromDate in the pk of the entity, and it is optional or undefined in the service def, populate automatically
+        ModelField fromDateField = modelEntity.getField("fromDate");
+        if (fromDateField != null && fromDateField.getIsPk()) {
+            ModelParam fromDateParam = modelService.getParam("fromDate");
+            if (fromDateParam == null || parameters.get("fromDate") == null) {
+                newEntity.set("fromDate", UtilDateTime.nowTimestamp());
+            }
+        }
+
+        newEntity.setNonPKFields(parameters, true);
+        if (modelEntity.getField("createdDate") != null) {
+            newEntity.set("createdDate", UtilDateTime.nowTimestamp());
+            if (modelEntity.getField("createdByUserLogin") != null) {
+                GenericValue userLogin = (GenericValue) parameters.get("userLogin");
+                if (userLogin != null) {
+                    newEntity.set("createdByUserLogin", userLogin.get("userLoginId"));
+                    if (modelEntity.getField("lastModifiedByUserLogin") != null) {
+                        newEntity.set("lastModifiedByUserLogin", userLogin.get("userLoginId"));
+                    } else if (modelEntity.getField("changedByUserLogin") != null) {
+                        newEntity.set("changedByUserLogin", userLogin.get("userLoginId"));
                     }
                 }
-                newEntity.create();
-                result.putAll(modelService.makeValid(newEntity, "OUT"));
-            } else if ("update".equals(modelService.invoke)) {
-                /*
-                <auto-attributes include="pk" mode="IN" optional="false"/>
-                 *
-                <entity-one entity-name="ExampleItem" value-name="lookedUpValue"/>
-                <set-nonpk-fields value-name="lookedUpValue" map-name="parameters"/>
-                <store-value value-name="lookedUpValue"/>
-                 */
-
-                // check to make sure that all primary key fields are defined as IN attributes
-                if (!allPksInOnly) {
-                    throw new GenericServiceException("In Service [" + modelService.name + "] which uses the entity-auto engine with the update invoke option not all pk fields have the mode IN");
-                }
+            }
+            if (modelEntity.getField("lastModifiedDate") != null) {
+                newEntity.set("lastModifiedDate", UtilDateTime.nowTimestamp());
+            } else if (modelEntity.getField("changedDate") != null) {
+                newEntity.set("changedDate", UtilDateTime.nowTimestamp());
+            }
+        }
+        newEntity.create();
+        result.put("crudValue", newEntity);
+        return result;
+    }
 
-                GenericValue lookedUpValue = PrimaryKeyFinder.runFind(modelEntity, parameters, dctx.getDelegator(), false, true, null, null);
-                if (lookedUpValue == null) {
-                    return ServiceUtil.returnError(UtilProperties.getMessage(resource, "ServiceValueNotFound", locale));
-                }
+    private static Map<String, Object> invokeUpdate(DispatchContext dctx, Map<String, Object> parameters, ModelService modelService, ModelEntity modelEntity, boolean allPksInOnly)
+            throws GeneralException {
+        Locale locale = (Locale) parameters.get("locale");
+        Map<String, Object> localContext = new HashMap<String, Object>();
+        localContext.put("parameters", parameters);
+        Map<String, Object> result = ServiceUtil.returnSuccess();
+        /*
+         <auto-attributes include="pk" mode="IN" optional="false"/>
+         *
+         <entity-one entity-name="ExampleItem" value-name="lookedUpValue"/>
+         <set-nonpk-fields value-name="lookedUpValue" map-name="parameters"/>
+         <store-value value-name="lookedUpValue"/>
+         */
+
+        // check to make sure that all primary key fields are defined as IN attributes
+        if (!allPksInOnly) {
+            throw new GenericServiceException("In Service [" + modelService.name + "] which uses the entity-auto engine with the update invoke option not all pk fields have the mode IN");
+        }
 
-                localContext.put("lookedUpValue", lookedUpValue);
+        GenericValue lookedUpValue = PrimaryKeyFinder.runFind(modelEntity, parameters, dctx.getDelegator(), false, true, null, null);
+        if (lookedUpValue == null) {
+            return ServiceUtil.returnError(UtilProperties.getMessage(resource, "ServiceValueNotFound", locale));
+        }
 
-                // populate the oldStatusId out if there is a service parameter for it, and before we do the set non-pk fields
-                /*
-                <auto-attributes include="pk" mode="IN" optional="false"/>
-                <attribute name="oldStatusId" type="String" mode="OUT" optional="false"/>
-                 *
-                <field-to-result field-name="lookedUpValue.statusId" result-name="oldStatusId"/>
-                 */
-                ModelParam statusIdParam = modelService.getParam("statusId");
-                ModelField statusIdField = modelEntity.getField("statusId");
-                ModelParam oldStatusIdParam = modelService.getParam("oldStatusId");
-                if (statusIdParam != null && statusIdParam.isIn() && oldStatusIdParam != null && oldStatusIdParam.isOut() && statusIdField != null) {
-                    result.put("oldStatusId", lookedUpValue.get("statusId"));
-                }
+        //        localContext.put("lookedUpValue", lookedUpValue);
 
-                // do the StatusValidChange check
-                /*
-                <if-compare-field field="lookedUpValue.statusId" operator="not-equals" to-field="parameters.statusId">
-                    <!-- if the record exists there should be a statusId, but just in case make it so it won't blow up -->
-                    <if-not-empty field="lookedUpValue.statusId">
-                        <!-- if statusId change is not in the StatusValidChange list, complain... -->
-                        <entity-one entity-name="StatusValidChange" value-name="statusValidChange" auto-field-map="false">
-                            <field-map field-name="statusId" env-name="lookedUpValue.statusId"/>
-                            <field-map field-name="statusIdTo" env-name="parameters.statusId"/>
-                        </entity-one>
-                        <if-empty field="statusValidChange">
-                            <!-- no valid change record found? return an error... -->
-                            <add-error><fail-property resource="CommonUiLabels" property="CommonErrorNoStatusValidChange"/></add-error>
-                            <check-errors/>
-                        </if-empty>
-                    </if-not-empty>
-                </if-compare-field>
-                 */
-                String parameterStatusId = (String) parameters.get("statusId");
-                if (statusIdParam != null && statusIdParam.isIn() && UtilValidate.isNotEmpty(parameterStatusId) && statusIdField != null) {
-                    String lookedUpStatusId = (String) lookedUpValue.get("statusId");
-                    if (UtilValidate.isNotEmpty(lookedUpStatusId) && !parameterStatusId.equals(lookedUpStatusId)) {
-                        // there was an old status, and in this call we are trying to change it, so do the StatusValidChange check
-                        GenericValue statusValidChange = dctx.getDelegator().findOne("StatusValidChange", true, "statusId", lookedUpStatusId, "statusIdTo", parameterStatusId);
-                        if (statusValidChange == null) {
-                            // uh-oh, no valid change...
-                            return ServiceUtil.returnError(UtilProperties.getMessage("CommonUiLabels", "CommonErrorNoStatusValidChange", localContext, locale));
-                        }
-                    }
+        // populate the oldStatusId out if there is a service parameter for it, and before we do the set non-pk fields
+        /*
+        <auto-attributes include="pk" mode="IN" optional="false"/>
+        <attribute name="oldStatusId" type="String" mode="OUT" optional="false"/>
+         *
+        <field-to-result field-name="lookedUpValue.statusId" result-name="oldStatusId"/>
+         */
+        ModelParam statusIdParam = modelService.getParam("statusId");
+        ModelField statusIdField = modelEntity.getField("statusId");
+        ModelParam oldStatusIdParam = modelService.getParam("oldStatusId");
+        if (statusIdParam != null && statusIdParam.isIn() && oldStatusIdParam != null && oldStatusIdParam.isOut() && statusIdField != null) {
+            result.put("oldStatusId", lookedUpValue.get("statusId"));
+        }
+
+        // do the StatusValidChange check
+        /*
+         <if-compare-field field="lookedUpValue.statusId" operator="not-equals" to-field="parameters.statusId">
+             <!-- if the record exists there should be a statusId, but just in case make it so it won't blow up -->
+             <if-not-empty field="lookedUpValue.statusId">
+                 <!-- if statusId change is not in the StatusValidChange list, complain... -->
+                      <entity-one entity-name="StatusValidChange" value-name="statusValidChange" auto-field-map="false">
+                     <field-map field-name="statusId" env-name="lookedUpValue.statusId"/>
+                     <field-map field-name="statusIdTo" env-name="parameters.statusId"/>
+                 </entity-one>
+                 <if-empty field="statusValidChange">
+                     <!-- no valid change record found? return an error... -->
+                          <add-error><fail-property resource="CommonUiLabels" property="CommonErrorNoStatusValidChange"/></add-error>
+                     <check-errors/>
+                 </if-empty>
+             </if-not-empty>
+         </if-compare-field>
+         */
+        String parameterStatusId = (String) parameters.get("statusId");
+        if (statusIdParam != null && statusIdParam.isIn() && UtilValidate.isNotEmpty(parameterStatusId) && statusIdField != null) {
+            String lookedUpStatusId = (String) lookedUpValue.get("statusId");
+            if (UtilValidate.isNotEmpty(lookedUpStatusId) && !parameterStatusId.equals(lookedUpStatusId)) {
+                // there was an old status, and in this call we are trying to change it, so do the StatusValidChange check
+                GenericValue statusValidChange = dctx.getDelegator().findOne("StatusValidChange", true, "statusId", lookedUpStatusId, "statusIdTo", parameterStatusId);
+                if (statusValidChange == null) {
+                    // uh-oh, no valid change...
+                    return ServiceUtil.returnError(UtilProperties.getMessage("CommonUiLabels", "CommonErrorNoStatusValidChange", localContext, locale));
                 }
-                // NOTE: nothing here to maintain the status history, that should be done with a custom service called by SECA rule
+            }
+        }
+        // NOTE: nothing here to maintain the status history, that should be done with a custom service called by SECA rule
 
-                lookedUpValue.setNonPKFields(parameters, true);
-                if (modelEntity.getField("lastModifiedDate") != null) {
-                    lookedUpValue.set("lastModifiedDate", UtilDateTime.nowTimestamp());
+        lookedUpValue.setNonPKFields(parameters, true);
+        if (modelEntity.getField("lastModifiedDate") != null
+                || modelEntity.getField("changedDate") != null) {
+            if (modelEntity.getField("lastModifiedDate") != null) {
+                lookedUpValue.set("lastModifiedDate", UtilDateTime.nowTimestamp());
+            } else {
+                lookedUpValue.set("changedDate", UtilDateTime.nowTimestamp());
+            }
+            if (modelEntity.getField("lastModifiedByUserLogin") != null
+                    || modelEntity.getField("changedByUserLogin") != null) {
+                GenericValue userLogin = (GenericValue) parameters.get("userLogin");
+                if (userLogin != null) {
                     if (modelEntity.getField("lastModifiedByUserLogin") != null) {
-                        GenericValue userLogin = (GenericValue) parameters.get("userLogin");
-                        if (userLogin != null) {
-                            lookedUpValue.set("lastModifiedByUserLogin", userLogin.get("userLoginId"));
-                        }
+                        lookedUpValue.set("lastModifiedByUserLogin", userLogin.get("userLoginId"));
+                    } else {
+                        lookedUpValue.set("changedByUserLogin", userLogin.get("userLoginId"));
                     }
                 }
-                lookedUpValue.store();
-            } else if ("delete".equals(modelService.invoke)) {
-                /*
-                <auto-attributes include="pk" mode="IN" optional="false"/>
-                 *
-                <entity-one entity-name="ExampleItem" value-name="lookedUpValue"/>
-                <remove-value value-name="lookedUpValue"/>
-                 */
-
-                // check to make sure that all primary key fields are defined as IN attributes
-                if (!allPksInOnly) {
-                    throw new GenericServiceException("In Service [" + modelService.name + "] which uses the entity-auto engine with the delete invoke option not all pk fields have the mode IN");
-                }
+            }
+        }
+        lookedUpValue.store();
+        result.put("crudValue", lookedUpValue);
+        return result;
+    }
 
-                GenericValue lookedUpValue = PrimaryKeyFinder.runFind(modelEntity, parameters, dctx.getDelegator(), false, true, null, null);
-                if (lookedUpValue != null) {
-                    lookedUpValue.remove();
-                } else {
-                    return ServiceUtil.returnError(UtilProperties.getMessage(resource, "ServiceValueNotFoundForRemove", locale));
-                }
+    private static Map<String, Object> invokeDelete(DispatchContext dctx, Map<String, Object> parameters, ModelService modelService, ModelEntity modelEntity, boolean allPksInOnly)
+            throws GeneralException {
+        Locale locale = (Locale) parameters.get("locale");
+        /*
+        <auto-attributes include="pk" mode="IN" optional="false"/>
+         *
+        <entity-one entity-name="ExampleItem" value-name="lookedUpValue"/>
+        <remove-value value-name="lookedUpValue"/>
+         */
+
+        // check to make sure that all primary key fields are defined as IN attributes
+        if (!allPksInOnly) {
+            throw new GenericServiceException("In Service [" + modelService.name + "] which uses the entity-auto engine with the delete invoke option not all pk fields have the mode IN");
+        }
+
+        GenericValue lookedUpValue = PrimaryKeyFinder.runFind(modelEntity, parameters, dctx.getDelegator(), false, true, null, null);
+        if (lookedUpValue == null) {
+            return ServiceUtil.returnError(UtilProperties.getMessage(resource, "ServiceValueNotFoundForRemove", locale));
+        }
+        lookedUpValue.remove();
+        return ServiceUtil.returnSuccess();
+    }
+
+    /**
+     * Analyse the entity, service and parameter to resolve the field to update with what value
+     * @param dctx
+     * @param parameters
+     * @param modelService
+     * @param modelEntity
+     * @param lookedUpValue
+     * @param allPksInOnly
+     * @return
+     * @throws GeneralException
+     */
+    private static Map<String, Object> invokeExpire(DispatchContext dctx, Map<String, Object> parameters, ModelService modelService, ModelEntity modelEntity, boolean allPksInOnly)
+            throws GeneralException {
+        Locale locale = (Locale) parameters.get("locale");
+        LinkedList<String> fieldThruDates = new LinkedList<String>();
+        boolean thruDatePresent = false;
+        String fieldDateNameIn = null;
+
+        // check to make sure that all primary key fields are defined as IN attributes
+        if (!allPksInOnly) {
+            throw new GenericServiceException("In Service [" + modelService.name + "] which uses the entity-auto engine with the update invoke option not all pk fields have the mode IN");
+        }
+        GenericValue lookedUpValue = PrimaryKeyFinder.runFind(modelEntity, parameters, dctx.getDelegator(), false, true, null, null);
+        if (lookedUpValue == null) {
+            return ServiceUtil.returnError(UtilProperties.getMessage(resource, "ServiceValueNotFound", locale));
+        }
+
+        //check if a non pk date field is present on parameters
+        for (String fieldDateName : modelEntity.getNoPkFieldNames()) {
+            if ("thruDate".equals(fieldDateName)) {
+                thruDatePresent = true;
+            } else if (fieldDateName.endsWith("ThruDate")) {
+                fieldThruDates.add(fieldDateName);
+            } else if (fieldDateName.startsWith("thru") && fieldDateName.endsWith("Date")) {
+                fieldThruDates.add(fieldDateName);
+            } else if (fieldDateNameIn == null && modelService.getParam(fieldDateName) != null
+                    && modelEntity.getField(fieldDateName).getType().contains("date")) {
+                fieldDateNameIn = fieldDateName;
             }
-        } catch (GeneralException e) {
-            Debug.logError(e, "Error doing entity-auto operation for entity [" + modelEntity.getEntityName() + "] in service [" + modelService.name + "]: " + e.toString(), module);
-            return ServiceUtil.returnError(UtilProperties.getMessage(resource, "ServiceEntityAutoOperation", UtilMisc.toMap("entityName", modelEntity.getEntityName(), "serviceName", modelService.name,"errorString", e.toString()), locale));
         }
 
-        return result;
+        if (Debug.infoOn())
+            Debug.logInfo(" FIELD FOUND : " + fieldDateNameIn + " ## # " + fieldThruDates + " ### " + thruDatePresent, module);
+
+        if (Debug.infoOn())
+            Debug.logInfo(" parameters IN  : " + parameters, module);
+        // Resolve the field without value to expire and check if the value is present on parameters or use now
+        if (fieldDateNameIn != null) {
+            if (parameters.get(fieldDateNameIn) == null) parameters.put(fieldDateNameIn, UtilDateTime.nowTimestamp());
+        } else if (thruDatePresent && UtilValidate.isEmpty(lookedUpValue.getTimestamp("thruDate"))) {
+            if (UtilValidate.isEmpty(parameters.get("thruDate"))) parameters.put("thruDate", UtilDateTime.nowTimestamp());
+        } else {
+            for (String fieldDateName: fieldThruDates) {
+                if (UtilValidate.isEmpty(lookedUpValue.getTimestamp(fieldDateName))) {
+                    if (UtilValidate.isEmpty(parameters.get(fieldDateName))) parameters.put(fieldDateName, UtilDateTime.nowTimestamp());
+                    break;
+                }
+            }
+        }
+        if (Debug.infoOn())
+            Debug.logInfo(" parameters OUT  : " + parameters, module);
+        return ServiceUtil.returnSuccess();
     }
 }

Modified: ofbiz/trunk/framework/service/src/org/ofbiz/service/test/ServiceEntityAutoTests.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/service/src/org/ofbiz/service/test/ServiceEntityAutoTests.java?rev=1735737&r1=1735736&r2=1735737&view=diff
==============================================================================
--- ofbiz/trunk/framework/service/src/org/ofbiz/service/test/ServiceEntityAutoTests.java (original)
+++ ofbiz/trunk/framework/service/src/org/ofbiz/service/test/ServiceEntityAutoTests.java Sat Mar 19 08:04:16 2016
@@ -18,6 +18,7 @@
  *******************************************************************************/
 package org.ofbiz.service.test;
 
+import java.sql.Timestamp;
 import java.util.HashMap;
 import java.util.Locale;
 import java.util.Map;
@@ -153,4 +154,43 @@ public class ServiceEntityAutoTests exte
         assertTrue(ServiceUtil.isError(results));
         assertEquals(UtilProperties.getMessage("ServiceErrorUiLabels", "ServiceValueNotFoundForRemove", Locale.ENGLISH), ServiceUtil.getErrorMessage(results));
     }
+
+    public void testEntityAutoExpireEntity() throws Exception {
+        Timestamp now = UtilDateTime.nowTimestamp();
+        delegator.create("Testing", "testingId", "TESTING_6");
+        delegator.create("TestingNode", "testingNodeId", "TESTNODE_6");
+        Map<String, Object> testingNodeMemberPkMap = UtilMisc.toMap("testingId", "TESTING_6", "testingNodeId", "TESTNODE_6", "fromDate", now);
+        delegator.create("TestingNodeMember", testingNodeMemberPkMap);
+
+        //test expire the thruDate
+        Map<String, Object> results = dispatcher.runSync("testEntityAutoExpireTestingNodeMember", testingNodeMemberPkMap);
+        assertTrue(ServiceUtil.isSuccess(results));
+        GenericValue testingNodeMember = EntityQuery.use(delegator).from("TestingNodeMember").where(testingNodeMemberPkMap).queryOne();
+        Timestamp expireDate = testingNodeMember.getTimestamp("thruDate");
+        assertNotNull("Expire thruDate set ", expireDate);
+
+        //test expire to ensure the thruDate isn't update but extendThruDate is
+        results = dispatcher.runSync("testEntityAutoExpireTestingNodeMember", testingNodeMemberPkMap);
+        assertTrue(ServiceUtil.isSuccess(results));
+        testingNodeMember = EntityQuery.use(delegator).from("TestingNodeMember").where(testingNodeMemberPkMap).queryOne();
+        assertTrue(expireDate.compareTo(testingNodeMember.getTimestamp("thruDate")) == 0);
+        assertNotNull("Expire extendThruDate set ", testingNodeMember.getTimestamp("extendThruDate"));
+
+        //test expire a specific field
+        delegator.create("TestFieldType", "testFieldTypeId", "TESTING_6");
+        Map<String, Object> testingExpireMap = UtilMisc.toMap("testFieldTypeId", "TESTING_6");
+        results = dispatcher.runSync("testEntityAutoExpireTestFieldType", testingExpireMap);
+        assertTrue(ServiceUtil.isSuccess(results));
+        GenericValue testFieldType = EntityQuery.use(delegator).from("TestFieldType").where("testFieldTypeId", "TESTING_6").queryOne();
+        assertNotNull("Expire dateTimeField set", testFieldType.getTimestamp("dateTimeField"));
+
+        //test expire a specific field with in value
+        delegator.create("TestFieldType", "testFieldTypeId", "TESTING_6bis");
+        testingExpireMap = UtilMisc.toMap("testFieldTypeId", "TESTING_6bis", "dateTimeField", now);
+        results = dispatcher.runSync("testEntityAutoExpireTestFieldType", testingExpireMap);
+        assertTrue(ServiceUtil.isSuccess(results));
+        testFieldType = EntityQuery.use(delegator).from("TestFieldType").where("testFieldTypeId", "TESTING_6bis").queryOne();
+        assertTrue(now.compareTo(testFieldType.getTimestamp("dateTimeField")) == 0);
+    }
+
 }