/*
* 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.module.ld.document.validation.impl;
import java.util.Collection;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.kuali.kfs.coa.businessobject.Account;
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.SubAccount;
import org.kuali.kfs.coa.businessobject.SubObjectCode;
import org.kuali.kfs.module.ld.LaborKeyConstants;
import org.kuali.kfs.module.ld.batch.service.LaborAccountingCycleCachingService;
import org.kuali.kfs.module.ld.businessobject.LaborOriginEntry;
import org.kuali.kfs.module.ld.businessobject.LaborTransaction;
import org.kuali.kfs.sys.KFSConstants;
import org.kuali.kfs.sys.KFSKeyConstants;
import org.kuali.kfs.sys.Message;
import org.kuali.kfs.sys.MessageBuilder;
import org.kuali.kfs.sys.businessobject.SystemOptions;
import org.kuali.kfs.sys.context.SpringContext;
import org.kuali.rice.core.api.config.property.ConfigurationService;
import org.kuali.rice.core.api.util.type.KualiDecimal;
import org.kuali.rice.krad.util.ObjectUtils;
/**
* This class provides a set of utilities that can be used to validate a transaction in the field level.
*/
public class TransactionFieldValidator {
private static LaborAccountingCycleCachingService accountingCycleCachingService;
private static ConfigurationService kualiConfigurationService;
/**
* Checks if the given transaction contains valid university fiscal year
*
* @param transaction the given transaction
* @return null if the university fiscal year is valid; otherwise, return error message
*/
public static Message checkUniversityFiscalYear(LaborTransaction transaction) {
Integer fiscalYear = transaction.getUniversityFiscalYear();
if (fiscalYear == null) {
return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_UNIV_FISCAL_YR_NOT_FOUND, Message.TYPE_FATAL);
}
else {
SystemOptions option = getAccountingCycleCachingService().getSystemOptions(((LaborOriginEntry) transaction).getUniversityFiscalYear());
if (ObjectUtils.isNull(option)) {
return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_UNIV_FISCAL_YR_NOT_FOUND, fiscalYear.toString(), Message.TYPE_FATAL);
}
}
return null;
}
/**
* Checks if the given transaction contains valid char of accounts code
*
* @param transaction the given transaction
* @return null if the char of accounts code is valid; otherwise, return error message
*/
public static Message checkChartOfAccountsCode(LaborTransaction transaction) {
String chartOfAccountsCode = transaction.getChartOfAccountsCode();
Chart chart = getAccountingCycleCachingService().getChart(((LaborOriginEntry) transaction).getChartOfAccountsCode());
if (StringUtils.isBlank(chartOfAccountsCode) || ObjectUtils.isNull(chart)) {
return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_CHART_NOT_FOUND, chartOfAccountsCode, Message.TYPE_FATAL);
}
if (!chart.isActive()) {
return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_CHART_NOT_ACTIVE, chartOfAccountsCode, Message.TYPE_FATAL);
}
return null;
}
/**
* Checks if the given transaction contains valid account number
*
* @param transaction the given transaction
* @return null if the account number is valid; otherwise, return error message
*/
public static Message checkAccountNumber(LaborTransaction transaction) {
String accountNumber = transaction.getAccountNumber();
Account account = getAccountingCycleCachingService().getAccount(((LaborOriginEntry) transaction).getChartOfAccountsCode(), ((LaborOriginEntry) transaction).getAccountNumber());
if (StringUtils.isBlank(accountNumber) || ObjectUtils.isNull(account)) {
String chartOfAccountsCode = transaction.getChartOfAccountsCode();
String accountKey = chartOfAccountsCode + "-" + accountNumber;
return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_ACCOUNT_NOT_FOUND, accountKey, Message.TYPE_FATAL);
}
return null;
}
/**
* Checks if the given transaction contains valid sub account number
*
* @param transaction the given transaction
* @return null if the sub account number is valid; otherwise, return error message
*/
public static Message checkSubAccountNumber(LaborTransaction transaction) {
return checkSubAccountNumber(transaction, null);
}
/**
* Checks if the given transaction contains valid sub account number
*
* @param transaction the given transaction
* @param exclusiveDocumentTypeCode inactive sub account can be OK if the document type of the given transaction is
* exclusiveDocumentTypeCode
* @return null if the sub account number is valid; otherwise, return error message
*/
public static Message checkSubAccountNumber(LaborTransaction transaction, String exclusiveDocumentTypeCode) {
String subAccountNumber = transaction.getSubAccountNumber();
String chartOfAccountsCode = transaction.getChartOfAccountsCode();
String accountNumber = transaction.getAccountNumber();
String documentTypeCode = transaction.getFinancialDocumentTypeCode();
String subAccountKey = chartOfAccountsCode + "-" + accountNumber + "-" + subAccountNumber;
SubAccount subAccount = getAccountingCycleCachingService().getSubAccount(((LaborOriginEntry) transaction).getChartOfAccountsCode(), ((LaborOriginEntry) transaction).getAccountNumber(), ((LaborOriginEntry) transaction).getSubAccountNumber());
if (StringUtils.isBlank(subAccountNumber)) {
return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_SUB_ACCOUNT_NOT_FOUND, subAccountKey, Message.TYPE_FATAL);
}
if (!KFSConstants.getDashSubAccountNumber().equals(subAccountNumber)) {
if (ObjectUtils.isNull(subAccount)) {
return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_SUB_ACCOUNT_NOT_FOUND, subAccountKey, Message.TYPE_FATAL);
}
if (!StringUtils.equals(documentTypeCode, exclusiveDocumentTypeCode)) {
if (!subAccount.isActive()) {
return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_SUB_ACCOUNT_NOT_ACTIVE, subAccountKey, Message.TYPE_FATAL);
}
}
}
return null;
}
/**
* Checks if the given transaction contains valid account number
*
* @param transaction the given transaction
* @return null if the account number is valid; otherwise, return error message
*/
public static Message checkFinancialObjectCode(LaborTransaction transaction) {
String objectCode = transaction.getFinancialObjectCode();
if (StringUtils.isBlank(objectCode)) {
return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_OBJECT_CODE_EMPTY, Message.TYPE_FATAL);
}
Integer fiscalYear = transaction.getUniversityFiscalYear();
String chartOfAccountsCode = transaction.getChartOfAccountsCode();
String objectCodeKey = fiscalYear + "-" + chartOfAccountsCode + "-" + objectCode;
ObjectCode financialObject = getAccountingCycleCachingService().getObjectCode(((LaborOriginEntry) transaction).getUniversityFiscalYear(), ((LaborOriginEntry) transaction).getChartOfAccountsCode(), ((LaborOriginEntry) transaction).getFinancialObjectCode());
//do we need it?
transaction.refreshNonUpdateableReferences();
if (ObjectUtils.isNull(financialObject)) {
return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_OBJECT_CODE_NOT_FOUND, objectCodeKey, Message.TYPE_FATAL);
}
return null;
}
/**
* Checks if the given transaction contains valid sub object code
*
* @param transaction the given transaction
* @return null if the sub object code is valid; otherwise, return error message
*/
public static Message checkFinancialSubObjectCode(LaborTransaction transaction) {
Integer fiscalYear = transaction.getUniversityFiscalYear();
String chartOfAccountsCode = transaction.getChartOfAccountsCode();
String objectCode = transaction.getFinancialObjectCode();
String subObjectCode = transaction.getFinancialSubObjectCode();
String subObjectCodeKey = fiscalYear + "-" + chartOfAccountsCode + "-" + objectCode + "-" + subObjectCode;
if (StringUtils.isBlank(subObjectCode)) {
return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_SUB_OBJECT_CODE_NOT_BE_NULL, subObjectCodeKey, Message.TYPE_FATAL);
}
SubObjectCode financialSubObject = getAccountingCycleCachingService().getSubObjectCode(((LaborOriginEntry) transaction).getUniversityFiscalYear(), ((LaborOriginEntry) transaction).getChartOfAccountsCode(), ((LaborOriginEntry) transaction).getAccountNumber(), ((LaborOriginEntry) transaction).getFinancialObjectCode(), ((LaborOriginEntry) transaction).getFinancialSubObjectCode());
if (!KFSConstants.getDashFinancialSubObjectCode().equals(subObjectCode)) {
if (ObjectUtils.isNull(financialSubObject)) {
return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_SUB_OBJECT_CODE_NOT_BE_NULL, subObjectCodeKey, Message.TYPE_FATAL);
}
}
return null;
}
/**
* Checks if the given transaction contains valid balance type code
*
* @param transaction the given transaction
* @return null if the balance type code is valid; otherwise, return error message
*/
public static Message checkFinancialBalanceTypeCode(LaborTransaction transaction) {
String balanceTypeCode = transaction.getFinancialBalanceTypeCode();
BalanceType balanceType = getAccountingCycleCachingService().getBalanceType(((LaborOriginEntry) transaction).getFinancialBalanceTypeCode());
if (StringUtils.isBlank(balanceTypeCode) || ObjectUtils.isNull(balanceType)) {
return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_BALANCE_TYPE_NOT_FOUND, balanceTypeCode, Message.TYPE_FATAL);
}
return null;
}
/**
* Checks if the given transaction contains valid object type code
*
* @param transaction the given transaction
* @return null if the object type code is valid; otherwise, return error message
*/
public static Message checkFinancialObjectTypeCode(LaborTransaction transaction) {
String objectTypeCode = transaction.getFinancialObjectTypeCode();
ObjectType objectType = getAccountingCycleCachingService().getObjectType(((LaborOriginEntry) transaction).getFinancialObjectTypeCode());
if (StringUtils.isBlank(objectTypeCode) || ObjectUtils.isNull(objectType)) {
return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_OBJECT_TYPE_NOT_FOUND, objectTypeCode, Message.TYPE_FATAL);
}
return null;
}
/**
* Checks if the given transaction contains university fiscal period code
*
* @param transaction the given transaction
* @return null if the university fiscal period code is valid; otherwise, return error message
*/
public static Message checkUniversityFiscalPeriodCode(LaborTransaction transaction) {
String fiscalPeriodCode = transaction.getUniversityFiscalPeriodCode();
if (StringUtils.isBlank(fiscalPeriodCode)) {
return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_ACCOUNTING_PERIOD_NOT_FOUND, fiscalPeriodCode, Message.TYPE_FATAL);
}
return null;
}
/**
* Checks if the given transaction contains document type code
*
* @param transaction the given transaction
* @return null if the document type code is valid; otherwise, return error message
*/
public static Message checkFinancialDocumentTypeCode(LaborTransaction transaction) {
if (StringUtils.isBlank(transaction.getFinancialDocumentTypeCode()) || !getAccountingCycleCachingService().isCurrentActiveAccountingDocumentType(transaction.getFinancialDocumentTypeCode())) {
return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_DOCUMENT_TYPE_NOT_FOUND, transaction.getFinancialDocumentTypeCode(), Message.TYPE_FATAL);
}
return null;
}
/**
* Checks if the given transaction contains document number
*
* @param transaction the given transaction
* @return null if the document number is valid; otherwise, return error message
*/
public static Message checkFinancialDocumentNumber(LaborTransaction transaction) {
String documentNumber = transaction.getDocumentNumber();
if (StringUtils.isBlank(documentNumber)) {
return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_DOCUMENT_NUMBER_REQUIRED, Message.TYPE_FATAL);
}
return null;
}
/**
* Checks if the given transaction contains transaction sequence number
*
* @param transaction the given transaction
* @return null if the transaction sequence number is valid; otherwise, return error message
*/
// Don't need to check SequenceNumber because it sets in each poster (LaborLedgerEntryPoster and LaborGLLedgerEntryPoster), so commented out
// public static Message checkTransactionLedgerEntrySequenceNumber(LaborTransaction transaction) {
// Integer sequenceNumber = transaction.getTransactionLedgerEntrySequenceNumber();
// if (sequenceNumber == null) {
// return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_SEQUENCE_NUMBER_NOT_BE_NULL, Message.TYPE_FATAL);
// }
// return null;
// }
/**
* Checks if the given transaction contains debit credit code
*
* @param transaction the given transaction
* @return null if the debit credit code is valid; otherwise, return error message
*/
public static Message checkTransactionDebitCreditCode(LaborTransaction transaction) {
String[] validDebitCreditCode = { KFSConstants.GL_BUDGET_CODE, KFSConstants.GL_CREDIT_CODE, KFSConstants.GL_DEBIT_CODE };
String debitCreditCode = transaction.getTransactionDebitCreditCode();
if (debitCreditCode == null || !ArrayUtils.contains(validDebitCreditCode, debitCreditCode)) {
return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_DEDIT_CREDIT_CODE_NOT_BE_NULL, Message.TYPE_FATAL);
} else if (transaction.getBalanceType().isFinancialOffsetGenerationIndicator() && !KFSConstants.GL_DEBIT_CODE.equals(transaction.getTransactionDebitCreditCode()) && !KFSConstants.GL_CREDIT_CODE.equals(transaction.getTransactionDebitCreditCode())) {
return new Message(getConfigurationService().getPropertyValueAsString(KFSKeyConstants.MSG_DEDIT_CREDIT_CODE_MUST_BE) + " '" + KFSConstants.GL_DEBIT_CODE + " or " + KFSConstants.GL_CREDIT_CODE + getConfigurationService().getPropertyValueAsString(KFSKeyConstants.MSG_FOR_BALANCE_TYPE), Message.TYPE_FATAL);
}
return null;
}
/**
* Checks if the given transaction contains system origination code
*
* @param transaction the given transaction
* @return null if the system origination code is valid; otherwise, return error message
*/
public static Message checkFinancialSystemOriginationCode(LaborTransaction transaction) {
String originationCode = transaction.getFinancialSystemOriginationCode();
if (StringUtils.isBlank(originationCode)) {
return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_ORIGIN_CODE_NOT_FOUND, Message.TYPE_FATAL);
}
return null;
}
/**
* Checks if the given transaction contains the posteable period code
*
* @param transaction the given transaction
* @param unpostableperidCodes the list of unpostable period code
* @return null if the perid code of the transaction is not in unpostableperidCodes; otherwise, return error message
*/
public static Message checkPostablePeridCode(LaborTransaction transaction, Collection<String> unpostableperidCodes) {
String periodCode = transaction.getUniversityFiscalPeriodCode();
if (unpostableperidCodes.contains(periodCode)) {
return MessageBuilder.buildMessage(LaborKeyConstants.ERROR_UNPOSTABLE_PERIOD_CODE, periodCode, Message.TYPE_FATAL);
}
return null;
}
/**
* Checks if the given transaction contains the posteable balance type code
*
* @param transaction the given transaction
* @param unpostableBalanceTypeCodes the list of unpostable balance type codes
* @return null if the balance type code of the transaction is not in unpostableBalanceTypeCodes; otherwise, return error
* message
*/
public static Message checkPostableBalanceTypeCode(LaborTransaction transaction, Collection<String> unpostableBalanceTypeCodes) {
String balanceTypeCode = transaction.getFinancialBalanceTypeCode();
if (unpostableBalanceTypeCodes.contains(balanceTypeCode)) {
return MessageBuilder.buildMessage(LaborKeyConstants.ERROR_UNPOSTABLE_BALANCE_TYPE, balanceTypeCode, Message.TYPE_FATAL);
}
return null;
}
/**
* Checks if the transaction amount of the given transaction is ZERO
*
* @param transaction the given transaction
* @return null if the transaction amount is not ZERO or null; otherwise, return error message
*/
public static Message checkZeroTotalAmount(LaborTransaction transaction) {
KualiDecimal amount = transaction.getTransactionLedgerEntryAmount();
if (amount == null || amount.isZero()) {
return MessageBuilder.buildMessage(LaborKeyConstants.ERROR_ZERO_TOTAL_AMOUNT, Message.TYPE_FATAL);
}
return null;
}
/**
* Checks if the given transaction contains the valid employee id
*
* @param transaction the given transaction
* @param unpostableObjectCodes the list of unpostable object codes
* @return null if the object code of the transaction is not in unpostableObjectCodes; otherwise, return error message
*/
public static Message checkEmplid(LaborTransaction transaction) {
String emplid = transaction.getEmplid();
if (StringUtils.isBlank(emplid)) {
return MessageBuilder.buildMessage(LaborKeyConstants.MISSING_EMPLOYEE_ID, Message.TYPE_FATAL);
}
return null;
}
/**
* When in Rome... This method checks if the encumbrance update code is valid
* @param transaction the transaction to check
* @return a Message if the encumbrance update code is not valid, or null if all is well
*/
public static Message checkEncumbranceUpdateCode(LaborTransaction transaction) {
// The encumbrance update code can only be space, N, R or D. Nothing else
if ((StringUtils.isNotBlank(transaction.getTransactionEncumbranceUpdateCode())) && (!" ".equals(transaction.getTransactionEncumbranceUpdateCode())) && (!KFSConstants.ENCUMB_UPDT_NO_ENCUMBRANCE_CD.equals(transaction.getTransactionEncumbranceUpdateCode())) && (!KFSConstants.ENCUMB_UPDT_REFERENCE_DOCUMENT_CD.equals(transaction.getTransactionEncumbranceUpdateCode())) && (!KFSConstants.ENCUMB_UPDT_DOCUMENT_CD.equals(transaction.getTransactionEncumbranceUpdateCode()))) {
return new Message("Invalid Encumbrance Update Code (" + transaction.getTransactionEncumbranceUpdateCode() + ")", Message.TYPE_FATAL);
}
return null;
}
static LaborAccountingCycleCachingService getAccountingCycleCachingService() {
if (accountingCycleCachingService == null) {
accountingCycleCachingService = SpringContext.getBean(LaborAccountingCycleCachingService.class);
}
return accountingCycleCachingService;
}
static ConfigurationService getConfigurationService() {
if (kualiConfigurationService == null) {
kualiConfigurationService = SpringContext.getBean(ConfigurationService.class);
}
return kualiConfigurationService;
}
}