/* * 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. */ package org.apache.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; import org.apache.ofbiz.base.util.Debug; import org.apache.ofbiz.base.util.GeneralException; import org.apache.ofbiz.base.util.UtilDateTime; import org.apache.ofbiz.base.util.UtilMisc; import org.apache.ofbiz.base.util.UtilProperties; import org.apache.ofbiz.base.util.UtilValidate; import org.apache.ofbiz.entity.GenericValue; import org.apache.ofbiz.entity.finder.PrimaryKeyFinder; import org.apache.ofbiz.entity.model.ModelEntity; import org.apache.ofbiz.entity.model.ModelField; import org.apache.ofbiz.entity.util.EntityQuery; import org.apache.ofbiz.service.DispatchContext; import org.apache.ofbiz.service.GenericServiceException; import org.apache.ofbiz.service.ModelParam; import org.apache.ofbiz.service.ModelService; import org.apache.ofbiz.service.ServiceDispatcher; import org.apache.ofbiz.service.ServiceUtil; /** * Standard Java Static Method Service Engine */ public final class EntityAutoEngine extends GenericAsyncEngine { 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); } /** * @see org.apache.ofbiz.service.engine.GenericEngine#runSyncIgnore(java.lang.String, org.apache.ofbiz.service.ModelService, java.util.Map) */ @Override public void runSyncIgnore(String localName, ModelService modelService, Map<String, Object> context) throws GenericServiceException { runSync(localName, modelService, context); } /** * @see org.apache.ofbiz.service.engine.GenericEngine#runSync(java.lang.String, org.apache.ofbiz.service.ModelService, java.util.Map) */ @Override public Map<String, Object> runSync(String localName, ModelService modelService, Map<String, Object> parameters) throws GenericServiceException { // 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> result = ServiceUtil.returnSuccess(); // check the package and method names 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"); } if (UtilValidate.isEmpty(modelService.defaultEntityName)) { throw new GenericServiceException("In Service [" + modelService.name + "] you must specify a default-entity-name for entity-auto engine"); } ModelEntity modelEntity = dctx.getDelegator().getModelEntity(modelService.defaultEntityName); if (modelEntity == null) { throw new GenericServiceException("In Service [" + modelService.name + "] the specified default-entity-name [" + modelService.defaultEntityName + "] is not valid"); } try { boolean allPksInOnly = true; List<String> pkFieldNameOutOnly = null; /* Check for each pk if it's : * 1. part IN * 2. or part IN and OUT, but without value but present on parameters map * Help the engine to determinate the operation to realize for a create call or validate that * any pk is present for update/delete call. */ for (ModelField pkField: modelEntity.getPkFieldsUnmodifiable()) { ModelParam pkParam = modelService.getParam(pkField.getName()); boolean pkValueInParameters = pkParam.isIn() && UtilValidate.isNotEmpty(parameters.get(pkParam.getFieldName())); if (pkParam.isOut() && !pkValueInParameters) { if (pkFieldNameOutOnly == null) { pkFieldNameOutOnly = new LinkedList<String>(); allPksInOnly = false; } pkFieldNameOutOnly.add(pkField.getName()); } } switch (modelService.invoke) { case "create": result = invokeCreate(dctx, parameters, modelService, modelEntity, allPksInOnly, pkFieldNameOutOnly); result.put("successMessage", UtilProperties.getMessage("ServiceUiLabels", "EntityCreatedSuccessfully", UtilMisc.toMap("entityName", modelEntity.getEntityName()), locale)); break; case "update": result = invokeUpdate(dctx, parameters, modelService, modelEntity, allPksInOnly); result.put("successMessage", UtilProperties.getMessage("ServiceUiLabels", "EntityUpdatedSuccessfully", UtilMisc.toMap("entityName", modelEntity.getEntityName()), locale)); break; case "delete": result = invokeDelete(dctx, parameters, modelService, modelEntity, allPksInOnly); result.put("successMessage", UtilProperties.getMessage("ServiceUiLabels", "EntityDeletedSuccessfully", UtilMisc.toMap("entityName", modelEntity.getEntityName()), locale)); break; case "expire": result = invokeExpire(dctx, parameters, modelService, modelEntity, allPksInOnly); if (ServiceUtil.isSuccess(result)) { result = invokeUpdate(dctx, parameters, modelService, modelEntity, allPksInOnly); } result.put("successMessage", UtilProperties.getMessage("ServiceUiLabels", "EntityExpiredSuccessfully", UtilMisc.toMap("entityName", modelEntity.getEntityName()), locale)); 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)); } result.put(ModelService.SUCCESS_MESSAGE, result.get("successMessage")); return result; } 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(); 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"/> * */ 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); 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 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.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")); } } } if (modelEntity.getField("lastModifiedDate") != null) { newEntity.set("lastModifiedDate", UtilDateTime.nowTimestamp()); } else if (modelEntity.getField("changedDate") != null) { newEntity.set("changedDate", UtilDateTime.nowTimestamp()); } } if (modelEntity.getField("changeByUserLoginId") != null) { GenericValue userLogin = (GenericValue) parameters.get("userLogin"); if (userLogin != null) { newEntity.set("changeByUserLoginId", userLogin.get("userLoginId")); } else { throw new GenericServiceException("You call a creation on entity that require the userLogin to track the activity, please controle that your service definition has auth='true'"); } //Oh changeByUserLoginId detected, check if an EntityStatus concept if (modelEntity.getEntityName().endsWith("Status")) { if (modelEntity.getField("statusDate") != null && parameters.get("statusDate") == null) { newEntity.set("statusDate", UtilDateTime.nowTimestamp()); //if a statusEndDate is present, resolve the last EntityStatus to store this value on the previous element if (modelEntity.getField("statusEndDate") != null) { ModelEntity relatedEntity = dctx.getDelegator().getModelEntity(modelEntity.getEntityName().replaceFirst("Status", "")); if (relatedEntity != null) { Map<String, Object> conditionRelatedPkFieldMap = new HashMap<String, Object>(); for (String pkRelatedField : relatedEntity.getPkFieldNames()) { conditionRelatedPkFieldMap.put(pkRelatedField, parameters.get(pkRelatedField)); } GenericValue previousStatus = EntityQuery.use(newEntity.getDelegator()).from(modelEntity.getEntityName()) .where(conditionRelatedPkFieldMap).orderBy("-statusDate").queryFirst(); if (previousStatus != null) { previousStatus.put("statusEndDate", newEntity.get("statusDate")); previousStatus.store(); } } } } } } newEntity.create(); result.put("crudValue", newEntity); return result; } 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"); } GenericValue lookedUpValue = PrimaryKeyFinder.runFind(modelEntity, parameters, dctx.getDelegator(), false, true, null, null); if (lookedUpValue == null) { return ServiceUtil.returnError(UtilProperties.getMessage(resource, "ServiceValueNotFound", locale)); } // localContext.put("lookedUpValue", lookedUpValue); // 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 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) { lookedUpValue.set("lastModifiedByUserLogin", userLogin.get("userLoginId")); } else { lookedUpValue.set("changedByUserLogin", userLogin.get("userLoginId")); } } } } if (modelEntity.getField("changeByUserLoginId") != null ) { if (modelEntity.getEntityName().endsWith("Status")) { //Oh update on EntityStatus concept detected ... not possible, return invalid request throw new GenericServiceException("You call a updating operation on entity that track the activity, sorry I can't do that, please amazing developer check your service definition ;)"); } GenericValue userLogin = (GenericValue) parameters.get("userLogin"); if (userLogin != null) { lookedUpValue.set("changeByUserLoginId", userLogin.get("userLoginId")); } else { throw new GenericServiceException("You call a updating operation on entity that track the activity, sorry I can't do that, please amazing developer check your service definition ;)"); } } lookedUpValue.store(); result.put("crudValue", lookedUpValue); return result; } 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"); } if (modelEntity.getField("changeByUserLoginId") != null ) { if (modelEntity.getEntityName().endsWith("Status")) { //Oh update on EntityStatus concept detected ... not possible, return invalid request throw new GenericServiceException("You call a deleting operation on entity that track the activity, sorry I can't do that, please amazing developer check your service definition ;)"); } } 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"); List<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; } } 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(); } }