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); + } + } |
Free forum by Nabble | Edit this page |