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  import java.util.List;
21  
22  import org.apache.commons.lang.StringUtils;
23  import org.apache.commons.lang.time.DateUtils;
24  import org.kuali.ole.coa.businessobject.AccountDelegateGlobal;
25  import org.kuali.ole.coa.businessobject.AccountDelegateGlobalDetail;
26  import org.kuali.ole.coa.businessobject.AccountGlobalDetail;
27  import org.kuali.ole.sys.OLEConstants;
28  import org.kuali.ole.sys.OLEKeyConstants;
29  import org.kuali.ole.sys.OLEPropertyConstants;
30  import org.kuali.ole.sys.context.SpringContext;
31  import org.kuali.ole.sys.document.service.FinancialSystemDocumentTypeService;
32  import org.kuali.rice.core.api.util.type.KualiDecimal;
33  import org.kuali.rice.kim.api.identity.Person;
34  import org.kuali.rice.kns.document.MaintenanceDocument;
35  import org.kuali.rice.krad.bo.PersistableBusinessObject;
36  import org.kuali.rice.krad.util.GlobalVariables;
37  import org.kuali.rice.krad.util.ObjectUtils;
38  
39  /**
40   * This class executes specific rules for the {@link DelegateGlobalMaintenanceDocument}
41   */
42  public class DelegateGlobalRule extends GlobalDocumentRuleBase {
43  
44      protected static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(DelegateGlobalRule.class);
45  
46      protected static final KualiDecimal ZERO = KualiDecimal.ZERO;
47      protected AccountDelegateGlobal newDelegateGlobal;
48      protected static final String DELEGATE_GLOBALS_PREFIX = "delegateGlobals";
49  
50      public DelegateGlobalRule() {
51          super();
52      }
53  
54      /**
55       * This method sets the convenience objects like newAccount and oldAccount, so you have short and easy handles to the new and
56       * old objects contained in the maintenance document. It also calls the BusinessObjectBase.refresh(), which will attempt to load
57       * all sub-objects from the DB by their primary keys, if available.
58       */
59      @Override
60      public void setupConvenienceObjects() {
61  
62          // setup newDelegateGlobal convenience objects,
63          // make sure all possible sub-objects are populated
64          newDelegateGlobal = (AccountDelegateGlobal) super.getNewBo();
65  
66          // forces refreshes on all the sub-objects in the lists
67          for (AccountDelegateGlobalDetail delegateGlobal : newDelegateGlobal.getDelegateGlobals()) {
68              delegateGlobal.refreshNonUpdateableReferences();
69          }
70          for (AccountGlobalDetail accountGlobalDetail : newDelegateGlobal.getAccountGlobalDetails()) {
71              accountGlobalDetail.refreshNonUpdateableReferences();
72          }
73      }
74  
75      /**
76       * This checks some basic rules for document approval Specifically it calls the following:
77       * <ul>
78       * <li>{@link DelegateGlobalRule#checkSimpleRulesAllLines()}</li>
79       * <li>{@link DelegateGlobalRule#checkOnlyOneChartErrorWrapper(List)}</li>
80       * <li>{@link DelegateGlobalRule#checkForPrimaryDelegateAllLines()}</li>
81       * </ul>
82       * fails if any rules fail
83       * 
84       * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomApproveDocumentBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument)
85       */
86      @Override
87      protected boolean processCustomApproveDocumentBusinessRules(MaintenanceDocument document) {
88          boolean success = true;
89          setupConvenienceObjects();
90          // check simple rules
91          success &= checkSimpleRulesAllLines(document);
92  
93          success &= checkOnlyOneChartErrorWrapper(newDelegateGlobal.getAccountGlobalDetails());
94  
95          // check for primary routing
96          success &= checkForPrimaryDelegateAllLines();
97          return success;
98      }
99  
100     /**
101      * This checks some basic rules for document routing Specifically it calls the following:
102      * <ul>
103      * <li>{@link DelegateGlobalRule#checkSimpleRulesAllLines()}</li>
104      * <li>{@link DelegateGlobalRule#checkAccountDetails(List)}</li>
105      * <li>{@link DelegateGlobalRule#checkForPrimaryDelegateAllLines()}</li>
106      * </ul>
107      * fails if any rules fail
108      * 
109      * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomRouteDocumentBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument)
110      */
111     @Override
112     protected boolean processCustomRouteDocumentBusinessRules(MaintenanceDocument document) {
113         boolean success = true;
114         setupConvenienceObjects();
115         // check simple rules
116         success &= checkSimpleRulesAllLines(document);
117 
118         success &= checkAccountDetails(newDelegateGlobal.getAccountGlobalDetails());
119 
120         // check for primary routing
121         success &= checkForPrimaryDelegateAllLines();
122         return success;
123     }
124 
125     /**
126      * This checks some basic rules for document saving Specifically it calls the following:
127      * <ul>
128      * <li>{@link DelegateGlobalRule#checkSimpleRulesAllLines()}</li>
129      * <li>{@link DelegateGlobalRule#checkOnlyOneChartErrorWrapper(List)}</li>
130      * <li>{@link DelegateGlobalRule#checkForPrimaryDelegateAllLines()}</li>
131      * </ul>
132      * fails if any rules fail
133      * 
134      * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomSaveDocumentBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument)
135      */
136     @Override
137     protected boolean processCustomSaveDocumentBusinessRules(MaintenanceDocument document) {
138         setupConvenienceObjects();
139         // check simple rules
140         checkSimpleRulesAllLines(document);
141 
142         checkOnlyOneChartErrorWrapper(newDelegateGlobal.getAccountGlobalDetails());
143 
144         // check for primary routing
145         checkForPrimaryDelegateAllLines();
146         return true;
147     }
148 
149     /**
150      * This checks to see if there are any accounts in the details collection if there are then it calls
151      * {@link DelegateGlobalRule#checkAccountDetails(AccountGlobalDetail)}
152      * 
153      * @param details - collection of {@link AccountGlobalDetail}s
154      * @return false if there are no objects in the collection or any one of the {@link AccountGlobalDetail} fail
155      */
156     public boolean checkAccountDetails(List<AccountGlobalDetail> details) {
157         boolean success = true;
158 
159         // check if there are any accounts
160         if (details.size() == 0) {
161             putFieldError(OLEConstants.MAINTENANCE_ADD_PREFIX + "accountGlobalDetails.accountNumber", OLEKeyConstants.ERROR_DOCUMENT_GLOBAL_ACCOUNT_NO_ACCOUNTS);
162             success = false;
163         }
164         else {
165             // check each account
166             int index = 0;
167             for (AccountGlobalDetail dtl : details) {
168                 String errorPath = MAINTAINABLE_ERROR_PREFIX + "accountGlobalDetails[" + index + "]";
169                 GlobalVariables.getMessageMap().addToErrorPath(errorPath);
170                 success &= checkAccountDetails(dtl);
171                 GlobalVariables.getMessageMap().removeFromErrorPath(errorPath);
172                 index++;
173             }
174             success &= checkOnlyOneChartErrorWrapper(details);
175         }
176 
177         return success;
178     }
179 
180     /**
181      * This checks to make sure that each {@link AccountGlobalDetail} has a valid {@link Account}
182      * 
183      * @param dtl - the {@link AccountGlobalDetail}
184      * @return false if it does not have a valid {@link Account}
185      */
186     public boolean checkAccountDetails(AccountGlobalDetail dtl) {
187         boolean success = true;
188         int originalErrorCount = GlobalVariables.getMessageMap().getErrorCount();
189         getDictionaryValidationService().validateBusinessObject(dtl);
190         if (StringUtils.isNotBlank(dtl.getAccountNumber()) && StringUtils.isNotBlank(dtl.getChartOfAccountsCode())) {
191             dtl.refreshReferenceObject("account");
192             if (ObjectUtils.isNull(dtl.getAccount())) {
193                 GlobalVariables.getMessageMap().putError("accountNumber", OLEKeyConstants.ERROR_DOCUMENT_GLOBAL_ACCOUNT_INVALID_ACCOUNT, new String[] { dtl.getChartOfAccountsCode(), dtl.getAccountNumber() });
194             }
195         }
196         success &= GlobalVariables.getMessageMap().getErrorCount() == originalErrorCount;
197 
198         return success;
199     }
200 
201     /**
202      * This method checks the simple rules for all lines at once and gets called on save, submit, etc. but not on add Specifically
203      * it calls the following:
204      * <ul>
205      * <li>{@link DelegateGlobalRule#checkDelegateUserRules(DelegateGlobalDetail, int, boolean)}</li>
206      * <li>{@link DelegateGlobalRule#checkDelegateForNullToAmount(KualiDecimal, KualiDecimal, int, boolean)}</li>
207      * <li>{@link DelegateGlobalRule#checkDelegateToAmtGreaterThanFromAmt(KualiDecimal, KualiDecimal, int, boolean)}</li>
208      * <li>{@link DelegateGlobalRule#checkDelegateFromAmtGreaterThanEqualZero(KualiDecimal, int, boolean)}</li>
209      * <li>{@link DelegateGlobalRule#checkPrimaryRouteRules(List, DelegateGlobalDetail, Integer, boolean)}</li>
210      * </ul>
211      * 
212      * @return
213      */
214     protected boolean checkSimpleRulesAllLines(MaintenanceDocument document) {
215 
216         boolean success = true;
217         // check if there are any accounts
218         if (newDelegateGlobal.getDelegateGlobals().size() == 0) {
219             putFieldError(OLEConstants.MAINTENANCE_ADD_PREFIX + OLEPropertyConstants.DELEGATE_GLOBALS + "." + OLEPropertyConstants.FINANCIAL_DOCUMENT_TYPE_CODE, OLEKeyConstants.ERROR_DOCUMENT_DELEGATE_CHANGE_NO_DELEGATE);
220             success = false;
221         }
222         else {
223             // check each delegate
224             int i = 0;
225             final FinancialSystemDocumentTypeService documentService = SpringContext.getBean(FinancialSystemDocumentTypeService.class);
226             for (AccountDelegateGlobalDetail newDelegateGlobalDetail : newDelegateGlobal.getDelegateGlobals()) {
227                 KualiDecimal fromAmount = newDelegateGlobalDetail.getApprovalFromThisAmount();
228                 KualiDecimal toAmount = newDelegateGlobalDetail.getApprovalToThisAmount();
229 
230                 success &= checkDelegateUserRules(document, newDelegateGlobalDetail, i, false);
231 
232                 // FROM amount must be >= 0 (may not be negative)
233                 success &= checkDelegateFromAmtGreaterThanEqualZero(fromAmount, i, false);
234 
235                 // TO amount must be >= FROM amount or Zero
236                 success &= checkDelegateToAmtGreaterThanFromAmt(fromAmount, toAmount, i, false);
237 
238                 success &= checkDelegateDocumentTypeCode(newDelegateGlobalDetail.getFinancialDocumentTypeCode(), documentService);
239 
240                 // increment counter for delegate changes list
241                 i++;
242             }
243         }
244         return success;
245     }
246 
247 
248     /**
249      * This method will check through each delegate referenced in the DelegateGlobal to ensure that there is one and only primary
250      * for each account and doctype
251      * 
252      * @return false if there is more than one primary delegate
253      */
254     protected boolean checkForPrimaryDelegateAllLines() {
255         boolean success = true;
256         int i = 0;
257         for (AccountDelegateGlobalDetail newDelegateGlobalDetail : newDelegateGlobal.getDelegateGlobals()) {
258             success &= checkPrimaryRouteRules(newDelegateGlobal.getDelegateGlobals(), newDelegateGlobalDetail, new Integer(i), false);
259             i++;
260         }
261         return success;
262     }
263 
264     /**
265      * This method checks to see if the from amount is greater than zero
266      * 
267      * @param fromAmount
268      * @param lineNum
269      * @return false if from amount less than zero
270      */
271     protected boolean checkDelegateFromAmtGreaterThanEqualZero(KualiDecimal fromAmount, int lineNum, boolean add) {
272         boolean success = true;
273         if (ObjectUtils.isNotNull(fromAmount)) {
274             if (fromAmount.isLessThan(ZERO)) {
275                 String errorPath = OLEConstants.EMPTY_STRING;
276                 if (add) {
277                     errorPath = OLEConstants.MAINTENANCE_ADD_PREFIX + DELEGATE_GLOBALS_PREFIX + "." + "approvalFromThisAmount";
278                     putFieldError(errorPath, OLEKeyConstants.ERROR_DOCUMENT_ACCTDELEGATEMAINT_FROM_AMOUNT_NONNEGATIVE);
279                 }
280                 else {
281                     errorPath = DELEGATE_GLOBALS_PREFIX + "[" + lineNum + "]." + "approvalFromThisAmount";
282                     putFieldError(errorPath, OLEKeyConstants.ERROR_DOCUMENT_ACCTDELEGATEMAINT_FROM_AMOUNT_NONNEGATIVE);
283                 }
284                 success &= false;
285             }
286         }
287         return success;
288     }
289 
290     /**
291      * This method checks to see if the to Amount is greater than the from amount
292      * 
293      * @param fromAmount
294      * @param toAmount
295      * @param lineNum
296      * @return false if to amount less than from amount
297      */
298     protected boolean checkDelegateToAmtGreaterThanFromAmt(KualiDecimal fromAmount, KualiDecimal toAmount, int lineNum, boolean add) {
299         boolean success = true;
300         if (ObjectUtils.isNotNull(toAmount)) {
301 
302             if (!ObjectUtils.isNull(fromAmount)) {
303                 // case if FROM amount is non-null and positive, disallow TO amount being less if it is not ZERO (another indicator
304                 // of infinity)
305                 if (!toAmount.equals(ZERO) && toAmount.isLessThan(fromAmount)) {
306                     String errorPath = OLEConstants.EMPTY_STRING;
307                     if (add) {
308                         errorPath = OLEConstants.MAINTENANCE_ADD_PREFIX + DELEGATE_GLOBALS_PREFIX + "." + "approvalToThisAmount";
309                         putFieldError(errorPath, OLEKeyConstants.ERROR_DOCUMENT_ACCTDELEGATEMAINT_TO_AMOUNT_MORE_THAN_FROM_OR_ZERO);
310                     }
311                     else {
312                         errorPath = DELEGATE_GLOBALS_PREFIX + "[" + lineNum + "]." + "approvalToThisAmount";
313                         putFieldError(errorPath, OLEKeyConstants.ERROR_DOCUMENT_ACCTDELEGATEMAINT_TO_AMOUNT_MORE_THAN_FROM_OR_ZERO);
314                     }
315                     success &= false;
316                 }
317             }
318 
319             if (toAmount.isLessThan(KualiDecimal.ZERO)) {
320                 String errorPath = OLEConstants.EMPTY_STRING;
321                 if (add) {
322                     errorPath = OLEConstants.MAINTENANCE_ADD_PREFIX + DELEGATE_GLOBALS_PREFIX + "." + "approvalToThisAmount";
323                     putFieldError(errorPath, OLEKeyConstants.ERROR_DOCUMENT_ACCTDELEGATEMAINT_TO_AMOUNT_MORE_THAN_FROM_OR_ZERO);
324                 }
325                 else {
326                     errorPath = DELEGATE_GLOBALS_PREFIX + "[" + lineNum + "]." + "approvalToThisAmount";
327                     putFieldError(errorPath, OLEKeyConstants.ERROR_DOCUMENT_ACCTDELEGATEMAINT_TO_AMOUNT_MORE_THAN_FROM_OR_ZERO);
328                 }
329                 success &= false;
330             }
331         }
332         return success;
333     }
334 
335     /**
336      * Validates the document type code for the delegate, to make sure it is a Financial System document type code
337      * 
338      * @param documentTypeCode the document type code to check
339      * @param delegateService a helpful instance of the delegate service, so new ones don't have to be created all the time
340      * @return true if the document type code is valid, false otherwise
341      */
342     protected boolean checkDelegateDocumentTypeCode(String documentTypeCode, FinancialSystemDocumentTypeService documentService) {
343         if (!documentService.isFinancialSystemDocumentType(documentTypeCode)) {
344             String errorPath = OLEConstants.MAINTENANCE_ADD_PREFIX + DELEGATE_GLOBALS_PREFIX + "." + OLEPropertyConstants.FINANCIAL_DOCUMENT_TYPE_CODE;
345             putFieldError(errorPath, OLEKeyConstants.ERROR_DOCUMENT_ACCTDELEGATEMAINT_INVALID_DOC_TYPE, new String[] { documentTypeCode, OLEConstants.ROOT_DOCUMENT_TYPE });
346 
347             return false;
348         }
349         return true;
350     }
351 
352 
353     private boolean checkStartDate(AccountDelegateGlobalDetail delegateGlobalDetail, int lineNum) {
354         boolean success = true;
355         if (ObjectUtils.isNotNull(delegateGlobalDetail.getAccountDelegateStartDate())) {
356             Timestamp today = getDateTimeService().getCurrentTimestamp();
357             today.setTime(DateUtils.truncate(today, Calendar.DAY_OF_MONTH).getTime());
358             if (delegateGlobalDetail.getAccountDelegateStartDate().before(today)) {
359                 success = false;
360                 String errorPath = DELEGATE_GLOBALS_PREFIX + "[" + lineNum + "]." + "accountDelegateStartDate";
361                 putFieldError(errorPath, OLEKeyConstants.ERROR_DOCUMENT_ACCTDELEGATEMAINT_STARTDATE_IN_PAST,new String[0]);
362             }
363         }
364         return success;
365    }
366 
367     /**
368      * This method validates the rule that says there can be only one PrimaryRoute delegate for each given docType. It checks the
369      * delegateGlobalToTest against the list, to determine whether adding this new delegateGlobalToTest would violate any
370      * PrimaryRoute business rule violations. If any of the incoming variables is null or empty, the method will do nothing, and
371      * return Null. It will only process the business rules if there is sufficient data to do so.
372      * 
373      * @param delegateGlobalToTest A delegateGlobal line that you want to test against the list.
374      * @param delegateGlobals A List of delegateGlobal items that is being tested against.
375      * @return Null if the business rule passes, or an Integer value greater than zero, representing the line that the new line is
376      *         conflicting with
377      */
378     protected Integer checkPrimaryRoutePerDocType(AccountDelegateGlobalDetail delegateGlobalToTest, List<AccountDelegateGlobalDetail> delegateGlobals, Integer testLineNum) {
379 
380         // exit immediately if the adding line isnt a Primary routing
381         if (delegateGlobalToTest == null || delegateGlobals == null || delegateGlobals.isEmpty()) {
382             return null;
383         }
384         if (!delegateGlobalToTest.getAccountDelegatePrimaryRoutingIndicator()) {
385             return null;
386         }
387         if (StringUtils.isBlank(delegateGlobalToTest.getFinancialDocumentTypeCode())) {
388             return null;
389         }
390 
391         // at this point, the delegateGlobal being added is a Primary for ALL docTypes, so we need to
392         // test whether any in the existing list are also Primary, regardless of docType
393         String docType = delegateGlobalToTest.getFinancialDocumentTypeCode();
394         AccountDelegateGlobalDetail delegateGlobal = null;
395         for (int lineNumber = 0; lineNumber < delegateGlobals.size(); lineNumber++) {
396             delegateGlobal = delegateGlobals.get(lineNumber);
397             if (delegateGlobal.getAccountDelegatePrimaryRoutingIndicator()) {
398                 if (docType.equalsIgnoreCase(delegateGlobal.getFinancialDocumentTypeCode())) {
399                     if (testLineNum == null) {
400                         return new Integer(lineNumber);
401                     }
402                     else if (!(testLineNum.intValue() == lineNumber)) {
403                         return new Integer(lineNumber);
404                     }
405                 }
406             }
407         }
408 
409         return null;
410     }
411 
412     /**
413      * This checks that the primary routing for delegates is correct, specifically that - there is not already a primary route
414      * delegate setup for this {@link Account}
415      * 
416      * @param delegateGlobals
417      * @param delegateGlobalToTest
418      * @param lineNum
419      * @param add
420      * @return
421      */
422     protected boolean checkPrimaryRouteRules(List<AccountDelegateGlobalDetail> delegateGlobals, AccountDelegateGlobalDetail delegateGlobalToTest, Integer lineNum, boolean add) {
423         boolean success = true;
424 
425         String errorPath = "";
426         Integer result = null;
427         for (AccountDelegateGlobalDetail delegateGlobal : delegateGlobals) {
428 
429             result = checkPrimaryRoutePerDocType(delegateGlobalToTest, delegateGlobals, lineNum);
430             if (result != null) {
431                 if (add) {
432                     errorPath = OLEConstants.MAINTENANCE_ADD_PREFIX + DELEGATE_GLOBALS_PREFIX + "." + "financialDocumentTypeCode";
433                     putFieldError(errorPath, OLEKeyConstants.ERROR_DOCUMENT_GLOBAL_DELEGATEMAINT_PRIMARY_ROUTE_ALREADY_EXISTS_FOR_DOCTYPE);
434                 }
435                 else {
436                     errorPath = DELEGATE_GLOBALS_PREFIX + "[" + lineNum.toString() + "]." + "financialDocumentTypeCode";
437                     putFieldError(errorPath, OLEKeyConstants.ERROR_DOCUMENT_GLOBAL_DELEGATEMAINT_PRIMARY_ROUTE_ALREADY_EXISTS_FOR_DOCTYPE);
438                 }
439                 success &= false;
440             }
441         }
442         return success;
443     }
444 
445     /**
446      * This checks that the delegate for this {@link Account} exists and is valid (active and a professional)
447      * 
448      * @param delegateGlobal
449      * @param lineNum
450      * @param add
451      * @return false if the delegate for the {@link Account} doesn't exist or isn't valid
452      */
453     protected boolean checkDelegateUserRules(MaintenanceDocument document, AccountDelegateGlobalDetail delegateGlobal, int lineNum, boolean add) {
454         boolean success = true;
455         String errorPath = OLEConstants.EMPTY_STRING;
456         
457         Person accountDelegate = delegateGlobal.getAccountDelegate();
458         if (StringUtils.isBlank(delegateGlobal.getAccountDelegateUniversalId()) || ObjectUtils.isNull(accountDelegate)) {
459             if (add) {
460                 errorPath = OLEConstants.MAINTENANCE_ADD_PREFIX + DELEGATE_GLOBALS_PREFIX + "." + "accountDelegate.principalName";
461                 putFieldError(errorPath, OLEKeyConstants.ERROR_DOCUMENT_ACCTDELEGATEMAINT_USER_DOESNT_EXIST);
462             }
463             else {
464                 errorPath = DELEGATE_GLOBALS_PREFIX + "[" + lineNum + "]." + "accountDelegate.principalName";
465                 putFieldError(errorPath, OLEKeyConstants.ERROR_DOCUMENT_ACCTDELEGATEMAINT_USER_DOESNT_EXIST);
466             }
467 
468             success &= false;
469         }
470         else if (!getDocumentHelperService().getDocumentAuthorizer(document).isAuthorized(document, OLEConstants.PermissionNames.SERVE_AS_FISCAL_OFFICER_DELEGATE.namespace, OLEConstants.PermissionNames.SERVE_AS_FISCAL_OFFICER_DELEGATE.name, accountDelegate.getPrincipalId())) {
471             if (add) {
472                 errorPath = OLEConstants.MAINTENANCE_ADD_PREFIX + DELEGATE_GLOBALS_PREFIX + "." + "accountDelegate.principalName";
473                 putFieldError(errorPath, OLEKeyConstants.ERROR_USER_MISSING_PERMISSION, new String[] { accountDelegate.getName(), OLEConstants.PermissionNames.SERVE_AS_FISCAL_OFFICER_DELEGATE.namespace, OLEConstants.PermissionNames.SERVE_AS_FISCAL_OFFICER_DELEGATE.name });
474             }
475             else {
476                 errorPath = DELEGATE_GLOBALS_PREFIX + "[" + lineNum + "]." + "accountDelegate.principalName";
477                 putFieldError(errorPath, OLEKeyConstants.ERROR_USER_MISSING_PERMISSION, new String[] { accountDelegate.getName(), OLEConstants.PermissionNames.SERVE_AS_FISCAL_OFFICER_DELEGATE.namespace, OLEConstants.PermissionNames.SERVE_AS_FISCAL_OFFICER_DELEGATE.name });
478             }
479             
480             success &= false;
481         }
482 
483         return success;
484     }
485 
486     /**
487      * This checks that when a new line is added (either {@link AccountGlobalDetail} or {@link DelegateGlobalDetail}) that the
488      * appropriate rules are run on the new lines being added on {@link AccountGlobalDetail}: - make sure that the account number
489      * and chart are entered
490      * <ul>
491      * <li>{@link DelegateGlobalRule#checkAccountDetails(AccountGlobalDetail)}</li>
492      * </ul>
493      * on {@link DelegateGlobalDetail}
494      * <ul>
495      * <li>{@link DelegateGlobalRule#checkDelegateFromAmtGreaterThanEqualZero(KualiDecimal, int, boolean)}</li>
496      * <li>{@link DelegateGlobalRule#checkDelegateForNullToAmount(KualiDecimal, KualiDecimal, int, boolean)}</li>
497      * <li>{@link DelegateGlobalRule#checkDelegateToAmtGreaterThanFromAmt(KualiDecimal, KualiDecimal, int, boolean)}</li>
498      * <li>{@link DelegateGlobalRule#checkDelegateUserRules(DelegateGlobalDetail, int, boolean)}</li>
499      * <li>{@link DelegateGlobalRule#checkPrimaryRouteRules(List, DelegateGlobalDetail, Integer, boolean)}</li>
500      * </ul>
501      * 
502      * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomAddCollectionLineBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument,
503      *      java.lang.String, org.kuali.rice.krad.bo.PersistableBusinessObject)
504      */
505     @Override
506     public boolean processCustomAddCollectionLineBusinessRules(MaintenanceDocument document, String collectionName, PersistableBusinessObject bo) {
507         boolean success = true;
508         if (bo instanceof AccountGlobalDetail) {
509             AccountGlobalDetail detail = (AccountGlobalDetail) bo;
510             // make sure that both primary keys are available for this object
511             if (!checkEmptyValue(detail.getAccountNumber())) {
512                 // put an error about accountnumber
513                 GlobalVariables.getMessageMap().putError("accountNumber", OLEKeyConstants.ERROR_REQUIRED, "Account Number");
514                 success &= false;
515             }
516             if (!checkEmptyValue(detail.getChartOfAccountsCode())) {
517                 // put an error about chart code
518                 GlobalVariables.getMessageMap().putError("chartOfAccountsCode", OLEKeyConstants.ERROR_REQUIRED, "Chart of Accounts Code");
519                 success &= false;
520             }
521             success &= checkAccountDetails(detail);
522         }
523         else if (bo instanceof AccountDelegateGlobalDetail) {
524             AccountDelegateGlobalDetail detail = (AccountDelegateGlobalDetail) bo;
525             detail.refreshNonUpdateableReferences();
526             KualiDecimal fromAmount = detail.getApprovalFromThisAmount();
527             KualiDecimal toAmount = detail.getApprovalToThisAmount();
528 
529             // check the start date
530             success &= checkStartDate(detail, 0);
531 
532             // FROM amount must be >= 0 (may not be negative)
533             success &= checkDelegateFromAmtGreaterThanEqualZero(fromAmount, 0, true);
534 
535             // TO amount must be >= FROM amount or Zero
536             success &= checkDelegateToAmtGreaterThanFromAmt(fromAmount, toAmount, 0, true);
537 
538             // check the user that is being added
539             // TODO: add back in once the user issues have been fixed
540             success &= checkDelegateUserRules(document, detail, 0, true);
541 
542             // check the routing
543             success &= checkPrimaryRouteRules(newDelegateGlobal.getDelegateGlobals(), detail, null, true);
544 
545             final FinancialSystemDocumentTypeService documentService = SpringContext.getBean(FinancialSystemDocumentTypeService.class);
546             success &= checkDelegateDocumentTypeCode(detail.getFinancialDocumentTypeCode(), documentService);
547 
548         }
549         return success;
550     }
551 
552 }