View Javadoc
1   /*
2    * Copyright 2006 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.Date;
19  import java.sql.Timestamp;
20  import java.util.Calendar;
21  import java.util.HashMap;
22  import java.util.Iterator;
23  import java.util.List;
24  import java.util.Map;
25  
26  import org.apache.commons.lang.StringUtils;
27  import org.apache.commons.lang.time.DateUtils;
28  import org.kuali.ole.coa.businessobject.Account;
29  import org.kuali.ole.coa.businessobject.Organization;
30  import org.kuali.ole.coa.service.OrganizationService;
31  import org.kuali.ole.sys.OLEConstants;
32  import org.kuali.ole.sys.OLEKeyConstants;
33  import org.kuali.ole.sys.OLEPropertyConstants;
34  import org.kuali.ole.sys.context.SpringContext;
35  import org.kuali.ole.sys.identity.OleKimAttributes;
36  import org.kuali.rice.core.api.parameter.ParameterEvaluatorService;
37  import org.kuali.rice.coreservice.framework.parameter.ParameterService;
38  import org.kuali.rice.kim.api.KimConstants;
39  import org.kuali.rice.kim.api.identity.Person;
40  import org.kuali.rice.kim.api.services.IdentityManagementService;
41  import org.kuali.rice.kns.document.MaintenanceDocument;
42  import org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase;
43  import org.kuali.rice.krad.util.GlobalVariables;
44  import org.kuali.rice.krad.util.ObjectUtils;
45  
46  /**
47   * This class implements the business rules specific to the {@link Org} Maintenance Document.
48   */
49  public class OrgRule extends MaintenanceDocumentRuleBase {
50  
51      protected static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(OrgRule.class);
52  
53  
54      protected static OrganizationService orgService;
55  
56      protected Organization oldOrg;
57      protected Organization newOrg;
58      protected boolean isHrmsOrgActivated;
59  
60      /**
61       * Constructs a OrgRule and pseudo-injects services
62       */
63      public OrgRule() {
64          super();
65  
66          // Pseudo-inject some services.
67          //
68          // This approach is being used to make it simpler to convert the Rule classes
69          // to spring-managed with these services injected by Spring at some later date.
70          // When this happens, just remove these calls to the setters with
71          // SpringContext, and configure the bean defs for spring.
72          if (orgService == null) {
73              orgService = SpringContext.getBean(OrganizationService.class);
74          }
75      }
76  
77      /**
78       * This performs the following checks on document approve:
79       * <ul>
80       * <li>{@link OrgRule#checkExistenceAndActive()}</li>
81       * <li>{@link OrgRule#checkOrgClosureRules(MaintenanceDocument)}</li>
82       * <li>{@link OrgRule#checkSimpleRules(MaintenanceDocument)}</li>
83       * <li>{@link OrgRule#checkDefaultAccountNumber(MaintenanceDocument)}</li>
84       * </ul>
85       * This rule fails on rule failure
86       *
87       * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomApproveDocumentBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument)
88       */
89      @Override
90      protected boolean processCustomApproveDocumentBusinessRules(MaintenanceDocument document) {
91  
92          boolean success = true;
93  
94          LOG.debug("Entering processCustomApproveDocumentBusinessRules()");
95  
96          // determine whether HRMS ORG is activated in this app instance
97          isHrmsOrgActivated = isHrmsOrgActivated();
98  
99          // check that all sub-objects whose keys are specified have matching objects in the db
100         success &= checkExistenceAndActive();
101 
102         success &= checkOrgClosureRules(document);
103 
104         // check that end date is greater than begin date and Reports To Chart/Org should not be same as this Chart/Org
105         success &= checkSimpleRules(document);
106 
107         // check that defaultAccount is present unless
108         // ( (orgType = U or C) and ( document is a "create new" ))
109 
110         //Code changes for JIRA OLE2344 don't check default account number as account no is made optional
111         //success &= checkDefaultAccountNumber(document);
112 
113         return success;
114     }
115 
116     /**
117      * This performs the following checks on document route:
118      * <ul>
119      * <li>{@link OrgRule#checkExistenceAndActive()}</li>
120      * <li>{@link OrgRule#checkOrgClosureRules(MaintenanceDocument)}</li>
121      * <li>{@link OrgRule#checkSimpleRules(MaintenanceDocument)}</li>
122      * <li>{@link OrgRule#checkDefaultAccountNumber(MaintenanceDocument)}</li>
123      * </ul>
124      * This rule fails on rule failure
125      *
126      * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomRouteDocumentBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument)
127      */
128     @Override
129     protected boolean processCustomRouteDocumentBusinessRules(MaintenanceDocument document) {
130 
131         boolean success = true;
132 
133         LOG.debug("Entering processCustomRouteDocumentBusinessRules()");
134 
135         // determine whether HRMS ORG is activated in this app instance
136         isHrmsOrgActivated = isHrmsOrgActivated();
137 
138         // check that all sub-objects whose keys are specified have matching objects in the db
139         success &= checkExistenceAndActive();
140 
141         // check that end date is greater than begin date and Reports To Chart/Org should not be same as this Chart/Org
142         success &= checkSimpleRules(document);
143 
144         // check that defaultAccount is present unless
145         // ( (orgType = U or C) and ( document is a "create new" ))
146 
147         //Code changes for JIRA OLE2344 don't check default account number as account no is made optional
148         //success &= checkDefaultAccountNumber(document);
149 
150         success &= checkOrgClosureRules(document);
151 
152         return success;
153     }
154 
155     /**
156      * This performs the following checks on document save:
157      * <ul>
158      * <li>{@link OrgRule#checkExistenceAndActive()}</li>
159      * <li>{@link OrgRule#checkOrgClosureRules(MaintenanceDocument)}</li>
160      * <li>{@link OrgRule#checkSimpleRules(MaintenanceDocument)}</li>
161      * <li>{@link OrgRule#checkDefaultAccountNumber(MaintenanceDocument)}</li>
162      * </ul>
163      * This rule does not fail on rule failure
164      *
165      * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomSaveDocumentBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument)
166      */
167     @Override
168     protected boolean processCustomSaveDocumentBusinessRules(MaintenanceDocument document) {
169 
170         LOG.debug("Entering processCustomSaveDocumentBusinessRules()");
171 
172         // determine whether HRMS ORG is activated in this app instance
173         isHrmsOrgActivated = isHrmsOrgActivated();
174 
175         // check that all sub-objects whose keys are specified have matching objects in the db
176         checkExistenceAndActive();
177 
178         checkOrgClosureRules(document);
179 
180         // check that end date is greater than begin date and Reports To Chart/Org should not be same as this Chart/Org
181         checkSimpleRules(document);
182 
183         // check that defaultAccount is present unless
184         // ( (orgType = U or C) and ( document is a "create new" ))
185 
186         //Code changes for JIRA OLE2344 don't check default account number as account no is made optional
187         //checkDefaultAccountNumber(document);
188 
189         return true;
190     }
191 
192     /**
193      * This checks to see if the org is active
194      *
195      * @return true if the org is inactive or false otherwise
196      */
197     protected boolean checkExistenceAndActive() {
198 
199         LOG.debug("Entering checkExistenceAndActive()");
200         boolean success = true;
201 
202         // shortcut out with no enforcement if this org is closed
203         if (!newOrg.isActive()) {
204             return success;
205         }
206 
207         // Disabling plant attribute checking
208 //        success &= checkPlantAttributes();
209 
210         return success;
211     }
212 
213     /**
214      * This checks to see if a user is authorized for plant fields modification. If not then it returns true (without activating
215      * fields). If the org does not have to report to itself then it checks to see if the plant fields have been filled out
216      * correctly and fails if they haven't
217      *
218      * @return false if user can edit plant fields but they have not been filled out correctly
219      */
220     protected boolean checkPlantAttributes() {
221 
222         boolean success = true;
223 
224         /*
225          * KULCOA-1132 - exit if the user is not a member of the plant maintainer work group.
226          */
227 
228         // get user
229         Person user = GlobalVariables.getUserSession().getPerson();
230 
231         // if not authroized to edit plant fields, exit with true
232         if (isPlantAuthorized(user) == false) {
233             return true;
234         }
235 
236         // relax this edit for
237         if (!getOrgMustReportToSelf(newOrg)) {
238             // require Org Plant ChartCode
239             success &= checkEmptyBOField("organizationPlantChartCode", newOrg.getOrganizationPlantChartCode(), "Organization Plant Chart of Accounts Code");
240 
241             // require Org Plant AccountNumber
242             success &= checkEmptyBOField("organizationPlantAccountNumber", newOrg.getOrganizationPlantAccountNumber(), "Organization Plant Account Number");
243 
244             // require Campus Plant ChartCode
245             success &= checkEmptyBOField("campusPlantChartCode", newOrg.getCampusPlantChartCode(), "Campus Plant Chart of Accounts Code");
246 
247             // require Org Plant ChartCode
248             success &= checkEmptyBOField("campusPlantAccountNumber", newOrg.getCampusPlantAccountNumber(), "Campus Plant Account Number");
249 
250             // validate Org Plant Account
251             success &= getDictionaryValidationService().validateReferenceExistsAndIsActive(newOrg, "organizationPlantAccount", MAINTAINABLE_ERROR_PREFIX + "organizationPlantAccountNumber", "Organization Plant Account");
252 
253             // validate Campus Plant Account
254             success &= getDictionaryValidationService().validateReferenceExistsAndIsActive(newOrg, "campusPlantAccount", MAINTAINABLE_ERROR_PREFIX + "campusPlantAccountNumber", "Campus Plant Account");
255         }
256 
257         return success;
258     }
259 
260     /**
261      * This method enforces the business rules surrounding when an Org becomes closed/inactive. If we are editing and switching the
262      * org to inactive or if it is a new doc and it is marked as inactive then we assume we are closing the org. If we are not then
263      * we return true. If we are then we return false if there are still active accounts tied to the org
264      *
265      * @param document
266      * @return false if trying to close org but it still has accounts that are active linked to it
267      */
268     protected boolean checkOrgClosureRules(MaintenanceDocument document) {
269 
270         boolean success = true;
271         boolean orgBeingClosed = false;
272         boolean checkForChildObjects = true;
273 
274         // if its an edit, and its being closed
275         if (document.isEdit()) {
276             if (oldOrg.isActive() && !newOrg.isActive()) {
277                 orgBeingClosed = true;
278             }
279         }
280 
281         // if its new, and is being created as closed
282         if (document.isNew()) {
283             if (!newOrg.isActive()) {
284                 orgBeingClosed = true;
285                 // Since it's new, we don't need to check for accounts and orgs
286                 checkForChildObjects = false;
287             }
288         }
289 
290         // if the org isnt being closed, stop processing here
291         if (!orgBeingClosed) {
292             return success;
293         }
294 
295         // FROM HERE ON WE'RE ASSUMING THE ORG IS BEING CLOSED
296 
297         // do not allow the org to be closed while there are active accounts tied
298         // to this org
299         if (checkForChildObjects) {
300             List childAccounts = orgService.getActiveAccountsByOrg(newOrg.getChartOfAccountsCode(),
301                     newOrg.getOrganizationCode());
302             if (childAccounts.size() > 0) {
303 
304                 // get the first three accounts on the list for display
305                 StringBuffer childAccountList = new StringBuffer();
306                 int count = 0;
307                 String delim = "";
308                 for (Iterator iter = childAccounts.iterator(); iter.hasNext();) {
309                     Account account = (Account) iter.next();
310                     childAccountList
311                             .append(delim + account.getChartOfAccountsCode() + "-" + account.getAccountNumber());
312                     count++;
313                     if (count >= 1) {
314                         delim = ", ";
315                     }
316                     if (count >= 3) {
317                         break;
318                     }
319                 }
320                 if (childAccounts.size() > count) {
321                     childAccountList.append(", ... (" + (childAccounts.size() - count) + " more)");
322                 }
323 
324                 putGlobalError(OLEKeyConstants.ERROR_DOCUMENT_ORGMAINT_OPEN_CHILD_ACCOUNTS_ON_ORG_CLOSURE,
325                         childAccountList.toString());
326                 success &= false;
327             }
328 
329             // do not allow this org to be closed while there are still active orgs
330             // that have this org as their reportsToOrg
331             List childOrgs = orgService.getActiveChildOrgs(newOrg.getChartOfAccountsCode(),
332                     newOrg.getOrganizationCode());
333             if (childOrgs.size() > 0) {
334 
335                 // get the first three orgs on the list for display
336                 StringBuffer childOrgsList = new StringBuffer();
337                 int count = 0;
338                 String delim = "";
339                 for (Iterator iter = childOrgs.iterator(); iter.hasNext();) {
340                     Organization org = (Organization) iter.next();
341                     childOrgsList.append(delim + org.getChartOfAccountsCode() + "-" + org.getOrganizationCode());
342                     count++;
343                     if (count >= 1) {
344                         delim = ", ";
345                     }
346                     if (count >= 3) {
347                         break;
348                     }
349                 }
350                 if (childOrgs.size() > count) {
351                     childOrgsList.append(", ... (" + (childOrgs.size() - count) + " more)");
352                 }
353 
354                 putGlobalError(OLEKeyConstants.ERROR_DOCUMENT_ORGMAINT_OPEN_CHILD_ORGS_ON_ORG_CLOSURE,
355                         childOrgsList.toString());
356                 success &= false;
357             }
358         }
359 
360         // if org is being closed, end-date must be valid and present
361         if (ObjectUtils.isNull(newOrg.getOrganizationEndDate())) {
362             success &= false;
363             putFieldError("organizationEndDate", OLEKeyConstants.ERROR_DOCUMENT_ORGMAINT_END_DATE_REQUIRED_ON_ORG_CLOSURE);
364         }
365         return success;
366 
367     }
368 
369     /**
370      * This checks to see if the org is active and if it the HRMS org is active
371      *
372      * @param document
373      * @return true if either the org is inactive or isHrmsOrgActivated is false
374      */
375     protected boolean checkHrmsOrgRules(MaintenanceDocument document) {
376 
377         boolean success = true;
378 
379         // shortcut out with no enforcement if this org is closed
380         if (!newOrg.isActive()) {
381             return success;
382         }
383 
384         // short circuit and fail if HRMSOrg is turned off
385         if (!isHrmsOrgActivated) {
386             return success;
387         }
388 
389         // if the system has a HRMS Org record attached to this org record, then prompt the
390         // user to fill out the HRMS Org info
391 
392         // HRMS Org Campus == Org Campus
393         // HRMS Org campus code must be the same as Org campus code
394 
395 
396         // if the
397         return success;
398     }
399 
400     /**
401      * This checks our {@link Parameter} rules to see if this org needs to report to itself
402      *
403      * @param organization
404      * @return true if it does
405      */
406     protected boolean getOrgMustReportToSelf(Organization organization) {
407         return /*REFACTORME*/SpringContext.getBean(ParameterEvaluatorService.class).getParameterEvaluator(Organization.class, OLEConstants.ChartApcParms.ORG_MUST_REPORT_TO_SELF_ORG_TYPES, organization.getOrganizationTypeCode()).evaluationSucceeds();
408     }
409 
410     /**
411      * This checks the following conditions:
412      * <ul>
413      * <li>begin date must be greater than or equal to end date</li>
414      * <li>start date must be greater than or equal to today if new Document</li>
415      * <li>Reports To Chart/Org should not be same as this Chart/Org</li>
416      * </ul>
417      *
418      * @param document
419      * @return true if it passes all the rules, false otherwise
420      */
421     protected boolean checkSimpleRules(MaintenanceDocument document) {
422 
423         boolean success = true;
424         String lastReportsToChartOfAccountsCode;
425         String lastReportsToOrganizationCode;
426         boolean continueSearch;
427         Organization tempOrg;
428         Integer loopCount;
429         Integer maxLoopCount = 40;
430 
431         // begin date must be greater than or equal to end date
432         if ((ObjectUtils.isNotNull(newOrg.getOrganizationBeginDate()) && (ObjectUtils.isNotNull(newOrg.getOrganizationEndDate())))) {
433 
434             Date beginDate = newOrg.getOrganizationBeginDate();
435             Date endDate = newOrg.getOrganizationEndDate();
436 
437             if (endDate.before(beginDate)) {
438                 putFieldError("organizationEndDate", OLEKeyConstants.ERROR_DOCUMENT_ORGMAINT_END_DATE_GREATER_THAN_BEGIN_DATE);
439                 success &= false;
440             }
441         }
442 
443         // start date must be greater than or equal to today if new Document
444         if ((ObjectUtils.isNotNull(newOrg.getOrganizationBeginDate()) && (document.isNew()))) {
445             Timestamp today = getDateTimeService().getCurrentTimestamp();
446             today.setTime(DateUtils.truncate(today, Calendar.DAY_OF_MONTH).getTime());
447             if (newOrg.getOrganizationBeginDate().before(today)) {
448                 putFieldError("organizationBeginDate", OLEKeyConstants.ERROR_DOCUMENT_ORGMAINT_STARTDATE_IN_PAST);
449                 success &= false;
450             }
451         }
452 
453         // Reports To Chart/Org should not be same as this Chart/Org
454         // However, allow special case where organization type is listed in the business rules
455         if (ObjectUtils.isNotNull(newOrg.getReportsToChartOfAccountsCode()) && ObjectUtils.isNotNull(newOrg.getReportsToOrganizationCode()) && ObjectUtils.isNotNull(newOrg.getChartOfAccountsCode()) && ObjectUtils.isNotNull(newOrg.getOrganizationCode())) {
456             if (!getOrgMustReportToSelf(newOrg)) {
457 
458                 if ((newOrg.getReportsToChartOfAccountsCode().equals(newOrg.getChartOfAccountsCode())) && (newOrg.getReportsToOrganizationCode().equals(newOrg.getOrganizationCode()))) {
459                     putFieldError("reportsToOrganizationCode", OLEKeyConstants.ERROR_DOCUMENT_ORGMAINT_REPORTING_ORG_CANNOT_BE_SAME_ORG);
460                     success = false;
461                 }
462                 else {
463                     // Don't allow a circular reference on Reports to Chart/Org
464                     // terminate the search when a top-level org is found
465                     lastReportsToChartOfAccountsCode = newOrg.getReportsToChartOfAccountsCode();
466                     lastReportsToOrganizationCode = newOrg.getReportsToOrganizationCode();
467                     continueSearch = true;
468                     loopCount = 0;
469                     do {
470                         tempOrg = orgService.getByPrimaryId(lastReportsToChartOfAccountsCode, lastReportsToOrganizationCode);
471                         loopCount++;
472                         ;
473                         if (ObjectUtils.isNull(tempOrg)) {
474                             continueSearch = false;
475                             // if a null is returned on the first iteration, then the reports-to org does not exist
476                             // fail the validation
477                             if (loopCount == 1) {
478                                 putFieldError("reportsToOrganizationCode", OLEKeyConstants.ERROR_DOCUMENT_ORGMAINT_REPORTING_ORG_MUST_EXIST);
479                                 success = false;
480                             }
481                         }
482                         else {
483                             // on the first iteration, check whether the reports-to organization is active
484                             if (loopCount == 1 && !tempOrg.isActive()) {
485                                 putFieldError("reportsToOrganizationCode", OLEKeyConstants.ERROR_DOCUMENT_ORGMAINT_REPORTING_ORG_MUST_EXIST);
486                                 success = false;
487                                 continueSearch = false;
488                             }
489                             else {
490                                 // LOG.info("Found Org = " + lastReportsToChartOfAccountsCode + "/" +
491                                 // lastReportsToOrganizationCode);
492                                 lastReportsToChartOfAccountsCode = tempOrg.getReportsToChartOfAccountsCode();
493                                 lastReportsToOrganizationCode = tempOrg.getReportsToOrganizationCode();
494 
495                                 if ((tempOrg.getReportsToChartOfAccountsCode().equals(newOrg.getChartOfAccountsCode())) && (tempOrg.getReportsToOrganizationCode().equals(newOrg.getOrganizationCode()))) {
496                                     putFieldError("reportsToOrganizationCode", OLEKeyConstants.ERROR_DOCUMENT_ORGMAINT_REPORTING_ORG_CANNOT_BE_CIRCULAR_REF_TO_SAME_ORG);
497                                     success = false;
498                                     continueSearch = false;
499                                 }
500                             }
501                         }
502                         if (loopCount > maxLoopCount) {
503                             continueSearch = false;
504                         }
505                         // stop the search if we reach an org that must report to itself
506                         if (continueSearch && /*REFACTORME*/SpringContext.getBean(ParameterEvaluatorService.class).getParameterEvaluator(Organization.class, OLEConstants.ChartApcParms.ORG_MUST_REPORT_TO_SELF_ORG_TYPES, tempOrg.getOrganizationTypeCode()).evaluationSucceeds()) {
507                             continueSearch = false;
508                         }
509 
510                     } while (continueSearch == true);
511                 } // end else (checking for circular ref)
512             }
513             else { // org must report to self (university level organization)
514                 if (!(newOrg.getReportsToChartOfAccountsCode().equals(newOrg.getChartOfAccountsCode()) && newOrg.getReportsToOrganizationCode().equals(newOrg.getOrganizationCode()))) {
515                     putFieldError("reportsToOrganizationCode", OLEKeyConstants.ERROR_DOCUMENT_ORGMAINT_REPORTING_ORG_MUST_BE_SAME_ORG);
516                     success = false;
517                 }
518                 // org must be the only one of that type
519                 String topLevelOrgTypeCode = SpringContext.getBean(ParameterService.class).getParameterValueAsString(Organization.class, OLEConstants.ChartApcParms.ORG_MUST_REPORT_TO_SELF_ORG_TYPES);
520                 List<Organization> topLevelOrgs = orgService.getActiveOrgsByType(topLevelOrgTypeCode);
521                 if (!topLevelOrgs.isEmpty()) {
522                     // is the new org in the topLevelOrgs list? If not, then there's an error; if so, we're editing the top level
523                     // org
524                     if (!topLevelOrgs.contains(newOrg)) {
525                         putFieldError("organizationTypeCode", OLEKeyConstants.ERROR_DOCUMENT_ORGMAINT_ONLY_ONE_TOP_LEVEL_ORG, topLevelOrgs.get(0).getChartOfAccountsCode() + "-" + topLevelOrgs.get(0).getOrganizationCode());
526                         success = false;
527                     }
528                 }
529             }
530         }
531 
532 
533         return success;
534     }
535 
536 
537     /**
538      * This checks that defaultAccount is present unless ( (orgType = U or C) and ( document is a "create new" or "edit" ))
539      *
540      * @param document
541      * @return false if missing default account number and it is not an exempt type code
542      */
543     protected boolean checkDefaultAccountNumber(MaintenanceDocument document) {
544 
545         boolean success = true;
546         boolean exemptOrganizationTypeCode = false;
547         boolean missingDefaultAccountNumber = StringUtils.isBlank(newOrg.getOrganizationDefaultAccountNumber());
548 
549         if (ObjectUtils.isNotNull(newOrg.getOrganizationTypeCode())) {
550             String organizationTypeCode = newOrg.getOrganizationTypeCode();
551             if (/*REFACTORME*/SpringContext.getBean(ParameterEvaluatorService.class).getParameterEvaluator(Organization.class, OLEConstants.ChartApcParms.DEFAULT_ACCOUNT_NOT_REQUIRED_ORG_TYPES, newOrg.getOrganizationTypeCode()).evaluationSucceeds()) {
552                 exemptOrganizationTypeCode = true;
553             }
554         }
555 
556         if (missingDefaultAccountNumber && (!exemptOrganizationTypeCode || (!document.isNew() && !document.isEdit()))) {
557             putFieldError("organizationDefaultAccountNumber", OLEKeyConstants.ERROR_DOCUMENT_ORGMAINT_DEFAULT_ACCOUNT_NUMBER_REQUIRED);
558             success &= false;
559         }
560 
561         return success;
562     }
563 
564     /**
565      * This method compares an old and new value, and determines if they've changed. If the old was null/blank, and the new is not,
566      * return true. If the old had a value, and the new is null/blank, return true. If both old and new had a value, and the values
567      * are different (excluding trailing or leading whitespaces, and excluding case changes), return true. If none of the above,
568      * return false.
569      *
570      * @param oldValue - Old value to test.
571      * @param newValue - New value to test.
572      * @return true or false, based on the algorithm described above.
573      */
574     protected boolean fieldsHaveChanged(String oldValue, String newValue) {
575 
576         // if old was null/blank and new is not
577         if (StringUtils.isBlank(oldValue) && StringUtils.isNotBlank(newValue)) {
578             return true;
579         }
580 
581         // if old had a value, but new is null/blank
582         if (StringUtils.isNotBlank(oldValue) && StringUtils.isBlank(newValue)) {
583             return true;
584         }
585 
586         // at this point, we know that we had a value before, and we have a
587         // value now, so we need to test whether this value has changed
588         if (oldValue != null && newValue != null) {
589             if (!oldValue.trim().equalsIgnoreCase(newValue.trim())) {
590                 return true;
591             }
592         }
593 
594         // if we've made it to here, then no changes have happened to the values
595         return false;
596     }
597 
598     /**
599      * This method looks up in the ParameterService whether ther HRMS Org system is turned on.
600      *
601      * @return true or false depending on the app configuration
602      */
603     protected boolean isHrmsOrgActivated() {
604         return SpringContext.getBean(ParameterService.class).getParameterValueAsBoolean(Organization.class, OLEConstants.ChartApcParms.APC_HRMS_ACTIVE_KEY);
605     }
606 
607     /**
608      * This method sets the convenience objects like newOrg and oldOrg, so you have short and easy handles to the new and old
609      * objects contained in the maintenance document. It also calls the BusinessObjectBase.refresh(), which will attempt to load all
610      * sub-objects from the DB by their primary keys, if available.
611      *
612      * @param document - the maintenanceDocument being evaluated
613      */
614     @Override
615     public void setupConvenienceObjects() {
616 
617         // setup oldAccount convenience objects, make sure all possible sub-objects are populated
618         oldOrg = (Organization) super.getOldBo();
619 
620         // setup newAccount convenience objects, make sure all possible sub-objects are populated
621         newOrg = (Organization) super.getNewBo();
622     }
623 
624     /**
625      * This method tests whether the specified user is part of the group that grants authorization to the Plant fields.
626      *
627      * @param user - the user to test
628      * @return true if user is part of the group, false otherwise
629      */
630     protected boolean isPlantAuthorized(Person user) {
631         String principalId = user.getPrincipalId();
632         String namespaceCode = OLEConstants.ParameterNamespaces.KNS;
633         String permissionTemplateName = KimConstants.PermissionTemplateNames.MODIFY_FIELD;
634 
635         Map<String,String> roleQualifiers = new HashMap<String,String>();
636         roleQualifiers.put(OleKimAttributes.CHART_OF_ACCOUNTS_CODE, newOrg.getChartOfAccountsCode());
637 
638         Map<String,String> permissionDetails = new HashMap<String,String>();
639         permissionDetails.put(KimConstants.AttributeConstants.COMPONENT_NAME, Organization.class.getSimpleName());
640         permissionDetails.put(KimConstants.AttributeConstants.PROPERTY_NAME, OLEPropertyConstants.ORGANIZATION_PLANT_CHART_CODE);
641 
642         IdentityManagementService identityManagementService = SpringContext.getBean(IdentityManagementService.class);
643         Boolean isAuthorized = identityManagementService.isAuthorizedByTemplateName(principalId, namespaceCode, permissionTemplateName, permissionDetails, roleQualifiers);
644         if (!isAuthorized) {
645             if(LOG.isDebugEnabled()) {
646                 LOG.debug("User '" + user.getPrincipalName() + "' has no access to the Plant Chart.");
647             }
648             return false;
649         }
650 
651         permissionDetails.put(KimConstants.AttributeConstants.PROPERTY_NAME, OLEPropertyConstants.ORGANIZATION_PLANT_ACCOUNT_NUMBER);
652         isAuthorized = identityManagementService.isAuthorizedByTemplateName(principalId, namespaceCode, permissionTemplateName, permissionDetails, roleQualifiers);
653         if (!isAuthorized) {
654             if(LOG.isDebugEnabled()) {
655                 LOG.debug("User '" + user.getPrincipalName() + "' has no access to the Plant account.");
656             }
657             return false;
658         }
659 
660         if(LOG.isDebugEnabled()) {
661             LOG.debug("User '" + user.getPrincipalName() + "' has access to the Plant fields.");
662         }
663         return true;
664     }
665 }