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