View Javadoc
1   /*
2    * Copyright 2007 The Kuali Foundation
3    * 
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    * http://www.opensource.org/licenses/ecl2.php
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.ole.coa.document.validation.impl;
17  
18  import java.sql.Timestamp;
19  import java.util.Calendar;
20  
21  import org.apache.commons.lang.StringUtils;
22  import org.apache.commons.lang.time.DateUtils;
23  import org.kuali.ole.coa.businessobject.AccountDelegateModel;
24  import org.kuali.ole.coa.businessobject.AccountDelegateModelDetail;
25  import org.kuali.ole.sys.OLEConstants;
26  import org.kuali.ole.sys.OLEKeyConstants;
27  import org.kuali.ole.sys.context.SpringContext;
28  import org.kuali.ole.sys.document.service.FinancialSystemDocumentTypeService;
29  import org.kuali.ole.sys.document.validation.impl.KfsMaintenanceDocumentRuleBase;
30  import org.kuali.rice.core.api.util.type.KualiDecimal;
31  import org.kuali.rice.kns.document.MaintenanceDocument;
32  import org.kuali.rice.krad.bo.PersistableBusinessObject;
33  import org.kuali.rice.krad.util.GlobalVariables;
34  import org.kuali.rice.krad.util.ObjectUtils;
35  
36  /**
37   * This class implements the business rules specific to the {@link OrganizationRoutingModelName} Maintenance Document.
38   */
39  public class AccountDelegateModelRule extends KfsMaintenanceDocumentRuleBase {
40  
41      protected AccountDelegateModel model;
42  
43      /**
44       * This method sets the convenience objects like model, so you have short and easy handles to the new and
45       * old objects contained in the maintenance document. It also calls the BusinessObjectBase.refresh(), which will attempt to load
46       * all sub-objects from the DB by their primary keys, if available.
47       * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#setupConvenienceObjects()
48       */
49      @Override
50      public void setupConvenienceObjects() {
51          model = (AccountDelegateModel) super.getNewBo();
52          for (AccountDelegateModelDetail delegateModel : model.getAccountDelegateModelDetails()) {
53              delegateModel.refreshNonUpdateableReferences();
54          }
55      }
56  
57      /**
58       * This performs rules checks on document approve
59       * <ul>
60       * <li>{@link AccountDelegateModelRule#checkSimpleRules(OrganizationRoutingModelName)}</li>
61       * </ul>
62       * This rule fails on business rule failures
63       * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomApproveDocumentBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument)
64       */
65      @Override
66      protected boolean processCustomApproveDocumentBusinessRules(MaintenanceDocument document) {
67          setupConvenienceObjects();
68          return checkSimpleRules(document, this.model);
69      }
70  
71      /**
72       * This performs rules checks on document route
73       * <ul>
74       * <li>{@link AccountDelegateModelRule#checkSimpleRules(OrganizationRoutingModelName)}</li>
75       * </ul>
76       * This rule fails on business rule failures
77       * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomRouteDocumentBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument)
78       */
79      @Override
80      protected boolean processCustomRouteDocumentBusinessRules(MaintenanceDocument document) {
81          setupConvenienceObjects();
82          return checkSimpleRules(document, this.model);
83      }
84  
85      /**
86       * This performs rules checks on document save
87       * <ul>
88       * <li>{@link AccountDelegateModelRule#checkSimpleRules(OrganizationRoutingModelName)}</li>
89       * </ul>
90       * This rule does not fail on business rule failures
91       * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomSaveDocumentBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument)
92       */
93      @Override
94      protected boolean processCustomSaveDocumentBusinessRules(MaintenanceDocument document) {
95          setupConvenienceObjects();
96          checkSimpleRules(document, this.model);
97          return true;
98      }
99  
100     /**
101      * This method calls 
102      * <ul>
103      * <li>{@link AccountDelegateModelRule#checkSimpleRulesForOrganizationRoutingModel(OrganizationRoutingModelName, OrganizationRoutingModel)}</li>
104      * </ul>
105      * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomAddCollectionLineBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument,
106      *      java.lang.String, org.kuali.rice.krad.bo.PersistableBusinessObject)
107      */
108     @Override
109     public boolean processCustomAddCollectionLineBusinessRules(MaintenanceDocument document, String collectionName, PersistableBusinessObject line) {
110         setupConvenienceObjects();
111         final FinancialSystemDocumentTypeService documentService = SpringContext.getBean(FinancialSystemDocumentTypeService.class);
112         return checkSimpleRulesForOrganizationRoutingModel(document, this.model, (AccountDelegateModelDetail) line, documentService);
113     }
114 
115     /**
116      * Checks the given rules against the entire Organization Routing Model parent.
117      * 
118      * @param globalDelegateTemplate the Organization Routing Model parent to check
119      * @return true if document passes all rules, false if otherwise
120      */
121     protected boolean checkSimpleRules(MaintenanceDocument document, AccountDelegateModel globalDelegateTemplate) {
122         boolean success = true;
123 
124         success &= checkModelNameHasAtLeastOneModel(globalDelegateTemplate);
125 
126         int line = 0;
127         final FinancialSystemDocumentTypeService documentService = SpringContext.getBean(FinancialSystemDocumentTypeService.class);
128         for (AccountDelegateModelDetail delegateModel : globalDelegateTemplate.getAccountDelegateModelDetails()) {
129             GlobalVariables.getMessageMap().addToErrorPath(MAINTAINABLE_ERROR_PATH + ".accountDelegateModelDetails[" + line + "].");
130             success &= checkSimpleRulesForOrganizationRoutingModel(document, globalDelegateTemplate, delegateModel, documentService);
131             GlobalVariables.getMessageMap().addToErrorPath(MAINTAINABLE_ERROR_PATH + ".accountDelegateModelDetails[" + line + "].");
132             line++;
133         }
134         return success;
135     }
136 
137     /**
138      * This method checks a series of basic rules for a single org routing model.
139      * 
140      * @return true if model passes all the checks, false if otherwise
141      */
142     protected boolean checkSimpleRulesForOrganizationRoutingModel(MaintenanceDocument document, AccountDelegateModel globalDelegateTemplate, AccountDelegateModelDetail delegateModel, FinancialSystemDocumentTypeService documentService) {
143         boolean success = true;
144 
145         if (delegateModel.isActive()) {
146             success &= checkStartDate(delegateModel);
147             success &= checkDelegateFromAmountPositive(delegateModel);
148             success &= checkDelegateToAmountGreaterThanFromAmount(delegateModel);
149             success &= checkDelegateUserRules(document, delegateModel);
150             success &= checkPrimaryRoutePerDocType(globalDelegateTemplate, delegateModel);
151             success &= checkDelegateDocumentTypeCode(delegateModel.getFinancialDocumentTypeCode(), documentService);
152         }
153 
154         return success;
155     }
156 
157     private boolean checkStartDate(AccountDelegateModelDetail delegateModel) {
158         boolean success = true;
159         if (ObjectUtils.isNotNull(delegateModel.getAccountDelegateStartDate())) {
160             Timestamp today = getDateTimeService().getCurrentTimestamp();
161             today.setTime(DateUtils.truncate(today, Calendar.DAY_OF_MONTH).getTime());
162             if (delegateModel.getAccountDelegateStartDate().before(today)) {
163                 success = false;
164                 GlobalVariables.getMessageMap().putError("accountDelegateStartDate", OLEKeyConstants.ERROR_DOCUMENT_ACCTDELEGATEMAINT_STARTDATE_IN_PAST, new String[0]);
165             }
166         }
167         return success;
168    }
169 
170 
171     /**
172      * This method makes certain that the collection of account delegates in the "mo itdel" has at least one account delegate
173      * template in it.
174      * 
175      * @param globalDelegateTemplate the account delegate model to check
176      * @return true if account delegate model has at least one account delegate template in it
177      */
178     protected boolean checkModelNameHasAtLeastOneModel(AccountDelegateModel globalDelegateTemplate) {
179         boolean success = true;
180         if (globalDelegateTemplate.getAccountDelegateModelDetails().size() == 0) {
181             success = false;
182             GlobalVariables.getMessageMap().putError(OLEConstants.MAINTENANCE_NEW_MAINTAINABLE + "add.accountDelegateModelDetails.financialDocumentTypeCode", OLEKeyConstants.ERROR_DOCUMENT_DELEGATE_CHANGE_NO_DELEGATE, new String[0]);
183         }
184         return success;
185     }
186 
187     /**
188      * This method checks that the account delegate model has at least one active "model" within it.
189      * 
190      * @param globalDelegateTemplate the account delegate model to check
191      * @return true if account delegate model has at least one active model in it.
192      */
193     // method not currently in use, as per Bill's comments in KULRNE-4805
194     protected boolean checkModelNameHasAtLeastOneActiveModel(AccountDelegateModel globalDelegateTemplate) {
195         boolean success = true;
196         int activeModelCount = 0;
197 
198         for (AccountDelegateModelDetail mdl : globalDelegateTemplate.getAccountDelegateModelDetails()) {
199             if (mdl.isActive()) {
200                 activeModelCount++;
201             }
202         }
203 
204         if (activeModelCount == 0) {
205             success = false;
206             if (globalDelegateTemplate.getAccountDelegateModelDetails().size() == 0) {
207                 GlobalVariables.getMessageMap().putError(OLEConstants.MAINTENANCE_NEW_MAINTAINABLE + "add.accountDelegateModelDetails.active", OLEKeyConstants.ERROR_DOCUMENT_DELEGATE_CHANGE_NO_ACTIVE_DELEGATE, new String[0]);
208             }
209             else {
210                 GlobalVariables.getMessageMap().putError(OLEConstants.MAINTENANCE_NEW_MAINTAINABLE + "accountDelegateModelDetails[0].active", OLEKeyConstants.ERROR_DOCUMENT_DELEGATE_CHANGE_NO_ACTIVE_DELEGATE, new String[0]);
211             }
212         }
213         return success;
214     }
215 
216     /**
217      * Checks that if approval from amount is not null, then it is positive
218      * 
219      * @param delegateModel Organization Routing Model to check
220      * @return true if Organization Routing Model passes the checks, false if otherwise
221      */
222     protected boolean checkDelegateFromAmountPositive(AccountDelegateModelDetail delegateModel) {
223         boolean result = true;
224         if (!ObjectUtils.isNull(delegateModel.getApprovalFromThisAmount())) {
225             if (delegateModel.getApprovalFromThisAmount().isLessThan(KualiDecimal.ZERO)) {
226                 GlobalVariables.getMessageMap().putError("approvalFromThisAmount", OLEKeyConstants.ERROR_DOCUMENT_ACCTDELEGATEMAINT_FROM_AMOUNT_NONNEGATIVE, new String[0]);
227                 result = false;
228             }
229         }
230         return result;
231     }
232 
233     /**
234      * Checks that if approval from amount is null, that approval to this amount is null or zero; and then checks that approval to
235      * amount is greater than or equal to approval from amount.
236      * 
237      * @param delegateModel Organization Routing Model to check
238      * @return true if the Organization Routing Model passes the checks, false if otherwise
239      */
240     protected boolean checkDelegateToAmountGreaterThanFromAmount(AccountDelegateModelDetail delegateModel) {
241         boolean result = true;
242         if (!ObjectUtils.isNull(delegateModel.getApprovalFromThisAmount())) {
243             if (!ObjectUtils.isNull(delegateModel.getApprovalToThisAmount())) {
244                 if (delegateModel.getApprovalToThisAmount().isLessThan(delegateModel.getApprovalFromThisAmount())) {
245                     GlobalVariables.getMessageMap().putError("approvalToThisAmount", OLEKeyConstants.ERROR_DOCUMENT_ACCTDELEGATEMAINT_TO_AMOUNT_MORE_THAN_FROM_OR_ZERO, new String[0]);
246                     result = false;
247                 }
248             }
249         }
250         if (!ObjectUtils.isNull(delegateModel.getApprovalToThisAmount()) && delegateModel.getApprovalToThisAmount().isLessThan(KualiDecimal.ZERO)) {
251             GlobalVariables.getMessageMap().putError("approvalToThisAmount", OLEKeyConstants.ERROR_DOCUMENT_ACCTDELEGATEMAINT_TO_AMOUNT_MORE_THAN_FROM_OR_ZERO, new String[0]);
252             result = false;
253         }
254         return result;
255     }
256 
257     /**
258      * Checks that the account delegate listed exists in the system, and that user has an active status and is a professional type
259      * 
260      * @param delegateModel the Organization Routing Model to check
261      * @return true if delegate user passes the rules described above; false if they fail
262      */
263     protected boolean checkDelegateUserRules(MaintenanceDocument document, AccountDelegateModelDetail delegateModel) {
264         boolean success = true;
265 
266         // refresh account delegate
267         try {
268             delegateModel.setAccountDelegate(SpringContext.getBean(org.kuali.rice.kim.api.identity.PersonService.class).getPerson(delegateModel.getAccountDelegateUniversalId()));
269         }
270         catch (Exception e) {
271             if (LOG.isDebugEnabled()) {
272                 LOG.debug("User Not Found Exception: " + e);
273             }
274         }
275 
276         // user must exist
277         if ((delegateModel.getAccountDelegate() == null) || (delegateModel.getAccountDelegate().getPrincipalId() == null)){
278             GlobalVariables.getMessageMap().putError("accountDelegate.principalName", OLEKeyConstants.ERROR_DOCUMENT_ACCTDELEGATEMAINT_USER_DOESNT_EXIST, new String[0]);
279             success = false;
280         }
281 
282         if (success) {
283             if (!getDocumentHelperService().getDocumentAuthorizer(document).isAuthorized(document, OLEConstants.PermissionNames.SERVE_AS_FISCAL_OFFICER_DELEGATE.namespace, OLEConstants.PermissionNames.SERVE_AS_FISCAL_OFFICER_DELEGATE.name, delegateModel.getAccountDelegate().getPrincipalId())) {
284                 super.putFieldError("accountDelegate.principalName", OLEKeyConstants.ERROR_USER_MISSING_PERMISSION, new String[] {delegateModel.getAccountDelegate().getName(), OLEConstants.PermissionNames.SERVE_AS_FISCAL_OFFICER_DELEGATE.namespace, OLEConstants.PermissionNames.SERVE_AS_FISCAL_OFFICER_DELEGATE.name});
285                 success = false;
286             }
287         }
288 
289         return success;
290     }
291 
292     /**
293      * This method validates the rule that says there can be only one PrimaryRoute delegate for each given docType. It checks the
294      * delegateGlobalToTest against the list, to determine whether adding this new delegateGlobalToTest would violate any
295      * PrimaryRoute business rule violations. If any of the incoming variables is null or empty, the method will do nothing, and
296      * return Null. It will only process the business rules if there is sufficient data to do so.
297      * 
298      * @param delegateGlobalToTest A delegateGlobal line that you want to test against the list.
299      * @param delegateGlobals A List of delegateGlobal items that is being tested against.
300      * @return true if model, delegate template or org routing model is null, or if the primary routing indicator is set to false or the doc type code is empty
301      * otherwise it checks to make sure that there is indeed one model marked as the primary route
302      */
303     protected boolean checkPrimaryRoutePerDocType(AccountDelegateModel globalDelegateTemplate, AccountDelegateModelDetail delegateModel) {
304         boolean success = true;
305 
306         // exit immediately if the adding line isnt a Primary routing
307         if (delegateModel == null || globalDelegateTemplate == null || globalDelegateTemplate.getAccountDelegateModelDetails().isEmpty()) {
308             return success;
309         }
310         if (!delegateModel.getAccountDelegatePrimaryRoutingIndicator()) {
311             return success;
312         }
313         if (StringUtils.isBlank(delegateModel.getFinancialDocumentTypeCode())) {
314             return success;
315         }
316 
317         // at this point, the delegateGlobal being added is a Primary for ALL docTypes, so we need to
318         // test whether any in the existing list are also Primary, regardless of docType
319         String docType = delegateModel.getFinancialDocumentTypeCode();
320         for (AccountDelegateModelDetail currDelegateModel : globalDelegateTemplate.getAccountDelegateModelDetails()) {
321             if (currDelegateModel.isActive() && !delegateModel.equals(currDelegateModel) && currDelegateModel.getAccountDelegatePrimaryRoutingIndicator() && delegateModel.getFinancialDocumentTypeCode().equals(currDelegateModel.getFinancialDocumentTypeCode())) {
322                 success = false;
323                 GlobalVariables.getMessageMap().putError("accountDelegatePrimaryRoutingIndicator", OLEKeyConstants.ERROR_DOCUMENT_GLOBAL_DELEGATEMAINT_PRIMARY_ROUTE_ALREADY_EXISTS_FOR_DOCTYPE, new String[0]);
324             }
325         }
326 
327         return success;
328     }
329     
330     /**
331      * Validates the document type code for the delegate, to make sure it is a Financial System document type code
332      * @param documentTypeCode the document type code to check
333      * @param delegateService a helpful instance of the delegate service, so new ones don't have to be created all the time
334      * @return true if the document type code is valid, false otherwise
335      */
336     protected boolean checkDelegateDocumentTypeCode(String documentTypeCode, FinancialSystemDocumentTypeService documentService) {
337         if (!documentService.isFinancialSystemDocumentType(documentTypeCode)) {
338             putFieldError("financialDocumentTypeCode", OLEKeyConstants.ERROR_DOCUMENT_ACCTDELEGATEMAINT_INVALID_DOC_TYPE, new String[] { documentTypeCode, OLEConstants.ROOT_DOCUMENT_TYPE });
339             return false;
340         }
341         return true;
342     }
343 
344 }
345