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 }