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.module.ld.batch.service.impl; 017 018 import java.util.ArrayList; 019 import java.util.Calendar; 020 import java.util.List; 021 import java.util.Map; 022 023 import org.apache.commons.lang.StringUtils; 024 import org.kuali.kfs.coa.businessobject.Account; 025 import org.kuali.kfs.coa.businessobject.AccountingPeriod; 026 import org.kuali.kfs.coa.businessobject.SubAccount; 027 import org.kuali.kfs.coa.service.AccountService; 028 import org.kuali.kfs.coa.service.BalanceTypeService; 029 import org.kuali.kfs.gl.batch.ScrubberStep; 030 import org.kuali.kfs.gl.batch.service.AccountingCycleCachingService; 031 import org.kuali.kfs.gl.businessobject.OriginEntryInformation; 032 import org.kuali.kfs.gl.businessobject.OriginEntryFull; 033 import org.kuali.kfs.gl.service.ScrubberValidator; 034 import org.kuali.kfs.module.ld.LaborConstants; 035 import org.kuali.kfs.module.ld.LaborKeyConstants; 036 import org.kuali.kfs.module.ld.batch.LaborScrubberStep; 037 import org.kuali.kfs.module.ld.batch.service.LaborAccountingCycleCachingService; 038 import org.kuali.kfs.module.ld.businessobject.LaborObject; 039 import org.kuali.kfs.module.ld.businessobject.LaborOriginEntry; 040 import org.kuali.kfs.sys.KFSConstants; 041 import org.kuali.kfs.sys.KFSKeyConstants; 042 import org.kuali.kfs.sys.KFSPropertyConstants; 043 import org.kuali.kfs.sys.Message; 044 import org.kuali.kfs.sys.MessageBuilder; 045 import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntry; 046 import org.kuali.kfs.sys.businessobject.SystemOptions; 047 import org.kuali.kfs.sys.businessobject.UniversityDate; 048 import org.kuali.kfs.sys.service.OptionsService; 049 import org.kuali.kfs.sys.service.impl.KfsParameterConstants; 050 import org.kuali.rice.kns.service.BusinessObjectService; 051 import org.kuali.rice.kns.service.KualiConfigurationService; 052 import org.kuali.rice.kns.service.ParameterService; 053 import org.kuali.rice.kns.service.PersistenceService; 054 import org.kuali.rice.kns.service.PersistenceStructureService; 055 import org.kuali.rice.kns.util.ObjectUtils; 056 057 /** 058 * Service implementation of ScrubberValidator. 059 */ 060 public class ScrubberValidatorImpl implements ScrubberValidator { 061 private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(ScrubberValidatorImpl.class); 062 063 private KualiConfigurationService kualiConfigurationService; 064 private BusinessObjectService businessObjectService; 065 private ParameterService parameterService; 066 private AccountService accountService; 067 private BalanceTypeService balanceTypService; 068 private OptionsService optionsService; 069 070 private PersistenceService persistenceService; 071 private ScrubberValidator scrubberValidator; 072 private PersistenceStructureService persistenceStructureService; 073 private boolean continuationAccountIndicator; 074 075 076 /** 077 * @see org.kuali.module.labor.service.LaborScrubberValidator#validateTransaction(owrg.kuali.module.labor.bo.LaborOriginEntry, 078 * org.kuali.kfs.module.ld.businessobject.LaborOriginEntry, org.kuali.kfs.gl.businessobject.UniversityDate) 079 */ 080 public List<Message> validateTransaction(OriginEntryInformation originEntry, OriginEntryInformation scrubbedEntry, UniversityDate universityRunDate, boolean laborIndicator, AccountingCycleCachingService laborAccountingCycleCachingService) { 081 LOG.debug("validateTransaction() started"); 082 List<Message> errors = new ArrayList<Message>(); 083 continuationAccountIndicator = false; 084 085 LaborOriginEntry laborOriginEntry = (LaborOriginEntry) originEntry; 086 LaborOriginEntry laborScrubbedEntry = (LaborOriginEntry) scrubbedEntry; 087 088 // gl scrubber validation 089 errors = scrubberValidator.validateTransaction(laborOriginEntry, laborScrubbedEntry, universityRunDate, laborIndicator, laborAccountingCycleCachingService); 090 091 refreshOriginEntryReferences(laborOriginEntry); 092 refreshOriginEntryReferences(laborScrubbedEntry); 093 094 if (StringUtils.isBlank(laborOriginEntry.getEmplid())) { 095 laborScrubbedEntry.setEmplid(LaborConstants.getDashEmplId()); 096 } 097 098 if (StringUtils.isBlank(laborOriginEntry.getPositionNumber())) { 099 laborScrubbedEntry.setPositionNumber(LaborConstants.getDashPositionNumber()); 100 } 101 102 Message err = null; 103 104 //this validation is duplicated. This is in ScrubberValidatorImpl under GL 105 // err = this.validateClosedPeriodCode(laborOriginEntry, laborScrubbedEntry); 106 // if (err != null) { 107 // errors.add(err); 108 // } 109 110 err = validatePayrollEndFiscalYear(laborOriginEntry, laborScrubbedEntry, universityRunDate, (LaborAccountingCycleCachingService) laborAccountingCycleCachingService); 111 if (err != null) { 112 errors.add(err); 113 } 114 115 err = validatePayrollEndFiscalPeriodCode(laborOriginEntry, laborScrubbedEntry, universityRunDate, (LaborAccountingCycleCachingService) laborAccountingCycleCachingService); 116 if (err != null) { 117 errors.add(err); 118 } 119 120 err = validateAccount(laborOriginEntry, laborScrubbedEntry, universityRunDate, (LaborAccountingCycleCachingService) laborAccountingCycleCachingService); 121 if (err != null) { 122 errors.add(err); 123 } 124 125 err = validateSubAccount(laborOriginEntry, laborScrubbedEntry, (LaborAccountingCycleCachingService) laborAccountingCycleCachingService); 126 if (err != null) { 127 errors.add(err); 128 } 129 130 return errors; 131 } 132 133 /** 134 * This method is for refreshing References of Origin Entry 135 */ 136 protected void refreshOriginEntryReferences(OriginEntryFull originEntry) { 137 Map<String, Class> referenceClasses = persistenceStructureService.listReferenceObjectFields(originEntry.getClass()); 138 for (String reference : referenceClasses.keySet()) { 139 if (KFSPropertyConstants.PROJECT.equals(reference)) { 140 if (KFSConstants.getDashProjectCode().equals(originEntry.getProjectCode())) { 141 originEntry.setProject(null); 142 } 143 else { 144 persistenceService.retrieveReferenceObject(originEntry, reference); 145 } 146 } 147 else if (KFSPropertyConstants.FINANCIAL_SUB_OBJECT.equals(reference)) { 148 if (KFSConstants.getDashFinancialSubObjectCode().equals(originEntry.getFinancialSubObjectCode())) { 149 originEntry.setFinancialSubObject(null); 150 } 151 else { 152 persistenceService.retrieveReferenceObject(originEntry, reference); 153 } 154 } 155 else if (KFSPropertyConstants.SUB_ACCOUNT.equals(reference)) { 156 if (KFSConstants.getDashSubAccountNumber().equals(originEntry.getSubAccountNumber())) { 157 originEntry.setSubAccount(null); 158 } 159 else { 160 persistenceService.retrieveReferenceObject(originEntry, reference); 161 } 162 } 163 else { 164 persistenceService.retrieveReferenceObject(originEntry, reference); 165 } 166 } 167 } 168 169 /** 170 * Validates the closed period code of the origin entry. Scrubber accepts closed fiscal periods for the specified balance type. 171 * 172 * @param originEntry the origin entry being scrubbed 173 * @param workingEntry the scrubbed version of the origin entry 174 * @return a Message if an error was encountered, otherwise null 175 */ 176 //this validation is duplicated. This is in ScrubberValidatorImpl under GL 177 // protected Message validateClosedPeriodCode(LaborOriginEntry laborOriginEntry, LaborOriginEntry laborWorkingEntry) { 178 // LOG.debug("validateClosedPeriodCode() started"); 179 // 180 // String periodCode = laborOriginEntry.getUniversityFiscalPeriodCode(); 181 // if (StringUtils.isBlank(periodCode)) { 182 // return null; 183 // } 184 // 185 // // Scrubber accepts closed fiscal periods for A21 Balance 186 // AccountingPeriod accountingPeriod = referenceLookup.get().getAccountingPeriod(laborOriginEntry); 187 // if (ObjectUtils.isNotNull(accountingPeriod) && !accountingPeriod.isActive()) { 188 // String bypassBalanceType = parameterService.getParameterValue(LaborScrubberStep.class, LaborConstants.Scrubber.CLOSED_FISCAL_PERIOD_BYPASS_BALANCE_TYPES); 189 // 190 // if (!laborWorkingEntry.getFinancialBalanceTypeCode().equals(bypassBalanceType)) { 191 // return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_FISCAL_PERIOD_CLOSED, periodCode, Message.TYPE_FATAL); 192 // } 193 // 194 // laborWorkingEntry.setUniversityFiscalPeriodCode(periodCode); 195 // } 196 // 197 // return null; 198 // } 199 200 /** 201 * This method is for validation of payrollEndFiscalYear 202 */ 203 protected Message validatePayrollEndFiscalYear(LaborOriginEntry laborOriginEntry, LaborOriginEntry laborWorkingEntry, UniversityDate universityRunDate, LaborAccountingCycleCachingService laborAccountingCycleCachingService) { 204 LOG.debug("validatePayrollEndFiscalYear() started"); 205 SystemOptions scrubbedEntryOption = null; 206 if (laborOriginEntry.getPayrollEndDateFiscalYear() != null){ 207 scrubbedEntryOption = laborAccountingCycleCachingService.getSystemOptions(laborOriginEntry.getPayrollEndDateFiscalYear()); 208 209 if (scrubbedEntryOption == null) { 210 return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_PAYROLL_END_DATE_FISCAL_YEAR, "" + laborOriginEntry.getPayrollEndDateFiscalYear(), Message.TYPE_FATAL); 211 } 212 213 } 214 215 return null; 216 } 217 218 /** 219 * This method is for validation of PayrollEndFiscalPeriodCode 220 */ 221 protected Message validatePayrollEndFiscalPeriodCode(LaborOriginEntry laborOriginEntry, LaborOriginEntry laborWorkingEntry, UniversityDate universityRunDate, LaborAccountingCycleCachingService laborAccountingCycleCachingService) { 222 LOG.debug("validateUniversityFiscalPeriodCode() started"); 223 224 AccountingPeriod accountingPeriod = null; 225 Integer tempPayrollFiscalYear = 0; 226 if (laborOriginEntry.getPayrollEndDateFiscalYear()== null ){ 227 tempPayrollFiscalYear = universityRunDate.getUniversityFiscalYear(); 228 } else { 229 tempPayrollFiscalYear = laborOriginEntry.getPayrollEndDateFiscalYear(); 230 } 231 232 if (!laborOriginEntry.getPayrollEndDateFiscalPeriodCode().equals("") ){ 233 accountingPeriod = laborAccountingCycleCachingService.getAccountingPeriod(tempPayrollFiscalYear, laborOriginEntry.getPayrollEndDateFiscalPeriodCode()); 234 if (accountingPeriod == null) { 235 return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_PAYROLL_END_DATE_FISCAL_PERIOD, laborOriginEntry.getPayrollEndDateFiscalPeriodCode(), Message.TYPE_FATAL); 236 } 237 } 238 239 240 return null; 241 } 242 243 /** 244 * Performs Account Validation. 245 */ 246 protected Message validateAccount(LaborOriginEntry laborOriginEntry, LaborOriginEntry laborWorkingEntry, UniversityDate universityRunDate, LaborAccountingCycleCachingService laborAccountingCycleCachingService) { 247 LOG.debug("validateAccount() started"); 248 249 Account account = laborOriginEntry.getAccount(); 250 boolean suspenseAccountLogicInd = parameterService.getIndicatorParameter(LaborScrubberStep.class, LaborConstants.Scrubber.SUSPENSE_ACCOUNT_LOGIC_PARAMETER); 251 if (ObjectUtils.isNull(account)) { 252 if (suspenseAccountLogicInd) { 253 return useSuspenseAccount(laborWorkingEntry); 254 } 255 return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_ACCOUNT_NOT_FOUND, laborOriginEntry.getChartOfAccountsCode() + "-" + laborOriginEntry.getAccountNumber(), Message.TYPE_FATAL); 256 } 257 258 // default 259 laborWorkingEntry.setAccount(account); 260 laborWorkingEntry.setChartOfAccountsCode(account.getChartOfAccountsCode()); 261 laborWorkingEntry.setAccountNumber(account.getAccountNumber()); 262 263 // no further validation for gl annual doc type 264 String glAnnualClosingType = parameterService.getParameterValue(KfsParameterConstants.GENERAL_LEDGER_BATCH.class, KFSConstants.SystemGroupParameterNames.GL_ANNUAL_CLOSING_DOC_TYPE); 265 if (glAnnualClosingType.equals(laborOriginEntry.getFinancialDocumentTypeCode())) { 266 return null; 267 } 268 269 // Sub-Fund Wage Exclusion 270 String orginationCode = laborOriginEntry.getFinancialSystemOriginationCode(); 271 List<String> nonWageSubfundBypassOriginationCodes = parameterService.getParameterValues(LaborScrubberStep.class, LaborConstants.Scrubber.NON_WAGE_SUB_FUND_BYPASS_ORIGINATIONS); 272 boolean subfundWageExclusionInd = parameterService.getIndicatorParameter(LaborScrubberStep.class, LaborConstants.Scrubber.SUBFUND_WAGE_EXCLUSION_PARAMETER); 273 274 if (subfundWageExclusionInd && !account.getSubFundGroup().isSubFundGroupWagesIndicator() && !nonWageSubfundBypassOriginationCodes.contains(orginationCode)) { 275 if (suspenseAccountLogicInd) { 276 return useSuspenseAccount(laborWorkingEntry); 277 } 278 279 return MessageBuilder.buildMessage(LaborKeyConstants.ERROR_SUN_FUND_NOT_ACCEPT_WAGES, Message.TYPE_FATAL); 280 } 281 282 // Account Fringe Validation 283 List<String> nonFringeAccountBypassOriginationCodes = parameterService.getParameterValues(LaborScrubberStep.class, LaborConstants.Scrubber.NON_FRINGE_ACCOUNT_BYPASS_ORIGINATIONS); 284 boolean accountFringeExclusionInd = parameterService.getIndicatorParameter(LaborScrubberStep.class, LaborConstants.Scrubber.ACCOUNT_FRINGE_EXCLUSION_PARAMETER); 285 286 if (accountFringeExclusionInd && !nonFringeAccountBypassOriginationCodes.contains(orginationCode)) { 287 return checkAccountFringeIndicator(laborOriginEntry, laborWorkingEntry, account, universityRunDate, laborAccountingCycleCachingService); 288 } 289 290 // Expired/Closed Validation 291 return handleExpiredClosedAccount(laborOriginEntry.getAccount(), laborOriginEntry, laborWorkingEntry, universityRunDate); 292 } 293 294 /** 295 * Checks the continuation account system indicator. If on checks whether the account is expired or closed, and if so calls the 296 * contination logic. 297 */ 298 protected Message handleExpiredClosedAccount(Account account, LaborOriginEntry laborOriginEntry, LaborOriginEntry laborWorkingEntry, UniversityDate universityRunDate) { 299 List<String> continuationAccountBypassBalanceTypeCodes = balanceTypService.getContinuationAccountBypassBalanceTypeCodes(universityRunDate.getUniversityFiscalYear()); 300 List<String> continuationAccountBypassOriginationCodes = parameterService.getParameterValues(LaborScrubberStep.class, LaborConstants.Scrubber.CONTINUATION_ACCOUNT_BYPASS_ORIGINATION_CODES); 301 List<String> continuationAccountBypassDocumentTypeCodes = parameterService.getParameterValues(LaborScrubberStep.class, LaborConstants.Scrubber.CONTINUATION_ACCOUNT_BYPASS_DOCUMENT_TYPE_CODES); 302 303 Calendar today = Calendar.getInstance(); 304 today.setTime(universityRunDate.getUniversityDate()); 305 306 long offsetAccountExpirationTime = getAdjustedAccountExpirationDate(account); 307 boolean isAccountExpiredOrClosed = (account.getAccountExpirationDate() != null && isAccountExpired(account, universityRunDate)) || !account.isActive(); 308 boolean continuationAccountLogicInd = parameterService.getIndicatorParameter(LaborScrubberStep.class, LaborConstants.Scrubber.CONTINUATION_ACCOUNT_LOGIC_PARAMETER); 309 310 if (continuationAccountLogicInd && isAccountExpiredOrClosed) { 311 // special checks for origination codes that have override ability 312 boolean isOverrideOriginCode = continuationAccountBypassOriginationCodes.contains(laborOriginEntry.getFinancialSystemOriginationCode()); 313 if (isOverrideOriginCode && !account.isActive()) { 314 return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_ORIGIN_CODE_CANNOT_HAVE_CLOSED_ACCOUNT, laborOriginEntry.getChartOfAccountsCode() + "-" + laborOriginEntry.getAccountNumber(), Message.TYPE_FATAL); 315 } 316 317 boolean canBypass = isOverrideOriginCode || continuationAccountBypassBalanceTypeCodes.contains(laborOriginEntry.getFinancialBalanceTypeCode()) || continuationAccountBypassDocumentTypeCodes.contains(laborOriginEntry.getFinancialDocumentTypeCode().trim()); 318 if (account.isActive() && canBypass) { 319 return null; 320 } 321 322 return continuationAccountLogic(account, laborOriginEntry, laborWorkingEntry, universityRunDate); 323 } 324 325 return null; 326 } 327 328 /** 329 * Loops through continuation accounts for 10 tries or until it finds an account that is not expired. 330 */ 331 protected Message continuationAccountLogic(Account expiredClosedAccount, LaborOriginEntry laborOriginEntry, LaborOriginEntry laborWorkingEntry, UniversityDate universityRunDate) { 332 String chartCode = expiredClosedAccount.getContinuationFinChrtOfAcctCd(); 333 String accountNumber = expiredClosedAccount.getContinuationAccountNumber(); 334 335 List<String> checkedAccountNumbers = new ArrayList<String>(); 336 for (int i = 0; i < 10; ++i) { 337 if (checkedAccountNumbers.contains(chartCode + accountNumber)) { 338 // Something is really wrong with the data because this account has already been evaluated. 339 return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_CIRCULAR_DEPENDENCY_IN_CONTINUATION_ACCOUNT_LOGIC, Message.TYPE_FATAL); 340 } 341 342 checkedAccountNumbers.add(chartCode + accountNumber); 343 344 if (chartCode == null || accountNumber == null) { 345 return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_CONTINUATION_ACCOUNT_NOT_FOUND, Message.TYPE_FATAL); 346 } 347 348 // Lookup the account 349 Account account = accountService.getByPrimaryId(chartCode, accountNumber); 350 if (ObjectUtils.isNull(account)) { 351 return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_CONTINUATION_ACCOUNT_NOT_FOUND, Message.TYPE_FATAL); 352 } 353 354 // check account expiration 355 long offsetAccountExpirationTime = getAdjustedAccountExpirationDate(account); 356 if (ObjectUtils.isNotNull(account.getAccountExpirationDate()) && isAccountExpired(account, universityRunDate)) { 357 chartCode = account.getContinuationFinChrtOfAcctCd(); 358 accountNumber = account.getContinuationAccountNumber(); 359 } 360 else { 361 362 // set continuationAccountLogicIndi 363 continuationAccountIndicator = true; 364 365 laborWorkingEntry.setAccount(account); 366 laborWorkingEntry.setAccountNumber(accountNumber); 367 laborWorkingEntry.setChartOfAccountsCode(chartCode); 368 laborWorkingEntry.setSubAccountNumber(KFSConstants.getDashSubAccountNumber()); 369 laborWorkingEntry.setTransactionLedgerEntryDescription(kualiConfigurationService.getPropertyString(KFSKeyConstants.MSG_AUTO_FORWARD) + " " + expiredClosedAccount.getChartOfAccountsCode() + expiredClosedAccount.getAccountNumber() + laborOriginEntry.getTransactionLedgerEntryDescription()); 370 371 return MessageBuilder.buildMessage(KFSKeyConstants.MSG_ACCOUNT_CLOSED_TO, laborWorkingEntry.getChartOfAccountsCode() + "-" + laborWorkingEntry.getAccountNumber(), Message.TYPE_WARNING); 372 } 373 } 374 375 // We failed to find a valid continuation account. 376 boolean suspenseAccountLogicInd = parameterService.getIndicatorParameter(LaborScrubberStep.class, LaborConstants.Scrubber.SUSPENSE_ACCOUNT_LOGIC_PARAMETER); 377 if (suspenseAccountLogicInd) { 378 return useSuspenseAccount(laborWorkingEntry); 379 } 380 else { 381 return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_CONTINUATION_ACCOUNT_LIMIT_REACHED, Message.TYPE_FATAL); 382 } 383 } 384 385 /** 386 * For fringe transaction types checks if the account accepts fringe benefits. If not, retrieves the alternative account, then 387 * calls expiration checking on either the alternative account or the account passed in. 388 */ 389 protected Message checkAccountFringeIndicator(LaborOriginEntry laborOriginEntry, LaborOriginEntry laborWorkingEntry, Account account, UniversityDate universityRunDate, LaborAccountingCycleCachingService laborAccountingCycleCachingService) { 390 // check for fringe tranaction type 391 //LaborObject laborObject = (LaborObject) businessObjectService.findByPrimaryKey(LaborObject.class, fieldValues); 392 LaborObject laborObject = laborAccountingCycleCachingService.getLaborObject(laborOriginEntry.getUniversityFiscalYear(), laborOriginEntry.getChartOfAccountsCode(), laborOriginEntry.getFinancialObjectCode()); 393 boolean isFringeTransaction = laborObject != null && org.apache.commons.lang.StringUtils.equals(LaborConstants.BenefitExpenseTransfer.LABOR_LEDGER_BENEFIT_CODE, laborObject.getFinancialObjectFringeOrSalaryCode()); 394 395 // alternative account handling for non fringe accounts 396 if (isFringeTransaction && !account.isAccountsFringesBnftIndicator()) { 397 Account altAccount = accountService.getByPrimaryId(laborOriginEntry.getAccount().getReportsToChartOfAccountsCode(), laborOriginEntry.getAccount().getReportsToAccountNumber()); 398 if (ObjectUtils.isNotNull(altAccount)) { 399 laborWorkingEntry.setAccount(altAccount); 400 laborWorkingEntry.setAccountNumber(altAccount.getAccountNumber()); 401 laborWorkingEntry.setChartOfAccountsCode(altAccount.getChartOfAccountsCode()); 402 403 return handleExpiredClosedAccount(altAccount, laborOriginEntry, laborWorkingEntry, universityRunDate); 404 } 405 406 // no alt acct, use suspense acct if active 407 boolean suspenseAccountLogicInd = parameterService.getIndicatorParameter(LaborScrubberStep.class, LaborConstants.Scrubber.SUSPENSE_ACCOUNT_LOGIC_PARAMETER); 408 if (suspenseAccountLogicInd) { 409 return useSuspenseAccount(laborWorkingEntry); 410 } 411 412 return MessageBuilder.buildMessage(LaborKeyConstants.ERROR_NON_FRINGE_ACCOUNT_ALTERNATIVE_NOT_FOUND, Message.TYPE_FATAL); 413 } 414 415 return handleExpiredClosedAccount(account, laborOriginEntry, laborWorkingEntry, universityRunDate); 416 } 417 418 /** 419 * Adjustment of Account if it is contracts and grants 420 */ 421 protected long getAdjustedAccountExpirationDate(Account account) { 422 long offsetAccountExpirationTime = 0; 423 424 if (account.getAccountExpirationDate() != null) { 425 offsetAccountExpirationTime = account.getAccountExpirationDate().getTime(); 426 427 if (account.isForContractsAndGrants() && account.isActive()) { 428 String daysOffset = parameterService.getParameterValue(ScrubberStep.class, KFSConstants.SystemGroupParameterNames.GL_SCRUBBER_VALIDATION_DAYS_OFFSET); 429 int daysOffsetInt = 0; // default to 0 430 431 if (!org.apache.commons.lang.StringUtils.isBlank(daysOffset)) { 432 daysOffsetInt = new Integer(daysOffset).intValue(); 433 } 434 435 Calendar tempCal = Calendar.getInstance(); 436 tempCal.setTimeInMillis(offsetAccountExpirationTime); 437 tempCal.add(Calendar.DAY_OF_MONTH, daysOffsetInt); 438 offsetAccountExpirationTime = tempCal.getTimeInMillis(); 439 } 440 } 441 442 return offsetAccountExpirationTime; 443 } 444 445 /** 446 * This method changes account to suspenseAccount 447 */ 448 protected Message useSuspenseAccount(LaborOriginEntry workingEntry) { 449 String suspenseAccountNumber = parameterService.getParameterValue(LaborScrubberStep.class, LaborConstants.Scrubber.SUSPENSE_ACCOUNT); 450 String suspenseCOAcode = parameterService.getParameterValue(LaborScrubberStep.class, LaborConstants.Scrubber.SUSPENSE_CHART); 451 String suspenseSubAccountNumber = parameterService.getParameterValue(LaborScrubberStep.class, LaborConstants.Scrubber.SUSPENSE_SUB_ACCOUNT); 452 453 Account account = accountService.getByPrimaryId(suspenseCOAcode, suspenseAccountNumber); 454 455 if (ObjectUtils.isNull(account)) { 456 return MessageBuilder.buildMessage(LaborKeyConstants.ERROR_INVALID_SUSPENSE_ACCOUNT, Message.TYPE_FATAL); 457 } 458 459 workingEntry.setAccount(account); 460 workingEntry.setAccountNumber(suspenseAccountNumber); 461 workingEntry.setChartOfAccountsCode(suspenseCOAcode); 462 workingEntry.setSubAccountNumber(suspenseSubAccountNumber); 463 464 return MessageBuilder.buildMessageWithPlaceHolder(LaborKeyConstants.MESSAGE_SUSPENSE_ACCOUNT_APPLIED, Message.TYPE_WARNING, suspenseCOAcode, suspenseAccountNumber, suspenseSubAccountNumber); 465 } 466 467 /** 468 * Validates the sub account of the origin entry 469 * 470 * @param originEntry the origin entry being scrubbed 471 * @param workingEntry the scrubbed version of the origin entry 472 * @return a Message if an error was encountered, otherwise null 473 */ 474 475 protected Message validateSubAccount(LaborOriginEntry originEntry, LaborOriginEntry workingEntry, LaborAccountingCycleCachingService laborAccountingCycleCachingService) { 476 LOG.debug("validateSubAccount() started"); 477 478 // when continuationAccount used, the subAccountNumber should be changed to dashes and skip validation subAccount process 479 if (continuationAccountIndicator) { 480 workingEntry.setSubAccountNumber(KFSConstants.getDashSubAccountNumber()); 481 return null; 482 } 483 484 // If the sub account number is empty, set it to dashes. 485 // Otherwise set the workingEntry sub account number to the 486 // sub account number of the input origin entry. 487 if (org.springframework.util.StringUtils.hasText(originEntry.getSubAccountNumber())) { 488 // sub account IS specified 489 if (!KFSConstants.getDashSubAccountNumber().equals(originEntry.getSubAccountNumber())) { 490 SubAccount originEntrySubAccount = laborAccountingCycleCachingService.getSubAccount(originEntry.getChartOfAccountsCode(), originEntry.getAccountNumber(), originEntry.getSubAccountNumber()); 491 //SubAccount originEntrySubAccount = getSubAccount(originEntry); 492 if (originEntrySubAccount == null) { 493 // sub account is not valid 494 return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_SUB_ACCOUNT_NOT_FOUND, originEntry.getChartOfAccountsCode() + "-" + originEntry.getAccountNumber() + "-" + originEntry.getSubAccountNumber(), Message.TYPE_FATAL); 495 } 496 else { 497 // sub account IS valid 498 if (originEntrySubAccount.isActive()) { 499 // sub account IS active 500 workingEntry.setSubAccountNumber(originEntry.getSubAccountNumber()); 501 } 502 else { 503 // sub account IS NOT active 504 if (parameterService.getParameterValue(KfsParameterConstants.GENERAL_LEDGER_BATCH.class, KFSConstants.SystemGroupParameterNames.GL_ANNUAL_CLOSING_DOC_TYPE).equals(originEntry.getFinancialDocumentTypeCode())) { 505 // document IS annual closing 506 workingEntry.setSubAccountNumber(originEntry.getSubAccountNumber()); 507 } 508 else { 509 // document is NOT annual closing 510 return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_SUB_ACCOUNT_NOT_ACTIVE, originEntry.getChartOfAccountsCode() + "-" + originEntry.getAccountNumber() + "-" + originEntry.getSubAccountNumber(), Message.TYPE_FATAL); 511 } 512 } 513 } 514 } 515 else { 516 // the sub account is dashes 517 workingEntry.setSubAccountNumber(KFSConstants.getDashSubAccountNumber()); 518 } 519 } 520 else { 521 // No sub account is specified. 522 workingEntry.setSubAccountNumber(KFSConstants.getDashSubAccountNumber()); 523 } 524 525 526 return null; 527 528 } 529 530 531 /** 532 * @see org.kuali.kfs.gl.service.ScrubberValidator#isAccountExpired(org.kuali.kfs.coa.businessobject.Account, org.kuali.kfs.sys.businessobject.UniversityDate) 533 */ 534 public boolean isAccountExpired(Account account, UniversityDate universityRunDate) { 535 return scrubberValidator.isAccountExpired(account, universityRunDate); 536 } 537 538 public void validateForInquiry(GeneralLedgerPendingEntry entry) { 539 } 540 541 /** 542 * Sets the parameterService attribute value. 543 * 544 * @param parameterService The parameterService to set. 545 */ 546 public void setParameterService(ParameterService parameterService) { 547 this.parameterService = parameterService; 548 } 549 550 /** 551 * Sets the kualiConfigurationService attribute value. 552 * 553 * @param service The kualiConfigurationService to set. 554 */ 555 public void setKualiConfigurationService(KualiConfigurationService service) { 556 kualiConfigurationService = service; 557 } 558 559 /** 560 * Sets the accountService attribute value. 561 * 562 * @param as The accountService to set. 563 */ 564 public void setAccountService(AccountService as) { 565 accountService = as; 566 } 567 568 /** 569 * Sets the persistenceService attribute value. 570 * 571 * @param ps The persistenceService to set. 572 */ 573 public void setPersistenceService(PersistenceService ps) { 574 persistenceService = ps; 575 } 576 577 /** 578 * Sets the businessObjectService attribute value. 579 * 580 * @param businessObjectService The businessObjectService to set. 581 */ 582 public void setBusinessObjectService(BusinessObjectService businessObjectService) { 583 this.businessObjectService = businessObjectService; 584 } 585 586 /** 587 * Sets the balanceTypService attribute value. 588 * 589 * @param balanceTypService The balanceTypService to set. 590 */ 591 public void setBalanceTypService(BalanceTypeService balanceTypService) { 592 this.balanceTypService = balanceTypService; 593 } 594 595 /** 596 * Sets the scrubberValidator attribute value. 597 * 598 * @param sv The scrubberValidator to set. 599 */ 600 public void setScrubberValidator(ScrubberValidator sv) { 601 scrubberValidator = sv; 602 } 603 604 /** 605 * Sets the persistenceStructureService attribute value. 606 * 607 * @param persistenceStructureService The persistenceStructureService to set. 608 */ 609 public void setPersistenceStructureService(PersistenceStructureService persistenceStructureService) { 610 this.persistenceStructureService = persistenceStructureService; 611 } 612 613 /** 614 * Sets the optionsService attribute value. 615 * 616 * @param optionsService The optionsService to set. 617 */ 618 public void setOptionsService(OptionsService optionsService) { 619 this.optionsService = optionsService; 620 } 621 622 623 }