001 /* 002 * Copyright 2011 The Kuali Foundation. 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package org.kuali.kfs.coa.document.validation.impl; 017 018 import java.util.ArrayList; 019 import java.util.Collection; 020 import java.util.HashMap; 021 import java.util.List; 022 import java.util.Map; 023 import java.util.Properties; 024 import java.util.Set; 025 026 import org.apache.commons.lang.StringUtils; 027 import org.kuali.kfs.coa.businessobject.IndirectCostRecoveryExclusionAccount; 028 import org.kuali.kfs.coa.businessobject.ObjectCode; 029 import org.kuali.kfs.coa.businessobject.ObjectCodeGlobal; 030 import org.kuali.kfs.coa.businessobject.ObjectCodeGlobalDetail; 031 import org.kuali.kfs.coa.businessobject.ObjectLevel; 032 import org.kuali.kfs.coa.businessobject.OffsetDefinition; 033 import org.kuali.kfs.coa.service.ObjectCodeService; 034 import org.kuali.kfs.coa.service.ObjectLevelService; 035 import org.kuali.kfs.sys.KFSConstants; 036 import org.kuali.kfs.sys.KFSKeyConstants; 037 import org.kuali.kfs.sys.KFSPropertyConstants; 038 import org.kuali.kfs.sys.context.SpringContext; 039 import org.kuali.kfs.sys.service.UniversityDateService; 040 import org.kuali.rice.kns.bo.BusinessObject; 041 import org.kuali.rice.kns.bo.GlobalBusinessObject; 042 import org.kuali.rice.kns.bo.PersistableBusinessObject; 043 import org.kuali.rice.kns.datadictionary.InactivationBlockingMetadata; 044 import org.kuali.rice.kns.document.MaintenanceDocument; 045 import org.kuali.rice.kns.maintenance.Maintainable; 046 import org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase; 047 import org.kuali.rice.kns.service.BusinessObjectService; 048 import org.kuali.rice.kns.service.InactivationBlockingDetectionService; 049 import org.kuali.rice.kns.service.KNSServiceLocator; 050 import org.kuali.rice.kns.util.GlobalVariables; 051 import org.kuali.rice.kns.util.KNSConstants; 052 import org.kuali.rice.kns.util.ObjectUtils; 053 import org.kuali.rice.kns.util.UrlFactory; 054 055 /** 056 * This class represents the business rules for the maintenance of {@link ObjectCodeGlobal} business objects 057 */ 058 public class ObjectCodeGlobalRule extends MaintenanceDocumentRuleBase { 059 protected ObjectCodeGlobal objectCodeGlobal; 060 protected ObjectCodeService objectCodeService; 061 protected ObjectLevelService objectLevelService; 062 063 public ObjectCodeGlobalRule() { 064 super(); 065 setObjectCodeService(SpringContext.getBean(ObjectCodeService.class)); 066 setObjectLevelService(SpringContext.getBean(ObjectLevelService.class)); 067 } 068 069 070 /** 071 * This method sets the convenience objects like objectCodeGlobal, so you have short and easy handles to the new and 072 * old objects contained in the maintenance document. It also calls the BusinessObjectBase.refresh(), which will attempt to load 073 * all sub-objects from the DB by their primary keys, if available. 074 * 075 * @param document - the maintenanceDocument being evaluated 076 */ 077 @Override 078 public void setupConvenienceObjects() { 079 080 // setup ObjectCodeGlobal convenience objects, 081 // make sure all possible sub-objects are populated 082 objectCodeGlobal = (ObjectCodeGlobal) super.getNewBo(); 083 084 // forces refreshes on all the sub-objects in the lists 085 for (ObjectCodeGlobalDetail objectCodeGlobalDetail : objectCodeGlobal.getObjectCodeGlobalDetails()) { 086 objectCodeGlobalDetail.refreshNonUpdateableReferences(); 087 } 088 } 089 090 /** 091 * This performs rules checks on document approve 092 * <ul> 093 * <li>{@link ObjectCodeGlobalRule#checkSimpleRulesAllLines()}</li> 094 * </ul> 095 * This rule fails on business rule failures 096 * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomApproveDocumentBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument) 097 */ 098 @Override 099 protected boolean processCustomApproveDocumentBusinessRules(MaintenanceDocument document) { 100 boolean success = true; 101 setupConvenienceObjects(); 102 // check simple rules 103 success &= checkSimpleRulesAllLines(); 104 return success; 105 } 106 107 /** 108 * This performs rules checks on document route 109 * <ul> 110 * <li>{@link ObjectCodeGlobalRule#checkSimpleRulesAllLines()}</li> 111 * </ul> 112 * This rule fails on business rule failures 113 * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomRouteDocumentBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument) 114 */ 115 @Override 116 protected boolean processCustomRouteDocumentBusinessRules(MaintenanceDocument document) { 117 boolean success = true; 118 setupConvenienceObjects(); 119 // check simple rules 120 success &= checkSimpleRulesAllLines(); 121 return success; 122 } 123 124 125 @Override 126 protected boolean processInactivationBlockChecking(MaintenanceDocument maintenanceDocument) { 127 boolean success = true; 128 if (!objectCodeGlobal.isFinancialObjectActiveIndicator()) { 129 // we can only inactivate if the new active status will be false, now check whether the object codes on the document exist and are currently true 130 List<ObjectCodeGlobalDetail> objectCodeGlobalDetails = objectCodeGlobal.getObjectCodeGlobalDetails(); 131 for (int i = 0; i < objectCodeGlobalDetails.size(); i++) { 132 ObjectCodeGlobalDetail objectCodeGlobalDetail = objectCodeGlobalDetails.get(i); 133 // get current object code from the DB 134 ObjectCode objectCode = objectCodeService.getByPrimaryId(objectCodeGlobalDetail.getUniversityFiscalYear(), objectCodeGlobalDetail.getChartOfAccountsCode(), objectCodeGlobal.getFinancialObjectCode()); 135 if (ObjectUtils.isNotNull(objectCode)) { 136 if (objectCode.isActive()) { 137 // now we know that the document intends to inactivate this object code... check to see whether a record blocks it 138 success &= processInactivationBlockChecking(maintenanceDocument.getNewMaintainableObject(), objectCode, i); 139 } 140 } 141 } 142 } 143 return success; 144 } 145 146 protected boolean processInactivationBlockChecking(Maintainable maintainable, ObjectCode objectCode, int index) { 147 Set<InactivationBlockingMetadata> inactivationBlockingMetadatas = ddService.getAllInactivationBlockingDefinitions(ObjectCode.class); 148 for (InactivationBlockingMetadata inactivationBlockingMetadata : inactivationBlockingMetadatas) { 149 String inactivationBlockingDetectionServiceBeanName = inactivationBlockingMetadata.getInactivationBlockingDetectionServiceBeanName(); 150 if (StringUtils.isBlank(inactivationBlockingDetectionServiceBeanName)) { 151 inactivationBlockingDetectionServiceBeanName = KNSServiceLocator.DEFAULT_INACTIVATION_BLOCKING_DETECTION_SERVICE; 152 } 153 InactivationBlockingDetectionService inactivationBlockingDetectionService = KNSServiceLocator.getInactivationBlockingDetectionService(inactivationBlockingDetectionServiceBeanName); 154 155 Collection<BusinessObject> blockingBusinessObjects = inactivationBlockingDetectionService.listAllBlockerRecords(objectCode, inactivationBlockingMetadata); 156 blockingBusinessObjects = addAdditionalBlockingBusinessObjects(blockingBusinessObjects, objectCode); 157 158 if (blockingBusinessObjects != null && !blockingBusinessObjects.isEmpty()) { 159 final List<PersistableBusinessObject> persistingChanges = ((GlobalBusinessObject)maintainable.getBusinessObject()).generateGlobalChangesToPersist(); 160 if (!isOnlyPersistingChangesInBlockingBusinessObjects(blockingBusinessObjects, persistingChanges)) { 161 putInactivationBlockingErrorOnPage(objectCode, inactivationBlockingMetadata, index); 162 return false; 163 } 164 } 165 } 166 return true; 167 } 168 169 protected void putInactivationBlockingErrorOnPage(ObjectCode objectCode, InactivationBlockingMetadata inactivationBlockingMetadata, int index) { 170 String objectCodeSummaryString = objectCode.getUniversityFiscalYear() + " - " + objectCode.getChartOfAccountsCode() + " - " + objectCode.getFinancialObjectCode(); 171 172 Properties parameters = new Properties(); 173 parameters.put(KNSConstants.BUSINESS_OBJECT_CLASS_ATTRIBUTE, inactivationBlockingMetadata.getBlockedBusinessObjectClass().getName()); 174 parameters.put(KNSConstants.DISPATCH_REQUEST_PARAMETER, KNSConstants.METHOD_DISPLAY_ALL_INACTIVATION_BLOCKERS); 175 parameters.put(KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR, objectCode.getUniversityFiscalYear().toString()); 176 parameters.put(KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE, objectCode.getChartOfAccountsCode()); 177 parameters.put(KFSPropertyConstants.FINANCIAL_OBJECT_CODE, objectCode.getFinancialObjectCode()); 178 String blockingUrl = UrlFactory.parameterizeUrl(KNSConstants.DISPLAY_ALL_INACTIVATION_BLOCKERS_ACTION, parameters); 179 180 String errorPropertyPath = KFSConstants.MAINTENANCE_NEW_MAINTAINABLE + KFSPropertyConstants.OBJECT_CODE_GLOBAL_DETAILS + "[" + index + "]." + KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE; 181 182 // post an error about the locked document 183 GlobalVariables.getMessageMap().putErrorWithoutFullErrorPath(errorPropertyPath, KFSKeyConstants.ERROR_DOCUMENT_GLOBAL_OBJECTMAINT_INACTIVATION_BLOCKING, objectCodeSummaryString, blockingUrl); 184 } 185 186 /** 187 * Determines if all of the given blocking business objects are among the persisting changes 188 * @param blockingBusinessObjects the Collection of blocking business objects 189 * @param persistingChanges the List of Object Codes which will be persisted by this document 190 * @return true if all the blocking business objects are persisting changes 191 */ 192 protected boolean isOnlyPersistingChangesInBlockingBusinessObjects(Collection<BusinessObject> blockingBusinessObjects, List<PersistableBusinessObject> persistingChanges) { 193 for (BusinessObject bo : blockingBusinessObjects) { 194 if (bo instanceof ObjectCode) { 195 if (!isObjectCodeInPersistingChanges(persistingChanges, (ObjectCode)bo)) return false; 196 } else { 197 return false; 198 } 199 } 200 return true; 201 } 202 203 /** 204 * Determines if the given object code is within the list of persisting changes 205 * @param persistingChanges the changes to persist 206 * @param objectCode the blocking object code to look for in the persisting changes 207 * @return true if the object code was found in the list of persisting changes, false otherwise 208 */ 209 protected boolean isObjectCodeInPersistingChanges(List<PersistableBusinessObject> persistingChanges, ObjectCode objectCode) { 210 for (PersistableBusinessObject persistingObjectCodeAsObject : persistingChanges) { 211 if (isEqualObjectCode(objectCode, (ObjectCode)persistingObjectCodeAsObject)) return true; 212 } 213 return false; 214 } 215 216 /** 217 * Determines if the two given object codes are roughly equal 218 * @param castor an object code 219 * @param pollux another, though perhaps very similar, object code 220 * @return true if the two object codes share primary key values, false otherwise 221 */ 222 protected boolean isEqualObjectCode(ObjectCode castor, ObjectCode pollux) { 223 return ObjectUtils.nullSafeEquals(castor.getUniversityFiscalYear(), pollux.getUniversityFiscalYear()) && ObjectUtils.nullSafeEquals(castor.getChartOfAccountsCode(), pollux.getChartOfAccountsCode()) && ObjectUtils.nullSafeEquals(castor.getFinancialObjectCode(), pollux.getFinancialObjectCode()); 224 } 225 226 /** 227 * Retrieves any additional blocking objects not handled by the inactivation framework 228 * @param blockingBusinessObjects the current list of blocking business objects 229 * @param objectCode the object code to find additional blocking objects for 230 * @return the perhaps fuller Collection of blocking business objects 231 */ 232 protected Collection<BusinessObject> addAdditionalBlockingBusinessObjects(Collection<BusinessObject> blockingBusinessObjects, ObjectCode objectCode) { 233 List<BusinessObject> additionalBlockingObjects = new ArrayList<BusinessObject>(); 234 retrieveBlockingOffsetDefinitions(objectCode, additionalBlockingObjects); 235 retrieveBlockingIndirectCostRecoveryExclusionAccounts(objectCode, additionalBlockingObjects); 236 if (!additionalBlockingObjects.isEmpty()) { 237 additionalBlockingObjects.addAll(blockingBusinessObjects); 238 return additionalBlockingObjects; 239 } 240 return blockingBusinessObjects; 241 } 242 243 /** 244 * Retrieves all Offset Definitions blocking the given object code and puts them in the List of additional blocking objects 245 * @param objectCode the object code to find additional blocking objects for 246 * @param additionalBlockingObjects the List of additional blocking objects to populate 247 */ 248 protected void retrieveBlockingOffsetDefinitions(ObjectCode objectCode, List<BusinessObject> additionalBlockingObjects) { 249 final BusinessObjectService businessObjectService = SpringContext.getBean(BusinessObjectService.class); 250 251 Map<String, Object> keys = new HashMap<String, Object>(); 252 keys.put("universityFiscalYear", objectCode.getUniversityFiscalYear()); 253 keys.put("chartOfAccountsCode", objectCode.getChartOfAccountsCode()); 254 keys.put("financialObjectCode", objectCode.getFinancialObjectCode()); 255 256 Collection<BusinessObject> offsetDefinitions = (Collection<BusinessObject>)businessObjectService.findMatching(OffsetDefinition.class, keys); 257 if (offsetDefinitions != null && !offsetDefinitions.isEmpty()) { 258 additionalBlockingObjects.addAll(offsetDefinitions); 259 } 260 } 261 262 /** 263 * Retrieves all Indirect Cost Recovery Exclusion by Account records blocking the given object code and puts them in the List of additional blocking objects 264 * @param objectCode the object code to find additional blocking objects for 265 * @param additionalBlockingObjects the List of additional blocking objects to populate 266 */ 267 protected void retrieveBlockingIndirectCostRecoveryExclusionAccounts(ObjectCode objectCode, List<BusinessObject> additionalBlockingObjects) { 268 final UniversityDateService universityDateService = SpringContext.getBean(UniversityDateService.class); 269 if (objectCode.getUniversityFiscalYear() != null && objectCode.getUniversityFiscalYear().equals(universityDateService.getCurrentFiscalYear())) { 270 final BusinessObjectService businessObjectService = SpringContext.getBean(BusinessObjectService.class); 271 272 Map<String, Object> keys = new HashMap<String, Object>(); 273 keys.put("financialObjectChartOfAccountCode", objectCode.getChartOfAccountsCode()); 274 keys.put("financialObjectCode", objectCode.getFinancialObjectCode()); 275 276 Collection<BusinessObject> icrExclusionAccounts = (Collection<BusinessObject>)businessObjectService.findMatching(IndirectCostRecoveryExclusionAccount.class, keys); 277 if (icrExclusionAccounts != null && !icrExclusionAccounts.isEmpty()) { 278 additionalBlockingObjects.addAll(icrExclusionAccounts); 279 } 280 } 281 } 282 283 /** 284 * This performs rules checks on document save 285 * <ul> 286 * <li>{@link ObjectCodeGlobalRule#checkSimpleRulesAllLines()}</li> 287 * </ul> 288 * This rule does not fail on business rule failures 289 * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomSaveDocumentBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument) 290 */ 291 @Override 292 protected boolean processCustomSaveDocumentBusinessRules(MaintenanceDocument document) { 293 setupConvenienceObjects(); 294 // check simple rules 295 checkSimpleRulesAllLines(); 296 297 return true; 298 } 299 300 /** 301 * This method checks to make sure that each new {@link ObjectCodeGlobalDetail} has: 302 * <ul> 303 * <li>valid chart of accounts code</li> 304 * <li>valid fiscal year</li> 305 * <li>unique identifiers (not currently implemented)</li> 306 * </ul> 307 * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomAddCollectionLineBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument, java.lang.String, org.kuali.rice.kns.bo.PersistableBusinessObject) 308 */ 309 @Override 310 public boolean processCustomAddCollectionLineBusinessRules(MaintenanceDocument document, String collectionName, PersistableBusinessObject bo) { 311 boolean success = true; 312 if (bo instanceof ObjectCodeGlobalDetail) { 313 ObjectCodeGlobalDetail detail = (ObjectCodeGlobalDetail) bo; 314 if (!checkEmptyValue(detail.getChartOfAccountsCode())) { 315 // put an error about chart code 316 GlobalVariables.getMessageMap().putError("chartOfAccountsCode", KFSKeyConstants.ERROR_REQUIRED, "Chart of Accounts Code"); 317 success &= false; 318 } 319 if (!checkEmptyValue(detail.getUniversityFiscalYear())) { 320 // put an error about fiscal year 321 GlobalVariables.getMessageMap().putError("universityFiscalYear", KFSKeyConstants.ERROR_REQUIRED, "University Fiscal Year"); 322 success &= false; 323 } 324 if (!checkUniqueIdentifiers(detail)) { 325 // TODO: put an error about unique identifier fields must not exist more than once. 326 success &= false; 327 } 328 // both keys are present and satisfy the unique identifiers requirement, go ahead and proces the rest of the rules 329 if (success) { 330 success &= checkObjectCodeDetails(detail); 331 } 332 333 } 334 return success; 335 } 336 337 /** 338 * 339 * This method (will)put an error about unique identifier fields must not exist more than once. 340 * @param dtl 341 * @return true (not currently implemented fully) 342 */ 343 protected boolean checkUniqueIdentifiers(ObjectCodeGlobalDetail dtl) { 344 boolean success = true; 345 return success; 346 347 } 348 349 /** 350 * 351 * This checks the following conditions: 352 * <ul> 353 * <li>{@link ObjectCodeGlobalRule#checkObjectLevelCode(ObjectCodeGlobal, ObjectCodeGlobalDetail, int, boolean)} </li> 354 * <li>{@link ObjectCodeGlobalRule#checkNextYearObjectCode(ObjectCodeGlobal, ObjectCodeGlobalDetail, int, boolean)} </li> 355 * <li>{@link ObjectCodeGlobalRule#checkReportsToObjectCode(ObjectCodeGlobal, ObjectCodeGlobalDetail, int, boolean)}</li> 356 * </ul> 357 * @param dtl 358 * @return true if sub-rules succeed 359 */ 360 public boolean checkObjectCodeDetails(ObjectCodeGlobalDetail dtl) { 361 boolean success = true; 362 int originalErrorCount = GlobalVariables.getMessageMap().getErrorCount(); 363 getDictionaryValidationService().validateBusinessObject(dtl); 364 dtl.refreshNonUpdateableReferences(); 365 // here is where we need our checks for level code nd next year object code 366 success &= checkObjectLevelCode(objectCodeGlobal, dtl, 0, true); 367 success &= checkNextYearObjectCode(objectCodeGlobal, dtl, 0, true); 368 success &= checkReportsToObjectCode(objectCodeGlobal, dtl, 0, true); 369 success &= GlobalVariables.getMessageMap().getErrorCount() == originalErrorCount; 370 371 return success; 372 } 373 374 /** 375 * This method checks that the reports to object code input on the top level of the global document is valid for a given chart's 376 * reportToChart in the detail section 377 * 378 * @param dtl 379 * @return true if the reports to object is valid for the given reports to chart 380 */ 381 protected boolean checkReportsToObjectCode(ObjectCodeGlobal objectCodeGlobal, ObjectCodeGlobalDetail dtl, int lineNum, boolean add) { 382 boolean success = true; 383 String errorPath = KFSConstants.EMPTY_STRING; 384 if (checkEmptyValue(objectCodeGlobal.getReportsToFinancialObjectCode())) { 385 // objectCodeGlobal.refreshReferenceObject("reportsToFinancialObject"); 386 String reportsToObjectCode = objectCodeGlobal.getReportsToFinancialObjectCode(); 387 String reportsToChartCode = dtl.getChartOfAccounts().getReportsToChartOfAccountsCode(); 388 Integer fiscalYear = dtl.getUniversityFiscalYear(); 389 390 // verify that this combination exists in the db 391 ObjectCode objCode = objectCodeService.getByPrimaryId(fiscalYear, reportsToChartCode, reportsToObjectCode); 392 if (ObjectUtils.isNull(objCode)) { 393 success &= false; 394 String[] errorParameters = { reportsToObjectCode, reportsToChartCode, fiscalYear.toString() }; 395 if (add) { 396 errorPath = KFSConstants.MAINTENANCE_ADD_PREFIX + KFSPropertyConstants.OBJECT_CODE_GLOBAL_DETAILS + "." + "chartOfAccountsCode"; 397 putFieldError(errorPath, KFSKeyConstants.ERROR_DOCUMENT_GLOBAL_OBJECTMAINT_INVALID_RPTS_TO_OBJ_CODE, errorParameters); 398 } 399 else { 400 errorPath = KFSPropertyConstants.OBJECT_CODE_GLOBAL_DETAILS + "[" + lineNum + "]." + "chartOfAccountsCode"; 401 putFieldError(errorPath, KFSKeyConstants.ERROR_DOCUMENT_GLOBAL_OBJECTMAINT_INVALID_RPTS_TO_OBJ_CODE, errorParameters); 402 } 403 } 404 return success; 405 406 } 407 else { 408 GlobalVariables.getMessageMap().putError("reportsToFinancialObjectCode", KFSKeyConstants.ERROR_REQUIRED, "Reports to Object Code"); 409 success &= false; 410 } 411 412 return success; 413 } 414 415 416 /** 417 * This method checks that the next year object code specified in the change document is a valid object code for a given chart 418 * and year 419 * 420 * @param dtl 421 * @return false if this object code doesn't exist in the next fiscal year 422 */ 423 protected boolean checkNextYearObjectCode(ObjectCodeGlobal objectCodeGlobal, ObjectCodeGlobalDetail dtl, int lineNum, boolean add) { 424 boolean success = true; 425 String errorPath = KFSConstants.EMPTY_STRING; 426 // first check to see if the Next Year object code was filled in 427 if (checkEmptyValue(objectCodeGlobal.getNextYearFinancialObjectCode())) { 428 // then this value must also exist as a regular financial object code currently 429 ObjectCode objCode = objectCodeService.getByPrimaryId(dtl.getUniversityFiscalYear(), dtl.getChartOfAccountsCode(), objectCodeGlobal.getNextYearFinancialObjectCode()); 430 if (ObjectUtils.isNull(objCode)) { 431 success &= false; 432 String[] errorParameters = { objectCodeGlobal.getNextYearFinancialObjectCode(), dtl.getChartOfAccountsCode(), dtl.getUniversityFiscalYear().toString() }; 433 if (add) { 434 errorPath = KFSConstants.MAINTENANCE_ADD_PREFIX + KFSPropertyConstants.OBJECT_CODE_GLOBAL_DETAILS + "." + "chartOfAccountsCode"; 435 putFieldError(errorPath, KFSKeyConstants.ERROR_DOCUMENT_GLOBAL_OBJECTMAINT_INVALID_NEXT_YEAR_OBJ_CODE, errorParameters); 436 } 437 else { 438 errorPath = KFSPropertyConstants.OBJECT_CODE_GLOBAL_DETAILS + "[" + lineNum + "]." + "chartOfAccountsCode"; 439 putFieldError(errorPath, KFSKeyConstants.ERROR_DOCUMENT_GLOBAL_OBJECTMAINT_INVALID_NEXT_YEAR_OBJ_CODE, errorParameters); 440 } 441 } 442 return success; 443 } 444 445 return success; 446 } 447 448 /** 449 * This method checks that the object level code from the object code change document actually exists for the chart object 450 * specified in the detail 451 * 452 * @param dtl 453 * @return false if object level doesn't exist for the chart, and level code filled in 454 */ 455 protected boolean checkObjectLevelCode(ObjectCodeGlobal objectCodeGlobal, ObjectCodeGlobalDetail dtl, int lineNum, boolean add) { 456 boolean success = true; 457 String errorPath = KFSConstants.EMPTY_STRING; 458 // first check to see if the level code is filled in 459 if (checkEmptyValue(objectCodeGlobal.getFinancialObjectLevelCode())) { 460 ObjectLevel objLevel = objectLevelService.getByPrimaryId(dtl.getChartOfAccountsCode(), objectCodeGlobal.getFinancialObjectLevelCode()); 461 if (ObjectUtils.isNull(objLevel)) { 462 success &= false; 463 String[] errorParameters = { objectCodeGlobal.getFinancialObjectLevelCode(), dtl.getChartOfAccountsCode() }; 464 if (add) { 465 errorPath = KFSConstants.MAINTENANCE_ADD_PREFIX + KFSPropertyConstants.OBJECT_CODE_GLOBAL_DETAILS + "." + "chartOfAccountsCode"; 466 putFieldError(errorPath, KFSKeyConstants.ERROR_DOCUMENT_GLOBAL_OBJECTMAINT_INVALID_OBJ_LEVEL, errorParameters); 467 } 468 else { 469 errorPath = KFSPropertyConstants.OBJECT_CODE_GLOBAL_DETAILS + "[" + lineNum + "]." + "chartOfAccountsCode"; 470 putFieldError(errorPath, KFSKeyConstants.ERROR_DOCUMENT_GLOBAL_OBJECTMAINT_INVALID_OBJ_LEVEL, errorParameters); 471 } 472 } 473 return success; 474 475 } 476 else { 477 GlobalVariables.getMessageMap().putError("financialObjectLevelCode", KFSKeyConstants.ERROR_REQUIRED, "Object Level Code"); 478 success &= false; 479 } 480 return success; 481 } 482 483 /** 484 * This method checks the simple rules for all lines at once and gets called on save, submit, etc. but not on add 485 * 486 * <ul> 487 * <li>{@link ObjectCodeGlobalRule#checkFiscalYearAllLines(ObjectCodeGlobal)} </li> 488 * <li>{@link ObjectCodeGlobalRule#checkChartAllLines(ObjectCodeGlobal)} </li> 489 * <li>{@link ObjectCodeGlobalRule#checkObjectLevelCodeAllLines(ObjectCodeGlobal)} </li> 490 * <li>{@link ObjectCodeGlobalRule#checkNextYearObjectCodeAllLines(ObjectCodeGlobal)} </li> 491 * <li>{@link ObjectCodeGlobalRule#checkReportsToObjectCodeAllLines(ObjectCodeGlobal)} </li> 492 * </ul> 493 * @return 494 */ 495 protected boolean checkSimpleRulesAllLines() { 496 boolean success = true; 497 // check if there are any object codes and accounts, if either fails this should fail 498 if (!checkForObjectCodeGlobalDetails(objectCodeGlobal.getObjectCodeGlobalDetails())) { 499 success = false; 500 } 501 else { 502 // check object codes 503 success &= checkFiscalYearAllLines(objectCodeGlobal); 504 505 // check chart code 506 success &= checkChartAllLines(objectCodeGlobal); 507 508 // check object level code 509 success &= checkObjectLevelCodeAllLines(objectCodeGlobal); 510 511 // check next year object code 512 success &= checkNextYearObjectCodeAllLines(objectCodeGlobal); 513 514 // check reports to object code 515 success &= checkReportsToObjectCodeAllLines(objectCodeGlobal); 516 517 } 518 return success; 519 } 520 521 /** 522 * 523 * This checks to make sure that there is at least one {@link ObjectCodeGlobalDetail} in the collection 524 * @param objectCodeGlobalDetails 525 * @return false if the collection is empty or null 526 */ 527 protected boolean checkForObjectCodeGlobalDetails(List<ObjectCodeGlobalDetail> objectCodeGlobalDetails) { 528 if (objectCodeGlobalDetails == null || objectCodeGlobalDetails.size() == 0) { 529 putFieldError(KFSConstants.MAINTENANCE_ADD_PREFIX + KFSPropertyConstants.OBJECT_CODE_GLOBAL_DETAILS + "." + KFSPropertyConstants.FINANCIAL_DOCUMENT_TYPE_CODE, KFSKeyConstants.ERROR_DOCUMENT_GLOBAL_OBJECTMAINT_NO_CHART_FISCAL_YEAR); 530 return false; 531 } 532 return true; 533 } 534 535 /** 536 * 537 * This method calls {@link ObjectCodeGlobalRule#checkFiscalYear(ObjectCodeGlobal, ObjectCodeGlobalDetail, int, boolean)} on each detail object 538 * @param objectCodeGlobal 539 * @return true if all lines pass 540 */ 541 protected boolean checkFiscalYearAllLines(ObjectCodeGlobal objectCodeGlobal) { 542 boolean success = true; 543 int i = 0; 544 for (ObjectCodeGlobalDetail objectCodeGlobalDetail : objectCodeGlobal.getObjectCodeGlobalDetails()) { 545 546 // check fiscal year first 547 success &= checkFiscalYear(objectCodeGlobal, objectCodeGlobalDetail, i, false); 548 549 // increment counter for sub object changes list 550 i++; 551 } 552 553 return success; 554 } 555 556 /** 557 * 558 * This method calls {@link ObjectCodeGlobalRule#checkChartOnObjCodeDetails(ObjectCodeGlobal, ObjectCodeGlobalDetail, int, boolean)} on each detail object 559 * 560 * @param ocChangeDocument 561 * @return true if all lines pass 562 */ 563 protected boolean checkChartAllLines(ObjectCodeGlobal ocChangeDocument) { 564 boolean success = true; 565 int i = 0; 566 for (ObjectCodeGlobalDetail objectCodeGlobalDetail : ocChangeDocument.getObjectCodeGlobalDetails()) { 567 568 // check chart 569 success &= checkChartOnObjCodeDetails(ocChangeDocument, objectCodeGlobalDetail, i, false); 570 // increment counter for sub object changes list 571 i++; 572 } 573 574 return success; 575 } 576 577 578 /** 579 * 580 * This method calls {@link ObjectCodeGlobalRule#checkReportsToObjectCode(ObjectCodeGlobal, ObjectCodeGlobalDetail, int, boolean)} on each detail object 581 * 582 * @param objectCodeGlobalDocument2 583 * @return true if all lines pass 584 */ 585 protected boolean checkReportsToObjectCodeAllLines(ObjectCodeGlobal objectCodeGlobalDocument2) { 586 boolean success = true; 587 int i = 0; 588 for (ObjectCodeGlobalDetail objectCodeGlobalDetail : objectCodeGlobal.getObjectCodeGlobalDetails()) { 589 590 // check fiscal year first 591 success &= checkReportsToObjectCode(objectCodeGlobal, objectCodeGlobalDetail, i, false); 592 593 // increment counter for sub object changes list 594 i++; 595 } 596 597 return success; 598 } 599 600 /** 601 * 602 * This method calls {@link ObjectCodeGlobalRule#checkNextYearObjectCode(ObjectCodeGlobal, ObjectCodeGlobalDetail, int, boolean)} on each detail object 603 * 604 * @param objectCodeGlobalDocument2 605 * @return true if all lines pass 606 */ 607 protected boolean checkNextYearObjectCodeAllLines(ObjectCodeGlobal objectCodeGlobalDocument2) { 608 boolean success = true; 609 int i = 0; 610 for (ObjectCodeGlobalDetail objectCodeGlobalDetail : objectCodeGlobal.getObjectCodeGlobalDetails()) { 611 612 // check fiscal year first 613 success &= checkNextYearObjectCode(objectCodeGlobal, objectCodeGlobalDetail, i, false); 614 615 // increment counter for sub object changes list 616 i++; 617 } 618 619 return success; 620 } 621 622 /** 623 * 624 * This method calls {@link ObjectCodeGlobalRule#checkObjectLevelCode(ObjectCodeGlobal, ObjectCodeGlobalDetail, int, boolean)} on each detail object 625 * 626 * @param objectCodeGlobalDocument2 627 * @return true if all lines pass 628 */ 629 protected boolean checkObjectLevelCodeAllLines(ObjectCodeGlobal objectCodeGlobalDocument2) { 630 boolean success = true; 631 int i = 0; 632 for (ObjectCodeGlobalDetail objectCodeGlobalDetail : objectCodeGlobal.getObjectCodeGlobalDetails()) { 633 634 // check fiscal year first 635 success &= checkObjectLevelCode(objectCodeGlobal, objectCodeGlobalDetail, i, false); 636 637 // increment counter for sub object changes list 638 i++; 639 } 640 641 return success; 642 } 643 644 /** 645 * 646 * This checks to make sure that the fiscal year has been entered 647 * @param objectCodeGlobal 648 * @param objectCodeGlobalDetail 649 * @param lineNum 650 * @param add 651 * @return false if no fiscal year value 652 */ 653 protected boolean checkFiscalYear(ObjectCodeGlobal objectCodeGlobal, ObjectCodeGlobalDetail objectCodeGlobalDetail, int lineNum, boolean add) { 654 boolean success = true; 655 String errorPath = KFSConstants.EMPTY_STRING; 656 // first must have an actual fiscal year 657 if (objectCodeGlobalDetail.getUniversityFiscalYear() == null) { 658 if (add) { 659 errorPath = KFSConstants.MAINTENANCE_ADD_PREFIX + KFSPropertyConstants.OBJECT_CODE_GLOBAL_DETAILS + "." + "universityFiscalYear"; 660 putFieldError(errorPath, KFSKeyConstants.ERROR_DOCUMENT_GLOBAL_OBJECTMAINT_FISCAL_YEAR_MUST_EXIST); 661 } 662 else { 663 errorPath = KFSPropertyConstants.OBJECT_CODE_GLOBAL_DETAILS + "[" + lineNum + "]." + "universityFiscalYear"; 664 putFieldError(errorPath, KFSKeyConstants.ERROR_DOCUMENT_GLOBAL_OBJECTMAINT_FISCAL_YEAR_MUST_EXIST); 665 } 666 success &= false; 667 return success; 668 } 669 670 return success; 671 } 672 673 /** 674 * 675 * This checks to make sure that the chart of accounts for the detail object has been filled in 676 * @param objectCodeGlobal 677 * @param objectCodeGlobalDetail 678 * @param lineNum 679 * @param add 680 * @return false if chart of accounts code null 681 */ 682 protected boolean checkChartOnObjCodeDetails(ObjectCodeGlobal objectCodeGlobal, ObjectCodeGlobalDetail objectCodeGlobalDetail, int lineNum, boolean add) { 683 boolean success = true; 684 String errorPath = KFSConstants.EMPTY_STRING; 685 // first must have an actual fiscal year 686 if (objectCodeGlobalDetail.getChartOfAccounts() == null) { 687 if (add) { 688 errorPath = KFSConstants.MAINTENANCE_ADD_PREFIX + KFSPropertyConstants.OBJECT_CODE_GLOBAL_DETAILS + "." + "chartOfAccountsCode"; 689 putFieldError(errorPath, KFSKeyConstants.ERROR_DOCUMENT_GLOBAL_OBJECTMAINT_CHART_MUST_EXIST); 690 } 691 else { 692 errorPath = KFSPropertyConstants.OBJECT_CODE_GLOBAL_DETAILS + "[" + lineNum + "]." + "chartOfAccountsCode"; 693 putFieldError(errorPath, KFSKeyConstants.ERROR_DOCUMENT_GLOBAL_OBJECTMAINT_CHART_MUST_EXIST); 694 } 695 success &= false; 696 return success; 697 } 698 699 return success; 700 } 701 702 protected void setObjectCodeService(ObjectCodeService objectCodeService) { 703 this.objectCodeService = objectCodeService; 704 705 } 706 707 708 protected void setObjectLevelService(ObjectLevelService objectLevelService) { 709 this.objectLevelService = objectLevelService; 710 711 } 712 }