/* * The Kuali Financial System, a comprehensive financial management system for higher education. * * Copyright 2005-2014 The Kuali Foundation * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.kuali.kfs.gl.service.impl; import java.sql.Date; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import org.kuali.kfs.coa.businessobject.Account; import org.kuali.kfs.coa.businessobject.AccountingPeriod; import org.kuali.kfs.coa.businessobject.BalanceType; import org.kuali.kfs.coa.businessobject.Chart; import org.kuali.kfs.coa.businessobject.ObjectCode; import org.kuali.kfs.coa.businessobject.ObjectType; import org.kuali.kfs.coa.businessobject.ProjectCode; import org.kuali.kfs.coa.businessobject.SubAccount; import org.kuali.kfs.coa.businessobject.SubObjectCode; import org.kuali.kfs.coa.service.AccountService; import org.kuali.kfs.coa.service.BalanceTypeService; import org.kuali.kfs.gl.GeneralLedgerConstants; import org.kuali.kfs.gl.batch.ScrubberStep; import org.kuali.kfs.gl.batch.service.AccountingCycleCachingService; import org.kuali.kfs.gl.businessobject.OriginEntryInformation; import org.kuali.kfs.gl.service.ScrubberValidator; import org.kuali.kfs.sys.KFSConstants; import org.kuali.kfs.sys.KFSKeyConstants; import org.kuali.kfs.sys.KFSPropertyConstants; import org.kuali.kfs.sys.Message; import org.kuali.kfs.sys.MessageBuilder; import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntry; import org.kuali.kfs.sys.businessobject.OriginationCode; import org.kuali.kfs.sys.businessobject.SystemOptions; import org.kuali.kfs.sys.businessobject.UniversityDate; import org.kuali.kfs.sys.context.SpringContext; import org.kuali.kfs.sys.dataaccess.UniversityDateDao; import org.kuali.kfs.sys.service.NonTransactional; import org.kuali.kfs.sys.service.OriginationCodeService; import org.kuali.kfs.sys.service.UniversityDateService; import org.kuali.kfs.sys.service.impl.KfsParameterConstants; import org.kuali.rice.core.api.config.property.ConfigurationService; import org.kuali.rice.core.api.util.type.KualiDecimal; import org.kuali.rice.coreservice.framework.parameter.ParameterService; import org.kuali.rice.kns.service.DataDictionaryService; import org.kuali.rice.krad.service.PersistenceService; import org.kuali.rice.krad.service.PersistenceStructureService; import org.springframework.util.StringUtils; /** * The default GL implementation of ScrubberValidator */ @NonTransactional public class ScrubberValidatorImpl implements ScrubberValidator { private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(ScrubberValidatorImpl.class); protected ConfigurationService kualiConfigurationService; protected ParameterService parameterService; protected PersistenceService persistenceService; protected UniversityDateDao universityDateDao; protected AccountService accountService; protected OriginationCodeService originationCodeService; protected PersistenceStructureService persistenceStructureService; protected BalanceTypeService balanceTypService; protected boolean continuationAccountIndicator; public static final String DATE_FORMAT_STRING = "yyyy-MM-dd"; protected static final Collection<String> debitOrCredit = Arrays.asList( KFSConstants.GL_DEBIT_CODE, KFSConstants.GL_CREDIT_CODE ); protected static final Collection<String> continuationAccountBypassBalanceTypeCodes = Arrays.asList( "EX","IE","PE" ); private static int count = 0; /** * Validate a transaction for use in balance inquiry * * @param entry Input transaction * @see org.kuali.module.gl.service.ScrubberValidator#validateForInquiry(org.kuali.kfs.bo.GeneralLedgerPendingEntry) */ @Override public void validateForInquiry(GeneralLedgerPendingEntry entry) { LOG.debug("validateForInquiry() started"); UniversityDate today = null; if (entry.getUniversityFiscalYear() == null) { // FIXME! - date service should be injected today = SpringContext.getBean(UniversityDateService.class).getCurrentUniversityDate(); entry.setUniversityFiscalYear(today.getUniversityFiscalYear()); } if (entry.getUniversityFiscalPeriodCode() == null) { if (today == null) { // FIXME! - date service should be injected today = SpringContext.getBean(UniversityDateService.class).getCurrentUniversityDate(); } entry.setUniversityFiscalPeriodCode(today.getUniversityFiscalAccountingPeriod()); } if ((entry.getSubAccountNumber() == null) || (!StringUtils.hasText(entry.getSubAccountNumber()))) { entry.setSubAccountNumber(KFSConstants.getDashSubAccountNumber()); } if ((entry.getFinancialSubObjectCode() == null) || (!StringUtils.hasText(entry.getFinancialSubObjectCode()))) { entry.setFinancialSubObjectCode(KFSConstants.getDashFinancialSubObjectCode()); } if ((entry.getProjectCode() == null) || (!StringUtils.hasText(entry.getProjectCode()))) { entry.setProjectCode(KFSConstants.getDashProjectCode()); } } /** * Validate a transaction in the scrubber * * @param originEntry Input transaction (never changed) * @param scrubbedEntry Output transaction (scrubbed version of input transaction) * @param universityRunDate Date of scrubber run * @return List of Message objects based for warnings or errors that happened when validating the transaction * @see org.kuali.module.gl.service.ScrubberValidator#validateTransaction(org.kuali.module.gl.bo.OriginEntry, org.kuali.module.gl.bo.OriginEntry, org.kuali.module.gl.bo.UniversityDate, boolean) */ @Override public List<Message> validateTransaction(OriginEntryInformation originEntry, OriginEntryInformation scrubbedEntry, UniversityDate universityRunDate, boolean laborIndicator, AccountingCycleCachingService accountingCycleCachingService) { LOG.debug("validateTransaction() started"); continuationAccountIndicator = false; List<Message> errors = new ArrayList<Message>(); count++; if (count % 1000 == 0) { LOG.info(count + " " + originEntry.getLine()); } // The cobol checks fdoc_nbr, trn_ldgr_entr_desc, org_doc_nbr, org_reference_id, and fdoc_ref_nbr for characters less than // ascii 32 or '~'. If found, it replaces that character with a space and reports a warning. This code does the ~, but not // the // less than 32 part. if ((originEntry.getDocumentNumber() != null) && (originEntry.getDocumentNumber().indexOf("~") > -1)) { String d = originEntry.getDocumentNumber(); scrubbedEntry.setDocumentNumber(d.replaceAll("~", " ")); errors.add(new Message("** INVALID CHARACTER EDITED", Message.TYPE_WARNING)); } if ((originEntry.getTransactionLedgerEntryDescription() != null) && (originEntry.getTransactionLedgerEntryDescription().indexOf("~") > -1)) { String d = originEntry.getTransactionLedgerEntryDescription(); scrubbedEntry.setTransactionLedgerEntryDescription(d.replaceAll("~", " ")); errors.add(new Message("** INVALID CHARACTER EDITED", Message.TYPE_WARNING)); } if ((originEntry.getOrganizationDocumentNumber() != null) && (originEntry.getOrganizationDocumentNumber().indexOf("~") > -1)) { String d = originEntry.getOrganizationDocumentNumber(); scrubbedEntry.setOrganizationDocumentNumber(d.replaceAll("~", " ")); errors.add(new Message("** INVALID CHARACTER EDITED", Message.TYPE_WARNING)); } if ((originEntry.getOrganizationReferenceId() != null) && (originEntry.getOrganizationReferenceId().indexOf("~") > -1)) { String d = originEntry.getOrganizationReferenceId(); scrubbedEntry.setOrganizationReferenceId(d.replaceAll("~", " ")); errors.add(new Message("** INVALID CHARACTER EDITED", Message.TYPE_WARNING)); } if ((originEntry.getReferenceFinancialDocumentNumber() != null) && (originEntry.getReferenceFinancialDocumentNumber().indexOf("~") > -1)) { String d = originEntry.getReferenceFinancialDocumentNumber(); scrubbedEntry.setReferenceFinancialDocumentNumber(d.replaceAll("~", " ")); errors.add(new Message("** INVALID CHARACTER EDITED", Message.TYPE_WARNING)); } // It's important that this check come before the checks for object, sub-object and accountingPeriod // because this validation method will set the fiscal year and reload those three objects if the fiscal // year was invalid. This will also set originEntry.getOption and workingEntry.getOption. So, it's // probably a good idea to validate the fiscal year first thing. Message err = validateFiscalYear(originEntry, scrubbedEntry, universityRunDate, accountingCycleCachingService); if (err != null) { errors.add(err); } err = validateUniversityFiscalPeriodCode(originEntry, scrubbedEntry, universityRunDate, accountingCycleCachingService); if (err != null) { errors.add(err); } err = validateBalanceType(originEntry, scrubbedEntry, accountingCycleCachingService); if (err != null) { errors.add(err); } err = validateTransactionDate(originEntry, scrubbedEntry, universityRunDate, accountingCycleCachingService); if (err != null) { errors.add(err); } err = validateTransactionAmount(originEntry, scrubbedEntry, accountingCycleCachingService); if (err != null) { errors.add(err); } err = validateChart(originEntry, scrubbedEntry, accountingCycleCachingService); if (err != null) { errors.add(err); } // Labor Scrubber doesn't validate Account here. if (!laborIndicator) { err = validateAccount(originEntry, scrubbedEntry, universityRunDate, accountingCycleCachingService); if (err != null) { errors.add(err); } } // Labor Scrubber doesn't validate SubAccount here if (!laborIndicator) { err = validateSubAccount(originEntry, scrubbedEntry, accountingCycleCachingService); if (err != null) { errors.add(err); } } err = validateProjectCode(originEntry, scrubbedEntry, accountingCycleCachingService); if (err != null) { errors.add(err); } err = validateDocumentType(originEntry, scrubbedEntry, accountingCycleCachingService); if (err != null) { errors.add(err); } err = validateOrigination(originEntry, scrubbedEntry, accountingCycleCachingService); if (err != null) { errors.add(err); } err = validateReferenceOrigination(originEntry, scrubbedEntry, accountingCycleCachingService); if (err != null) { errors.add(err); } err = validateDocumentNumber(originEntry, scrubbedEntry); if (err != null) { errors.add(err); } err = validateObjectCode(originEntry, scrubbedEntry, accountingCycleCachingService); if (err != null) { errors.add(err); } // If object code is invalid, we can't check the object type if (err == null) { err = validateObjectType(originEntry, scrubbedEntry, accountingCycleCachingService); if (err != null) { errors.add(err); } } err = validateSubObjectCode(originEntry, scrubbedEntry, accountingCycleCachingService); if (err != null) { errors.add(err); } // return messages could be multiple from validateReferenceFields List<Message> referenceErrors = new ArrayList<Message>(); referenceErrors = validateReferenceDocumentFields(originEntry, scrubbedEntry, accountingCycleCachingService); if (referenceErrors != null) { errors.addAll(referenceErrors); } err = validateReversalDate(originEntry, scrubbedEntry, accountingCycleCachingService); if (err != null) { errors.add(err); } err = validateDescription(originEntry); if (err != null) { errors.add(err); } return errors; } /** * Validates the account of an origin entry * * @param originEntry the origin entry to find the account of * @param workingEntry the copy of the entry to move the account over to if it is valid * @param universityRunDate the run date of the scrubber process * @return a Message if the account was invalid, or null if no error was encountered */ protected Message validateAccount(OriginEntryInformation originEntry, OriginEntryInformation workingEntry, UniversityDate universityRunDate, AccountingCycleCachingService accountingCycleCachingService) { LOG.debug("validateAccount() started"); Account originEntryAccount = accountingCycleCachingService.getAccount(originEntry.getChartOfAccountsCode(), originEntry.getAccountNumber()); if (originEntryAccount != null) { originEntryAccount.setSubFundGroup(accountingCycleCachingService.getSubFundGroup(originEntryAccount.getSubFundGroupCode())); } if (originEntryAccount == null) { return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_ACCOUNT_NOT_FOUND, originEntry.getChartOfAccountsCode() + "-" + originEntry.getAccountNumber(), Message.TYPE_FATAL); } if (parameterService.getParameterValueAsString(KfsParameterConstants.GENERAL_LEDGER_BATCH.class, KFSConstants.SystemGroupParameterNames.GL_ANNUAL_CLOSING_DOC_TYPE).equals(originEntry.getFinancialDocumentTypeCode())) { workingEntry.setAccountNumber(originEntry.getAccountNumber()); return null; } if ((originEntryAccount.getAccountExpirationDate() == null) && originEntryAccount.isActive()) { // account is neither closed nor expired workingEntry.setAccountNumber(originEntry.getAccountNumber()); return null; } Collection<String> continuationAccountBypassOriginationCodes = new ArrayList<String>( parameterService.getParameterValuesAsString(ScrubberStep.class, GeneralLedgerConstants.GlScrubberGroupRules.CONTINUATION_ACCOUNT_BYPASS_ORIGINATION_CODES) ); Collection<String> continuationAccountBypassDocumentTypeCodes = new ArrayList<String>( parameterService.getParameterValuesAsString(ScrubberStep.class, GeneralLedgerConstants.GlScrubberGroupRules.CONTINUATION_ACCOUNT_BYPASS_DOCUMENT_TYPE_CODES) ); // Has an expiration date or is closed if ((continuationAccountBypassOriginationCodes.contains( originEntry.getFinancialSystemOriginationCode())) && !originEntryAccount.isActive()) { return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_ORIGIN_CODE_CANNOT_HAVE_CLOSED_ACCOUNT, originEntryAccount.getChartOfAccountsCode() + "-" + originEntry.getAccountNumber(), Message.TYPE_FATAL); } if ((continuationAccountBypassOriginationCodes.contains( originEntry.getFinancialSystemOriginationCode()) || continuationAccountBypassBalanceTypeCodes.contains( originEntry.getFinancialBalanceTypeCode()) || continuationAccountBypassDocumentTypeCodes.contains( originEntry.getFinancialDocumentTypeCode().trim())) && originEntryAccount.isActive()) { workingEntry.setAccountNumber(originEntry.getAccountNumber()); return null; } Calendar today = Calendar.getInstance(); today.setTime(universityRunDate.getUniversityDate()); if (isAccountExpired(originEntryAccount, universityRunDate) || !originEntryAccount.isActive()) { Message error = continuationAccountLogic(originEntry, workingEntry, universityRunDate, accountingCycleCachingService); if (error != null) { return error; } } workingEntry.setAccountNumber(originEntry.getAccountNumber()); return null; } /** * Called when the account of the origin entry is expired or closed, this validates the continuation account * * @param originEntry the origin entry being scrubbed * @param workingEntry the scrubbed version of the origin entry * @param universityRunDate the run date of the scrubber (to test against expiration dates) * @return a Message if an error was encountered, otherwise null */ protected Message continuationAccountLogic(OriginEntryInformation originEntry, OriginEntryInformation workingEntry, UniversityDate universityRunDate, AccountingCycleCachingService accountingCycleCachingService) { Set<String> checkedAccountNumbers = new HashSet<String>(); Account continuationAccount = null; Account originEntryAccount = accountingCycleCachingService.getAccount(originEntry.getChartOfAccountsCode(), originEntry.getAccountNumber()); String chartCode = originEntryAccount.getContinuationFinChrtOfAcctCd(); String accountNumber = originEntryAccount.getContinuationAccountNumber(); for (int i = 0; i < 10; ++i) { if (checkedAccountNumbers.contains(chartCode + accountNumber)) { // Something is really wrong with the data because this account has already been evaluated. return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_CIRCULAR_DEPENDENCY_IN_CONTINUATION_ACCOUNT_LOGIC, Message.TYPE_FATAL); } if ((chartCode == null) || (accountNumber == null)) { return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_CONTINUATION_ACCOUNT_NOT_FOUND, Message.TYPE_FATAL); } // Lookup the account continuationAccount = accountingCycleCachingService.getAccount(chartCode, accountNumber); if (null == continuationAccount) { // account not found return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_CONTINUATION_ACCOUNT_NOT_FOUND, Message.TYPE_FATAL); } else { // the account exists continuationAccount.setSubFundGroup(accountingCycleCachingService.getSubFundGroup(continuationAccount.getSubFundGroupCode())); if (continuationAccount.getAccountExpirationDate() == null) { // No expiration date workingEntry.setAccountNumber(accountNumber); workingEntry.setChartOfAccountsCode(chartCode); // to set subAcount with dashes continuationAccountIndicator = true; //workingEntry.setSubAccountNumber(KFSConstants.getDashSubAccountNumber()); //workingEntry.setTransactionLedgerEntryDescription(kualiConfigurationService.getPropertyValueAsString(KFSKeyConstants.MSG_AUTO_FORWARD) + " " + originEntry.getChartOfAccountsCode() + originEntry.getAccountNumber() + originEntry.getTransactionLedgerEntryDescription()); // TODO:- use messageBuilder and KeyConstant - also, length issue!?!?? workingEntry.setTransactionLedgerEntryDescription("AUTO FR " + originEntry.getChartOfAccountsCode() + originEntry.getAccountNumber() + originEntry.getTransactionLedgerEntryDescription()); // FSKD-310 : need to check the account is closed for building message. if not, it is expired. if (!originEntryAccount.isActive()){ return MessageBuilder.buildMessage(KFSKeyConstants.MSG_ACCOUNT_CLOSED_TO, chartCode+accountNumber, Message.TYPE_WARNING); } else { return MessageBuilder.buildMessage(KFSKeyConstants.MSG_ACCOUNT_EXPIRED_TO, chartCode+accountNumber, Message.TYPE_WARNING); } } else { // the account does have an expiration date. // This is the only case in which we might go // on for another iteration of the loop. checkedAccountNumbers.add(chartCode + accountNumber); // Check that the account has not expired. // If the account has expired go around for another iteration. if (isAccountExpired(continuationAccount, universityRunDate)) { chartCode = continuationAccount.getContinuationFinChrtOfAcctCd(); accountNumber = continuationAccount.getContinuationAccountNumber(); } else { workingEntry.setAccountNumber(accountNumber); workingEntry.setChartOfAccountsCode(chartCode); // to set subAccount with dashes continuationAccountIndicator = true; //workingEntry.setSubAccountNumber(KFSConstants.getDashSubAccountNumber()); //workingEntry.setTransactionLedgerEntryDescription(kualiConfigurationService.getPropertyValueAsString(KFSKeyConstants.MSG_AUTO_FORWARD) + originEntry.getChartOfAccountsCode() + originEntry.getAccountNumber() + originEntry.getTransactionLedgerEntryDescription()); // TODO:- use messageBuilder and KeyConstant - also, length issue!?!?? workingEntry.setTransactionLedgerEntryDescription("AUTO FR " + originEntry.getChartOfAccountsCode() + originEntry.getAccountNumber() + originEntry.getTransactionLedgerEntryDescription()); // FSKD-310 : need to check the account is closed for building message. if not, it is expired. if (!originEntryAccount.isActive()){ return MessageBuilder.buildMessage(KFSKeyConstants.MSG_ACCOUNT_CLOSED_TO, chartCode+accountNumber, Message.TYPE_WARNING); } else { return MessageBuilder.buildMessage(KFSKeyConstants.MSG_ACCOUNT_EXPIRED_TO, chartCode+accountNumber, Message.TYPE_WARNING); } } } } } // We failed to find a valid continuation account. return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_CONTINUATION_ACCOUNT_LIMIT_REACHED, Message.TYPE_FATAL); } /** * Calculates the expiration date of an adjusted account * * @param account the expired account * @return the timestamp of the adjusted date */ protected long getAdjustedAccountExpirationDate(Account account) { long offsetAccountExpirationTime = account.getAccountExpirationDate().getTime(); if (account.isForContractsAndGrants() && (account.isActive())) { String daysOffset = parameterService.getParameterValueAsString(ScrubberStep.class, KFSConstants.SystemGroupParameterNames.GL_SCRUBBER_VALIDATION_DAYS_OFFSET); int daysOffsetInt = 0; // default to 0 if (!org.apache.commons.lang.StringUtils.isBlank(daysOffset)) { daysOffsetInt = new Integer(daysOffset).intValue(); } Calendar tempCal = Calendar.getInstance(); tempCal.setTimeInMillis(offsetAccountExpirationTime); tempCal.add(Calendar.DAY_OF_MONTH, daysOffsetInt); offsetAccountExpirationTime = tempCal.getTimeInMillis(); } return offsetAccountExpirationTime; } /** * Validates the reversal date of the origin entry * * @param originEntry the origin entry being scrubbed * @param workingEntry the scrubbed version of the origin entry * @return a Message if an error was encountered, otherwise null */ protected Message validateReversalDate(OriginEntryInformation originEntry, OriginEntryInformation workingEntry, AccountingCycleCachingService accountingCycleCachingService) { LOG.debug("validateReversalDate() started"); if (originEntry.getFinancialDocumentReversalDate() != null) { // UniversityDate universityDate = universityDateDao.getByPrimaryKey(originEntry.getFinancialDocumentReversalDate()); UniversityDate universityDate = accountingCycleCachingService.getUniversityDate(originEntry.getFinancialDocumentReversalDate()); if (universityDate == null) { Date reversalDate = originEntry.getFinancialDocumentReversalDate(); SimpleDateFormat format = new SimpleDateFormat(DATE_FORMAT_STRING); return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_REVERSAL_DATE_NOT_FOUND, format.format(reversalDate), Message.TYPE_FATAL); } else { workingEntry.setFinancialDocumentReversalDate(originEntry.getFinancialDocumentReversalDate()); } } return null; } /** * Validates the sub account of the origin entry * * @param originEntry the origin entry being scrubbed * @param workingEntry the scrubbed version of the origin entry * @return a Message if an error was encountered, otherwise null */ protected Message validateSubAccount(OriginEntryInformation originEntry, OriginEntryInformation workingEntry, AccountingCycleCachingService accountingCycleCachingService) { LOG.debug("validateSubAccount() started"); // when continuationAccount used, the subAccountNumber should be changed to dashes and skip validation subAccount process if (continuationAccountIndicator) { workingEntry.setSubAccountNumber(KFSConstants.getDashSubAccountNumber()); return null; } // If the sub account number is empty, set it to dashes. // Otherwise set the workingEntry sub account number to the // sub account number of the input origin entry. String subAccount = originEntry.getSubAccountNumber(); if (StringUtils.hasText(subAccount)) { // sub account IS specified // check if need upper case DataDictionaryService dataDictionaryService = SpringContext.getBean(DataDictionaryService.class); // uppercase the data used to generate the collector header if (dataDictionaryService.getAttributeForceUppercase(SubAccount.class, KFSPropertyConstants.SUB_ACCOUNT_NUMBER)) { subAccount = originEntry.getSubAccountNumber().toUpperCase(); } if (!KFSConstants.getDashSubAccountNumber().equals(subAccount)) { SubAccount originEntrySubAccount = accountingCycleCachingService.getSubAccount(originEntry.getChartOfAccountsCode(), originEntry.getAccountNumber(), subAccount); //SubAccount originEntrySubAccount = getSubAccount(originEntry); if (originEntrySubAccount == null) { // sub account is not valid return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_SUB_ACCOUNT_NOT_FOUND, originEntry.getChartOfAccountsCode() + "-" + originEntry.getAccountNumber() + "-" + subAccount, Message.TYPE_FATAL); } else { // sub account IS valid if (originEntrySubAccount.isActive()) { // sub account IS active workingEntry.setSubAccountNumber(subAccount); } else { // sub account IS NOT active if (parameterService.getParameterValueAsString(KfsParameterConstants.GENERAL_LEDGER_BATCH.class, KFSConstants.SystemGroupParameterNames.GL_ANNUAL_CLOSING_DOC_TYPE).equals(originEntry.getFinancialDocumentTypeCode())) { // document IS annual closing workingEntry.setSubAccountNumber(subAccount); } else { // document is NOT annual closing return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_SUB_ACCOUNT_NOT_ACTIVE, originEntry.getChartOfAccountsCode() + "-" + originEntry.getAccountNumber() + "-" + subAccount, Message.TYPE_FATAL); } } } } else { // the sub account is dashes workingEntry.setSubAccountNumber(KFSConstants.getDashSubAccountNumber()); } } else { // No sub account is specified. workingEntry.setSubAccountNumber(KFSConstants.getDashSubAccountNumber()); } return null; } /** * Validates the project code of the origin entry * * @param originEntry the origin entry being scrubbed * @param workingEntry the scrubbed version of the origin entry * @return a Message if an error was encountered, otherwise null */ protected Message validateProjectCode(OriginEntryInformation originEntry, OriginEntryInformation workingEntry, AccountingCycleCachingService accountingCycleCachingService) { LOG.debug("validateProjectCode() started"); if (StringUtils.hasText(originEntry.getProjectCode()) && !KFSConstants.getDashProjectCode().equals(originEntry.getProjectCode())) { ProjectCode originEntryProject = accountingCycleCachingService.getProjectCode(originEntry.getProjectCode()); if (originEntryProject == null) { return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_PROJECT_CODE_NOT_FOUND, originEntry.getProjectCode(), Message.TYPE_FATAL); } else { if (originEntryProject.isActive()) { workingEntry.setProjectCode(originEntry.getProjectCode()); } else { return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_PROJECT_CODE_MUST_BE_ACTIVE, originEntry.getProjectCode(), Message.TYPE_FATAL); } } } else { workingEntry.setProjectCode(KFSConstants.getDashProjectCode()); } return null; } /** * Validates the fiscal year of the origin entry * * @param originEntry the origin entry being scrubbed * @param workingEntry the scrubbed version of the origin entry * @param universityRunDate the university date when this scrubber process is being run * @return a Message if an error was encountered, otherwise null */ protected Message validateFiscalYear(OriginEntryInformation originEntry, OriginEntryInformation workingEntry, UniversityDate universityRunDate, AccountingCycleCachingService accountingCycleCachingService) { LOG.debug("validateFiscalYear() started"); if ((originEntry.getUniversityFiscalYear() == null) || (originEntry.getUniversityFiscalYear().intValue() == 0)) { //commented out for KULLAB-627 //if (!originEntry.getFinancialBalanceTypeCode().equals(KFSConstants.BALANCE_TYPE_A21)){ workingEntry.setUniversityFiscalYear(universityRunDate.getUniversityFiscalYear()); workingEntry.setUniversityFiscalPeriodCode(universityRunDate.getUniversityFiscalAccountingPeriod()); // TODO:- to display updated values on report // TODO:- need to check because below two lines are commented out in validateUniversityFiscalPeriodCode originEntry.setUniversityFiscalYear(universityRunDate.getUniversityFiscalYear()); originEntry.setUniversityFiscalPeriodCode(universityRunDate.getUniversityFiscalAccountingPeriod()); } else { workingEntry.setUniversityFiscalYear(originEntry.getUniversityFiscalYear()); } SystemOptions originEntryOption = accountingCycleCachingService.getSystemOptions(workingEntry.getUniversityFiscalYear()); if (originEntryOption == null) { return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_UNIV_FISCAL_YR_NOT_FOUND, originEntry.getUniversityFiscalYear() + "", Message.TYPE_FATAL); } return null; } /** * Validates the transaction date of the origin entry, make sure it is a valid university date * * @param originEntry the origin entry being scrubbed * @param workingEntry the scrubbed version of the origin entry * @param universityRunDate the university date when this scrubber process is being run * @return a Message if an error was encountered, otherwise null */ protected Message validateTransactionDate(OriginEntryInformation originEntry, OriginEntryInformation workingEntry, UniversityDate universityRunDate, AccountingCycleCachingService accountingCycleCachingService) { LOG.debug("validateTransactionDate() started"); Date transactionDate = new Date(universityRunDate.getUniversityDate().getTime()); if (originEntry.getTransactionDate() == null) { // Set the transaction date to the run date. originEntry.setTransactionDate(transactionDate); workingEntry.setTransactionDate(transactionDate); } else { workingEntry.setTransactionDate(originEntry.getTransactionDate()); } // Next, we have to validate the transaction date against the university date table. if (accountingCycleCachingService.getUniversityDate(originEntry.getTransactionDate()) == null) { //FSKD-193, KFSMI-5441 //return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_TRANSACTION_DATE_INVALID, originEntry.getTransactionDate().toString(), Message.TYPE_FATAL); originEntry.setTransactionDate(transactionDate); workingEntry.setTransactionDate(transactionDate); } return null; } /** * Validates the document type of an origin entry * @param originEntry the origin entry to check * @param workingEntryInfo the copy of that entry to move good data over to * @return a Message if the document type is invalid, otherwise if valid, null */ protected Message validateDocumentType(OriginEntryInformation originEntry, OriginEntryInformation workingEntry, AccountingCycleCachingService accountingCycleCachingService) { LOG.debug("validateDocumentType() started"); if ((originEntry.getFinancialDocumentTypeCode() == null) || !accountingCycleCachingService.isCurrentActiveAccountingDocumentType(originEntry.getFinancialDocumentTypeCode())) { return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_DOCUMENT_TYPE_NOT_FOUND, originEntry.getFinancialDocumentTypeCode(), Message.TYPE_FATAL); } workingEntry.setFinancialDocumentTypeCode(originEntry.getFinancialDocumentTypeCode()); return null; } /** * Validates the origination code of the origin entry * * @param originEntry the origin entry being scrubbed * @param workingEntry the scrubbed version of the origin entry * @return a Message if an error was encountered, otherwise null */ protected Message validateOrigination(OriginEntryInformation originEntry, OriginEntryInformation workingEntry, AccountingCycleCachingService accountingCycleCachingService) { LOG.debug("validateOrigination() started"); if (StringUtils.hasText(originEntry.getFinancialSystemOriginationCode())) { OriginationCode originEntryOrigination = accountingCycleCachingService.getOriginationCode(originEntry.getFinancialSystemOriginationCode()); if (originEntryOrigination == null) { return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_ORIGIN_CODE_NOT_FOUND, originEntry.getFinancialSystemOriginationCode(), Message.TYPE_FATAL); } if (!originEntryOrigination.isActive()) { return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_ORIGIN_CODE_NOT_ACTIVE, originEntry.getFinancialSystemOriginationCode(), Message.TYPE_FATAL); } workingEntry.setFinancialSystemOriginationCode(originEntry.getFinancialSystemOriginationCode()); } else { return new Message(kualiConfigurationService.getPropertyValueAsString(KFSKeyConstants.ERROR_ORIGIN_CODE_NOT_FOUND) + " (" + originEntry.getFinancialSystemOriginationCode() + ")", Message.TYPE_FATAL); } return null; } protected Message validateReferenceOrigination(OriginEntryInformation originEntry, OriginEntryInformation workingEntry, AccountingCycleCachingService accountingCycleCachingService) { LOG.debug("validateOrigination() started"); String referenceFinancialSystemOriginationCode = originEntry.getReferenceFinancialSystemOriginationCode(); if (StringUtils.hasText(referenceFinancialSystemOriginationCode)) { OriginationCode originEntryOrigination = accountingCycleCachingService.getOriginationCode(referenceFinancialSystemOriginationCode); if (originEntryOrigination == null) { return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_REFERENCE_ORIGIN_CODE_NOT_FOUND, " (" + referenceFinancialSystemOriginationCode + ")", Message.TYPE_FATAL); } else { workingEntry.setReferenceFinancialSystemOriginationCode(referenceFinancialSystemOriginationCode); } } return null; } /** * Validates the document number of the origin entry * * @param originEntry the origin entry being scrubbed * @param workingEntry the scrubbed version of the origin entry * @return a Message if an error was encountered, otherwise null */ protected Message validateDocumentNumber(OriginEntryInformation originEntry, OriginEntryInformation workingEntry) { LOG.debug("validateDocumentNumber() started"); if (!StringUtils.hasText(originEntry.getDocumentNumber())) { return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_DOCUMENT_NUMBER_REQUIRED, Message.TYPE_FATAL); } else { workingEntry.setDocumentNumber(originEntry.getDocumentNumber()); return null; } } /** * Validates the chart of the origin entry * * @param originEntry the origin entry being scrubbed * @param workingEntry the scrubbed version of the origin entry * @return a Message if an error was encountered, otherwise null */ protected Message validateChart(OriginEntryInformation originEntry, OriginEntryInformation workingEntry, AccountingCycleCachingService accountingCycleCachingService) { LOG.debug("validateChart() started"); Chart originEntryChart = accountingCycleCachingService.getChart(originEntry.getChartOfAccountsCode()); if (originEntryChart == null) { return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_CHART_NOT_FOUND, originEntry.getChartOfAccountsCode(), Message.TYPE_FATAL); } if (!originEntryChart.isActive()) { return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_CHART_NOT_ACTIVE, originEntry.getChartOfAccountsCode(), Message.TYPE_FATAL); } workingEntry.setChartOfAccountsCode(originEntry.getChartOfAccountsCode()); return null; } /** * Validates the object code of the origin entry * * @param originEntry the origin entry being scrubbed * @param workingEntry the scrubbed version of the origin entry * @return a Message if an error was encountered, otherwise null */ protected Message validateObjectCode(OriginEntryInformation originEntry, OriginEntryInformation workingEntry, AccountingCycleCachingService accountingCycleCachingService) { LOG.debug("validateObjectCode() started"); if (!StringUtils.hasText(originEntry.getFinancialObjectCode())) { return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_OBJECT_CODE_EMPTY, Message.TYPE_FATAL); } // We're checking the object code based on the year & chart from the working entry. workingEntry.setFinancialObjectCode(originEntry.getFinancialObjectCode()); // the fiscal year can be blank in originEntry, but we're assuming that the year attribute is populated by the validate year // method ObjectCode workingEntryFinancialObject = accountingCycleCachingService.getObjectCode(workingEntry.getUniversityFiscalYear(), workingEntry.getChartOfAccountsCode(), workingEntry.getFinancialObjectCode()); if (workingEntryFinancialObject == null) { String objectCodeString = workingEntry.getUniversityFiscalYear() + "-" + workingEntry.getChartOfAccountsCode() + "-" + workingEntry.getFinancialObjectCode(); return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_OBJECT_CODE_NOT_FOUND, objectCodeString, Message.TYPE_FATAL); } if (!workingEntryFinancialObject.isActive()) { String objectCodeString = workingEntry.getUniversityFiscalYear() + "-" + workingEntry.getChartOfAccountsCode() + "-" + workingEntry.getFinancialObjectCode(); return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_OBJECT_CODE_NOT_ACTIVE, objectCodeString, Message.TYPE_FATAL); } //TODO:- need to commented back after using file --> ?? // changed OriginEntryInformation to OriginEntryFull in ScrubberProcess line 537 (after getting entry from file) //((OriginEntryFull)workingEntry).setFinancialObject(workingEntryFinancialObject); //((OriginEntryFull)originEntry).setFinancialObject(workingEntryFinancialObject); return null; } /** * Assuming that the object code has been validated first, validates the object type of the entry * * @param originEntry the origin entry being scrubbed * @param workingEntry the scrubbed version of the origin entry * @return a Message if an error was encountered, otherwise null * @see org.kuali.module.gl.service.ScrubberValidator#validateObjectType(org.kuali.module.gl.bo.OriginEntryFull, * org.kuali.module.gl.bo.OriginEntryFull) */ protected Message validateObjectType(OriginEntryInformation originEntry, OriginEntryInformation workingEntry, AccountingCycleCachingService accountingCycleCachingService) { LOG.debug("validateObjectType() started"); // grab bypass origin codes from parameters Set<String> objectTypeBypassOriginationCodes = new HashSet<String>(parameterService.getParameterValuesAsString(ScrubberStep.class, GeneralLedgerConstants.GlScrubberGroupRules.OBJECT_TYPE_BYPASS_ORIGINATIONS)); // if this is not a bypassed origination code or if object type code is blank (PDP origin) then insert appropriate object type code // for incoming transaction based on the object code - want to do this before validation checks if ((!objectTypeBypassOriginationCodes.contains(originEntry.getFinancialSystemOriginationCode())) || (!StringUtils.hasText(originEntry.getFinancialObjectTypeCode()))) { ObjectCode workingEntryFinancialObject = accountingCycleCachingService.getObjectCode(workingEntry.getUniversityFiscalYear(), workingEntry.getChartOfAccountsCode(), workingEntry.getFinancialObjectCode()); workingEntry.setFinancialObjectTypeCode(workingEntryFinancialObject.getFinancialObjectTypeCode()); } else { workingEntry.setFinancialObjectTypeCode(originEntry.getFinancialObjectTypeCode()); } ObjectType workingEntryObjectType = accountingCycleCachingService.getObjectType(workingEntry.getFinancialObjectTypeCode()); if (workingEntryObjectType == null) { return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_OBJECT_TYPE_NOT_FOUND, originEntry.getFinancialObjectTypeCode(), Message.TYPE_FATAL); } if (!workingEntryObjectType.isActive()) { return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_OBJECT_TYPE_NOT_ACTIVE, originEntry.getFinancialObjectTypeCode(), Message.TYPE_FATAL); } return null; } /** * Validates the sub object code of the origin entry * * @param originEntry the origin entry being scrubbed * @param workingEntry the scrubbed version of the origin entry * @return a Message if an error was encountered, otherwise null */ protected Message validateSubObjectCode(OriginEntryInformation originEntry, OriginEntryInformation workingEntry, AccountingCycleCachingService accountingCycleCachingService) { LOG.debug("validateFinancialSubObjectCode() started"); if (!StringUtils.hasText(originEntry.getFinancialSubObjectCode())) { workingEntry.setFinancialSubObjectCode(KFSConstants.getDashFinancialSubObjectCode()); return null; } if (!KFSConstants.getDashFinancialSubObjectCode().equals(originEntry.getFinancialSubObjectCode())) { SubObjectCode originEntrySubObject = accountingCycleCachingService.getSubObjectCode(originEntry.getUniversityFiscalYear(), originEntry.getChartOfAccountsCode(), originEntry.getAccountNumber(), originEntry.getFinancialObjectCode(), originEntry.getFinancialSubObjectCode()); if (originEntrySubObject != null) { // Exists if (!originEntrySubObject.isActive()) { // if NOT active, set it to dashes workingEntry.setFinancialSubObjectCode(KFSConstants.getDashFinancialSubObjectCode()); return null; } } else { // Doesn't exist workingEntry.setFinancialSubObjectCode(KFSConstants.getDashFinancialSubObjectCode()); return null; } } workingEntry.setFinancialSubObjectCode(originEntry.getFinancialSubObjectCode()); return null; } /** * Validates the balance type of the origin entry * * @param originEntry the origin entry being scrubbed * @param workingEntry the scrubbed version of the origin entry * @return a Message if an error was encountered, otherwise null */ protected Message validateBalanceType(OriginEntryInformation originEntry, OriginEntryInformation workingEntry, AccountingCycleCachingService accountingCycleCachingService) { LOG.debug("validateBalanceType() started"); // balance type IS NOT empty String balanceTypeCode = originEntry.getFinancialBalanceTypeCode(); if (StringUtils.hasText(balanceTypeCode)) { BalanceType originEntryBalanceType = accountingCycleCachingService.getBalanceType(originEntry.getFinancialBalanceTypeCode()); if (originEntryBalanceType == null) { // balance type IS NOT valid return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_BALANCE_TYPE_NOT_FOUND, " (" + balanceTypeCode + ")", Message.TYPE_FATAL); } else if (!originEntryBalanceType.isActive()) { return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_BALANCE_TYPE_NOT_ACTIVE, balanceTypeCode, Message.TYPE_FATAL); } else { // balance type IS valid if (originEntryBalanceType.isFinancialOffsetGenerationIndicator()) { // entry IS an offset if (originEntry.getTransactionLedgerEntryAmount().isNegative()) { // it's an INVALID non-budget transaction return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_TRANS_CANNOT_BE_NEGATIVE_IF_OFFSET, Message.TYPE_FATAL); } else { // it's a VALID non-budget transaction if (!originEntry.isCredit() && !originEntry.isDebit()) { // entries requiring an offset must be either a // debit or a credit return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_DC_INDICATOR_MUST_BE_D_OR_C, originEntry.getTransactionDebitCreditCode(), Message.TYPE_FATAL); } else { workingEntry.setFinancialBalanceTypeCode(balanceTypeCode); } } } else { // entry IS NOT an offset, means it's a budget transaction if (StringUtils.hasText(originEntry.getTransactionDebitCreditCode())) { return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_DC_INDICATOR_MUST_BE_EMPTY, originEntry.getTransactionDebitCreditCode(), Message.TYPE_FATAL); } else { if (originEntry.isCredit() || originEntry.isDebit()) { // budget transactions must be neither debit nor credit return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_DC_INDICATOR_MUST_BE_NEITHER_D_NOR_C, originEntry.getTransactionDebitCreditCode(), Message.TYPE_FATAL); } else { // it's a valid budget transaction workingEntry.setFinancialBalanceTypeCode(balanceTypeCode); } } } } } else { // balance type IS empty. We can't set it if the year isn't set SystemOptions workingEntryOption = accountingCycleCachingService.getSystemOptions(workingEntry.getUniversityFiscalYear()); if (workingEntryOption != null) { workingEntry.setFinancialBalanceTypeCode(workingEntryOption.getActualFinancialBalanceTypeCd()); } else { //TODO:- need to change to use MessageBuilder return new Message("Unable to set balance type code when year is unknown: " + workingEntry.getUniversityFiscalYear(), Message.TYPE_FATAL); } } return null; } /** * Validates the period code of the origin entry * * @param originEntry the origin entry being scrubbed * @param workingEntry the scrubbed version of the origin entry * @param universityRunDate the university date when this scrubber process is being run * @return a Message if an error was encountered, otherwise null */ protected Message validateUniversityFiscalPeriodCode(OriginEntryInformation originEntry, OriginEntryInformation workingEntry, UniversityDate universityRunDate, AccountingCycleCachingService accountingCycleCachingService) { LOG.debug("validateUniversityFiscalPeriodCode() started"); String periodCode = originEntry.getUniversityFiscalPeriodCode(); if (!StringUtils.hasText(periodCode)) { if (universityRunDate.getAccountingPeriod().isOpen()) { workingEntry.setUniversityFiscalPeriodCode(universityRunDate.getUniversityFiscalAccountingPeriod()); workingEntry.setUniversityFiscalYear(universityRunDate.getUniversityFiscalYear()); } else { return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_ACCOUNTING_PERIOD_CLOSED, " (year " + universityRunDate.getUniversityFiscalYear() + ", period " + universityRunDate.getUniversityFiscalAccountingPeriod(), Message.TYPE_FATAL); } } else { AccountingPeriod originEntryAccountingPeriod = accountingCycleCachingService.getAccountingPeriod(originEntry.getUniversityFiscalYear(), originEntry.getUniversityFiscalPeriodCode()); if (originEntryAccountingPeriod == null) { return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_ACCOUNTING_PERIOD_NOT_FOUND, periodCode, Message.TYPE_FATAL); } else if (!originEntryAccountingPeriod.isActive()) { return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_ACCOUNTING_PERIOD_NOT_ACTIVE, periodCode, Message.TYPE_FATAL); } workingEntry.setUniversityFiscalPeriodCode(periodCode); } return null; } /** * If the encumbrance update code = R, ref doc number must exist, ref doc type must be valid and ref origin code must be valid. * If encumbrance update code is not R, and ref doc number is empty, make sure ref doc number, ref doc type and ref origin code * are null. If encumbrance update code is not R and the ref doc number has a value, ref doc type must be valid and ref origin * code must be valid. * * @param originEntry the origin entry to check * @param workingEntryInfo the copy of the entry to move valid data into * @return a Message if an error was encountered, otherwise null */ protected List<Message> validateReferenceDocumentFields(OriginEntryInformation originEntry, OriginEntryInformation workingEntry, AccountingCycleCachingService accountingCycleCachingService) { LOG.debug("validateReferenceDocument() started"); // 3148 of cobol List<Message> errors = new ArrayList(); boolean numberNullIndicator = !StringUtils.hasText(originEntry.getReferenceFinancialDocumentNumber()); boolean typeCodeNullIndicator = !StringUtils.hasText(originEntry.getReferenceFinancialDocumentTypeCode()); boolean originCodeNullIndicator = !StringUtils.hasText(originEntry.getReferenceFinancialSystemOriginationCode()); //TODO:- do we need this? boolean editReference = true; if (numberNullIndicator) { workingEntry.setReferenceFinancialDocumentNumber(null); workingEntry.setReferenceFinancialDocumentTypeCode(null); workingEntry.setReferenceFinancialSystemOriginationCode(null); if (KFSConstants.ENCUMB_UPDT_REFERENCE_DOCUMENT_CD.equals(originEntry.getTransactionEncumbranceUpdateCode())) { errors.add(MessageBuilder.buildMessage(KFSKeyConstants.ERROR_REF_DOC_NOT_BE_SPACE, Message.TYPE_FATAL)); } } else { workingEntry.setReferenceFinancialDocumentNumber(originEntry.getReferenceFinancialDocumentNumber()); if (!typeCodeNullIndicator){ if (accountingCycleCachingService.isCurrentActiveAccountingDocumentType(originEntry.getReferenceFinancialDocumentTypeCode())) { workingEntry.setReferenceFinancialDocumentTypeCode(originEntry.getReferenceFinancialDocumentTypeCode()); } else { errors.add(MessageBuilder.buildMessage(KFSKeyConstants.ERROR_REFERENCE_DOCUMENT_TYPE_NOT_FOUND, originEntry.getReferenceFinancialDocumentTypeCode(), Message.TYPE_FATAL)); } } else { errors.add(MessageBuilder.buildMessage(KFSKeyConstants.ERROR_REFERENCE_FIELDS, " " + KFSPropertyConstants.REFERENCE_FIN_DOCUMENT_TYPE_CODE + " is missing.", Message.TYPE_FATAL)); } if (!originCodeNullIndicator){ // Validate reference origin code OriginationCode oc = accountingCycleCachingService.getOriginationCode(originEntry.getFinancialSystemOriginationCode()); if (oc != null) { workingEntry.setReferenceFinancialSystemOriginationCode(originEntry.getReferenceFinancialSystemOriginationCode()); } else { errors.add(MessageBuilder.buildMessage(KFSKeyConstants.ERROR_REFERENCE_ORIGINATION_CODE_NOT_FOUND, " (" + originEntry.getReferenceFinancialSystemOriginationCode() + ")", Message.TYPE_FATAL)); } } else { errors.add(MessageBuilder.buildMessage(KFSKeyConstants.ERROR_REFERENCE_FIELDS, " " + KFSPropertyConstants.REFERENCE_FINANCIAL_SYSTEM_ORIGINATION_CODE + " is missing.", Message.TYPE_FATAL)); } } BalanceType workingEntryBalanceType = accountingCycleCachingService.getBalanceType(workingEntry.getFinancialBalanceTypeCode()); ObjectType workingEntryObjectType = accountingCycleCachingService.getObjectType(workingEntry.getFinancialObjectTypeCode()); if (workingEntryBalanceType == null || workingEntryObjectType == null) { // We are unable to check this because the balance type or object type is invalid. // It would be nice if we could still validate the entry, but we can't. return errors; } if (workingEntryBalanceType.isFinBalanceTypeEncumIndicator() && !workingEntryObjectType.isFundBalanceIndicator()) { if ( // KFSMI-5565 : Allow blank/null for encumbrance update code, since it is the same as "N" during processing and should not be an error org.apache.commons.lang.StringUtils.isBlank(originEntry.getTransactionEncumbranceUpdateCode()) || KFSConstants.ENCUMB_UPDT_DOCUMENT_CD.equals(originEntry.getTransactionEncumbranceUpdateCode()) || KFSConstants.ENCUMB_UPDT_NO_ENCUMBRANCE_CD.equals(originEntry.getTransactionEncumbranceUpdateCode()) || KFSConstants.ENCUMB_UPDT_REFERENCE_DOCUMENT_CD.equals(originEntry.getTransactionEncumbranceUpdateCode()) ) { workingEntry.setTransactionEncumbranceUpdateCode(originEntry.getTransactionEncumbranceUpdateCode()); } else { errors.add(MessageBuilder.buildMessage(KFSKeyConstants.ERROR_ENC_UPDATE_CODE_NOT_DRN, " (" + originEntry.getTransactionEncumbranceUpdateCode() + ")", Message.TYPE_FATAL)); } } else { workingEntry.setTransactionEncumbranceUpdateCode(null); } return errors; } /** * Validates the entry's transaction amount * * @param originEntry the origin entry being scrubbed * @param workingEntry the scrubbed version of the origin entry * @return a Message if an error was encountered, otherwise null */ protected Message validateTransactionAmount(OriginEntryInformation originEntry, OriginEntryInformation workingEntry, AccountingCycleCachingService accountingCycleCachingService) { LOG.debug("validateTransactionAmount() started"); KualiDecimal amount = originEntry.getTransactionLedgerEntryAmount(); BalanceType originEntryBalanceType = accountingCycleCachingService.getBalanceType(originEntry.getFinancialBalanceTypeCode()); if (originEntryBalanceType == null) { // We can't validate the amount without a balance type code return null; } if (originEntryBalanceType.isFinancialOffsetGenerationIndicator()) { if (amount.isPositive() || amount.isZero()) { workingEntry.setTransactionLedgerEntryAmount(originEntry.getTransactionLedgerEntryAmount()); } else { return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_NEGATIVE_AMOUNT, amount.toString(), Message.TYPE_FATAL); } if (StringHelper.isEmpty(originEntry.getTransactionDebitCreditCode())) { return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_DEBIT_CREDIT_INDICATOR_NEITHER_D_NOR_C, originEntry.getTransactionDebitCreditCode(), Message.TYPE_FATAL); } if ( debitOrCredit.contains(originEntry.getTransactionDebitCreditCode()) ) { workingEntry.setTransactionDebitCreditCode(originEntry.getTransactionDebitCreditCode()); } else { return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_DEBIT_CREDIT_INDICATOR_NEITHER_D_NOR_C, originEntry.getTransactionDebitCreditCode(), Message.TYPE_FATAL); } } else { if ((originEntry.getTransactionDebitCreditCode() == null) || (" ".equals(originEntry.getTransactionDebitCreditCode())) || ("".equals(originEntry.getTransactionDebitCreditCode()))) { workingEntry.setTransactionDebitCreditCode(KFSConstants.GL_BUDGET_CODE); } else { return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_DEBIT_CREDIT_INDICATOR_MUST_BE_SPACE, originEntry.getTransactionDebitCreditCode(), Message.TYPE_FATAL); } } return null; } protected Message validateDescription(OriginEntryInformation originEntry){ if (originEntry.getTransactionLedgerEntryDescription().trim().equals(KFSConstants.EMPTY_STRING)){ return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_DESCRIPTION_CANNOT_BE_BLANK, Message.TYPE_FATAL); } return null; } /** * @see org.kuali.kfs.gl.service.ScrubberValidator#isAccountExpired(org.kuali.kfs.coa.businessobject.Account, org.kuali.kfs.sys.businessobject.UniversityDate) */ @Override public boolean isAccountExpired(Account account, UniversityDate universityRunDate) { if (account.getAccountExpirationDate() == null) { return false; } Calendar runCalendar = Calendar.getInstance(); runCalendar.setTime(universityRunDate.getUniversityDate()); Calendar expirationDate = Calendar.getInstance(); long offsetAccountExpirationTime = getAdjustedAccountExpirationDate(account); expirationDate.setTimeInMillis(offsetAccountExpirationTime); int expirationYear = expirationDate.get(Calendar.YEAR); int runYear = runCalendar.get(Calendar.YEAR); int expirationDoy = expirationDate.get(Calendar.DAY_OF_YEAR); int runDoy = runCalendar.get(Calendar.DAY_OF_YEAR); return (expirationYear < runYear) || (expirationYear == runYear && expirationDoy < runDoy); } public void setUniversityDateDao(UniversityDateDao udd) { universityDateDao = udd; } public void setConfigurationService(ConfigurationService service) { kualiConfigurationService = service; } public void setPersistenceService(PersistenceService ps) { persistenceService = ps; } public void setAccountService(AccountService as) { accountService = as; } public void setOriginationCodeService(OriginationCodeService ocs) { originationCodeService = ocs; } public void setPersistenceStructureService(PersistenceStructureService persistenceStructureService) { this.persistenceStructureService = persistenceStructureService; } public void setParameterService(ParameterService parameterService) { this.parameterService = parameterService; } public void setBalanceTypService(BalanceTypeService balanceTypService) { this.balanceTypService = balanceTypService; } }