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.document.validation.impl; 017 018 019 import java.util.List; 020 021 import org.apache.commons.lang.ArrayUtils; 022 import org.apache.commons.lang.StringUtils; 023 import org.kuali.kfs.coa.businessobject.Account; 024 import org.kuali.kfs.coa.businessobject.BalanceType; 025 import org.kuali.kfs.coa.businessobject.Chart; 026 import org.kuali.kfs.coa.businessobject.ObjectCode; 027 import org.kuali.kfs.coa.businessobject.ObjectType; 028 import org.kuali.kfs.coa.businessobject.SubAccount; 029 import org.kuali.kfs.coa.businessobject.SubObjectCode; 030 import org.kuali.kfs.module.ld.LaborKeyConstants; 031 import org.kuali.kfs.module.ld.batch.service.LaborAccountingCycleCachingService; 032 import org.kuali.kfs.module.ld.businessobject.LaborOriginEntry; 033 import org.kuali.kfs.module.ld.businessobject.LaborTransaction; 034 import org.kuali.kfs.sys.KFSConstants; 035 import org.kuali.kfs.sys.KFSKeyConstants; 036 import org.kuali.kfs.sys.Message; 037 import org.kuali.kfs.sys.MessageBuilder; 038 import org.kuali.kfs.sys.businessobject.SystemOptions; 039 import org.kuali.kfs.sys.context.SpringContext; 040 import org.kuali.rice.kns.service.KualiConfigurationService; 041 import org.kuali.rice.kns.util.KualiDecimal; 042 import org.kuali.rice.kns.util.ObjectUtils; 043 044 /** 045 * This class provides a set of utilities that can be used to validate a transaction in the field level. 046 */ 047 public class TransactionFieldValidator { 048 private static LaborAccountingCycleCachingService accountingCycleCachingService; 049 private static KualiConfigurationService kualiConfigurationService; 050 051 /** 052 * Checks if the given transaction contains valid university fiscal year 053 * 054 * @param transaction the given transaction 055 * @return null if the university fiscal year is valid; otherwise, return error message 056 */ 057 public static Message checkUniversityFiscalYear(LaborTransaction transaction) { 058 059 Integer fiscalYear = transaction.getUniversityFiscalYear(); 060 if (fiscalYear == null) { 061 return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_UNIV_FISCAL_YR_NOT_FOUND, Message.TYPE_FATAL); 062 } 063 064 else { 065 SystemOptions option = getAccountingCycleCachingService().getSystemOptions(((LaborOriginEntry) transaction).getUniversityFiscalYear()); 066 if (ObjectUtils.isNull(option)) { 067 return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_UNIV_FISCAL_YR_NOT_FOUND, fiscalYear.toString(), Message.TYPE_FATAL); 068 } 069 } 070 return null; 071 } 072 073 /** 074 * Checks if the given transaction contains valid char of accounts code 075 * 076 * @param transaction the given transaction 077 * @return null if the char of accounts code is valid; otherwise, return error message 078 */ 079 public static Message checkChartOfAccountsCode(LaborTransaction transaction) { 080 String chartOfAccountsCode = transaction.getChartOfAccountsCode(); 081 Chart chart = getAccountingCycleCachingService().getChart(((LaborOriginEntry) transaction).getChartOfAccountsCode()); 082 if (StringUtils.isBlank(chartOfAccountsCode) || ObjectUtils.isNull(chart)) { 083 return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_CHART_NOT_FOUND, chartOfAccountsCode, Message.TYPE_FATAL); 084 } 085 086 if (!chart.isActive()) { 087 return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_CHART_NOT_ACTIVE, chartOfAccountsCode, Message.TYPE_FATAL); 088 } 089 return null; 090 } 091 092 /** 093 * Checks if the given transaction contains valid account number 094 * 095 * @param transaction the given transaction 096 * @return null if the account number is valid; otherwise, return error message 097 */ 098 public static Message checkAccountNumber(LaborTransaction transaction) { 099 String accountNumber = transaction.getAccountNumber(); 100 Account account = getAccountingCycleCachingService().getAccount(((LaborOriginEntry) transaction).getChartOfAccountsCode(), ((LaborOriginEntry) transaction).getAccountNumber()); 101 if (StringUtils.isBlank(accountNumber) || ObjectUtils.isNull(account)) { 102 String chartOfAccountsCode = transaction.getChartOfAccountsCode(); 103 String accountKey = chartOfAccountsCode + "-" + accountNumber; 104 return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_ACCOUNT_NOT_FOUND, accountKey, Message.TYPE_FATAL); 105 } 106 return null; 107 } 108 109 /** 110 * Checks if the given transaction contains valid sub account number 111 * 112 * @param transaction the given transaction 113 * @return null if the sub account number is valid; otherwise, return error message 114 */ 115 public static Message checkSubAccountNumber(LaborTransaction transaction) { 116 return checkSubAccountNumber(transaction, null); 117 } 118 119 /** 120 * Checks if the given transaction contains valid sub account number 121 * 122 * @param transaction the given transaction 123 * @param exclusiveDocumentTypeCode inactive sub account can be OK if the document type of the given transaction is 124 * exclusiveDocumentTypeCode 125 * @return null if the sub account number is valid; otherwise, return error message 126 */ 127 public static Message checkSubAccountNumber(LaborTransaction transaction, String exclusiveDocumentTypeCode) { 128 String subAccountNumber = transaction.getSubAccountNumber(); 129 String chartOfAccountsCode = transaction.getChartOfAccountsCode(); 130 String accountNumber = transaction.getAccountNumber(); 131 String documentTypeCode = transaction.getFinancialDocumentTypeCode(); 132 String subAccountKey = chartOfAccountsCode + "-" + accountNumber + "-" + subAccountNumber; 133 SubAccount subAccount = getAccountingCycleCachingService().getSubAccount(((LaborOriginEntry) transaction).getChartOfAccountsCode(), ((LaborOriginEntry) transaction).getAccountNumber(), ((LaborOriginEntry) transaction).getSubAccountNumber()); 134 135 if (StringUtils.isBlank(subAccountNumber)) { 136 return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_SUB_ACCOUNT_NOT_FOUND, subAccountKey, Message.TYPE_FATAL); 137 } 138 139 if (!KFSConstants.getDashSubAccountNumber().equals(subAccountNumber)) { 140 if (ObjectUtils.isNull(subAccount)) { 141 return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_SUB_ACCOUNT_NOT_FOUND, subAccountKey, Message.TYPE_FATAL); 142 } 143 144 if (!StringUtils.equals(documentTypeCode, exclusiveDocumentTypeCode)) { 145 if (!subAccount.isActive()) { 146 return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_SUB_ACCOUNT_NOT_ACTIVE, subAccountKey, Message.TYPE_FATAL); 147 } 148 } 149 } 150 return null; 151 } 152 153 /** 154 * Checks if the given transaction contains valid account number 155 * 156 * @param transaction the given transaction 157 * @return null if the account number is valid; otherwise, return error message 158 */ 159 public static Message checkFinancialObjectCode(LaborTransaction transaction) { 160 String objectCode = transaction.getFinancialObjectCode(); 161 if (StringUtils.isBlank(objectCode)) { 162 return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_OBJECT_CODE_EMPTY, Message.TYPE_FATAL); 163 } 164 165 Integer fiscalYear = transaction.getUniversityFiscalYear(); 166 String chartOfAccountsCode = transaction.getChartOfAccountsCode(); 167 String objectCodeKey = fiscalYear + "-" + chartOfAccountsCode + "-" + objectCode; 168 ObjectCode financialObject = getAccountingCycleCachingService().getObjectCode(((LaborOriginEntry) transaction).getUniversityFiscalYear(), ((LaborOriginEntry) transaction).getChartOfAccountsCode(), ((LaborOriginEntry) transaction).getFinancialObjectCode()); 169 170 //do we need it? 171 transaction.refreshNonUpdateableReferences(); 172 173 if (ObjectUtils.isNull(financialObject)) { 174 return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_OBJECT_CODE_NOT_FOUND, objectCodeKey, Message.TYPE_FATAL); 175 } 176 return null; 177 } 178 179 /** 180 * Checks if the given transaction contains valid sub object code 181 * 182 * @param transaction the given transaction 183 * @return null if the sub object code is valid; otherwise, return error message 184 */ 185 public static Message checkFinancialSubObjectCode(LaborTransaction transaction) { 186 Integer fiscalYear = transaction.getUniversityFiscalYear(); 187 String chartOfAccountsCode = transaction.getChartOfAccountsCode(); 188 String objectCode = transaction.getFinancialObjectCode(); 189 String subObjectCode = transaction.getFinancialSubObjectCode(); 190 191 String subObjectCodeKey = fiscalYear + "-" + chartOfAccountsCode + "-" + objectCode + "-" + subObjectCode; 192 if (StringUtils.isBlank(subObjectCode)) { 193 return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_SUB_OBJECT_CODE_NOT_BE_NULL, subObjectCodeKey, Message.TYPE_FATAL); 194 } 195 SubObjectCode financialSubObject = getAccountingCycleCachingService().getSubObjectCode(((LaborOriginEntry) transaction).getUniversityFiscalYear(), ((LaborOriginEntry) transaction).getChartOfAccountsCode(), ((LaborOriginEntry) transaction).getAccountNumber(), ((LaborOriginEntry) transaction).getFinancialObjectCode(), ((LaborOriginEntry) transaction).getFinancialSubObjectCode()); 196 if (!KFSConstants.getDashFinancialSubObjectCode().equals(subObjectCode)) { 197 if (ObjectUtils.isNull(financialSubObject)) { 198 return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_SUB_OBJECT_CODE_NOT_BE_NULL, subObjectCodeKey, Message.TYPE_FATAL); 199 } 200 } 201 return null; 202 } 203 204 /** 205 * Checks if the given transaction contains valid balance type code 206 * 207 * @param transaction the given transaction 208 * @return null if the balance type code is valid; otherwise, return error message 209 */ 210 public static Message checkFinancialBalanceTypeCode(LaborTransaction transaction) { 211 String balanceTypeCode = transaction.getFinancialBalanceTypeCode(); 212 BalanceType balanceType = getAccountingCycleCachingService().getBalanceType(((LaborOriginEntry) transaction).getFinancialBalanceTypeCode()); 213 if (StringUtils.isBlank(balanceTypeCode) || ObjectUtils.isNull(balanceType)) { 214 return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_BALANCE_TYPE_NOT_FOUND, balanceTypeCode, Message.TYPE_FATAL); 215 } 216 return null; 217 } 218 219 /** 220 * Checks if the given transaction contains valid object type code 221 * 222 * @param transaction the given transaction 223 * @return null if the object type code is valid; otherwise, return error message 224 */ 225 public static Message checkFinancialObjectTypeCode(LaborTransaction transaction) { 226 String objectTypeCode = transaction.getFinancialObjectTypeCode(); 227 ObjectType objectType = getAccountingCycleCachingService().getObjectType(((LaborOriginEntry) transaction).getFinancialObjectTypeCode()); 228 if (StringUtils.isBlank(objectTypeCode) || ObjectUtils.isNull(objectType)) { 229 return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_OBJECT_TYPE_NOT_FOUND, objectTypeCode, Message.TYPE_FATAL); 230 } 231 return null; 232 } 233 234 /** 235 * Checks if the given transaction contains university fiscal period code 236 * 237 * @param transaction the given transaction 238 * @return null if the university fiscal period code is valid; otherwise, return error message 239 */ 240 public static Message checkUniversityFiscalPeriodCode(LaborTransaction transaction) { 241 String fiscalPeriodCode = transaction.getUniversityFiscalPeriodCode(); 242 if (StringUtils.isBlank(fiscalPeriodCode)) { 243 return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_ACCOUNTING_PERIOD_NOT_FOUND, fiscalPeriodCode, Message.TYPE_FATAL); 244 } 245 return null; 246 } 247 248 /** 249 * Checks if the given transaction contains document type code 250 * 251 * @param transaction the given transaction 252 * @return null if the document type code is valid; otherwise, return error message 253 */ 254 public static Message checkFinancialDocumentTypeCode(LaborTransaction transaction) { 255 if (StringUtils.isBlank(transaction.getFinancialDocumentTypeCode()) || !getAccountingCycleCachingService().isCurrentActiveAccountingDocumentType(transaction.getFinancialDocumentTypeCode())) { 256 return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_DOCUMENT_TYPE_NOT_FOUND, transaction.getFinancialDocumentTypeCode(), Message.TYPE_FATAL); 257 } 258 return null; 259 } 260 261 /** 262 * Checks if the given transaction contains document number 263 * 264 * @param transaction the given transaction 265 * @return null if the document number is valid; otherwise, return error message 266 */ 267 public static Message checkFinancialDocumentNumber(LaborTransaction transaction) { 268 String documentNumber = transaction.getDocumentNumber(); 269 if (StringUtils.isBlank(documentNumber)) { 270 return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_DOCUMENT_NUMBER_REQUIRED, Message.TYPE_FATAL); 271 } 272 return null; 273 } 274 275 /** 276 * Checks if the given transaction contains transaction sequence number 277 * 278 * @param transaction the given transaction 279 * @return null if the transaction sequence number is valid; otherwise, return error message 280 */ 281 // Don't need to check SequenceNumber because it sets in each poster (LaborLedgerEntryPoster and LaborGLLedgerEntryPoster), so commented out 282 // public static Message checkTransactionLedgerEntrySequenceNumber(LaborTransaction transaction) { 283 // Integer sequenceNumber = transaction.getTransactionLedgerEntrySequenceNumber(); 284 // if (sequenceNumber == null) { 285 // return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_SEQUENCE_NUMBER_NOT_BE_NULL, Message.TYPE_FATAL); 286 // } 287 // return null; 288 // } 289 290 /** 291 * Checks if the given transaction contains debit credit code 292 * 293 * @param transaction the given transaction 294 * @return null if the debit credit code is valid; otherwise, return error message 295 */ 296 public static Message checkTransactionDebitCreditCode(LaborTransaction transaction) { 297 String[] validDebitCreditCode = { KFSConstants.GL_BUDGET_CODE, KFSConstants.GL_CREDIT_CODE, KFSConstants.GL_DEBIT_CODE }; 298 String debitCreditCode = transaction.getTransactionDebitCreditCode(); 299 if (debitCreditCode == null || !ArrayUtils.contains(validDebitCreditCode, debitCreditCode)) { 300 return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_DEDIT_CREDIT_CODE_NOT_BE_NULL, Message.TYPE_FATAL); 301 } else if (transaction.getBalanceType().isFinancialOffsetGenerationIndicator() && !KFSConstants.GL_DEBIT_CODE.equals(transaction.getTransactionDebitCreditCode()) && !KFSConstants.GL_CREDIT_CODE.equals(transaction.getTransactionDebitCreditCode())) { 302 return new Message(getKualiConfigurationService().getPropertyString(KFSKeyConstants.MSG_DEDIT_CREDIT_CODE_MUST_BE) + " '" + KFSConstants.GL_DEBIT_CODE + " or " + KFSConstants.GL_CREDIT_CODE + getKualiConfigurationService().getPropertyString(KFSKeyConstants.MSG_FOR_BALANCE_TYPE), Message.TYPE_FATAL); 303 } 304 return null; 305 } 306 307 /** 308 * Checks if the given transaction contains system origination code 309 * 310 * @param transaction the given transaction 311 * @return null if the system origination code is valid; otherwise, return error message 312 */ 313 public static Message checkFinancialSystemOriginationCode(LaborTransaction transaction) { 314 String originationCode = transaction.getFinancialSystemOriginationCode(); 315 if (StringUtils.isBlank(originationCode)) { 316 return MessageBuilder.buildMessage(KFSKeyConstants.ERROR_ORIGIN_CODE_NOT_FOUND, Message.TYPE_FATAL); 317 } 318 return null; 319 } 320 321 /** 322 * Checks if the given transaction contains the posteable period code 323 * 324 * @param transaction the given transaction 325 * @param unpostableperidCodes the list of unpostable period code 326 * @return null if the perid code of the transaction is not in unpostableperidCodes; otherwise, return error message 327 */ 328 public static Message checkPostablePeridCode(LaborTransaction transaction, List<String> unpostableperidCodes) { 329 String periodCode = transaction.getUniversityFiscalPeriodCode(); 330 if (unpostableperidCodes.contains(periodCode)) { 331 return MessageBuilder.buildMessage(LaborKeyConstants.ERROR_UNPOSTABLE_PERIOD_CODE, periodCode, Message.TYPE_FATAL); 332 } 333 return null; 334 } 335 336 /** 337 * Checks if the given transaction contains the posteable balance type code 338 * 339 * @param transaction the given transaction 340 * @param unpostableBalanceTypeCodes the list of unpostable balance type codes 341 * @return null if the balance type code of the transaction is not in unpostableBalanceTypeCodes; otherwise, return error 342 * message 343 */ 344 public static Message checkPostableBalanceTypeCode(LaborTransaction transaction, List<String> unpostableBalanceTypeCodes) { 345 String balanceTypeCode = transaction.getFinancialBalanceTypeCode(); 346 if (unpostableBalanceTypeCodes.contains(balanceTypeCode)) { 347 return MessageBuilder.buildMessage(LaborKeyConstants.ERROR_UNPOSTABLE_BALANCE_TYPE, balanceTypeCode, Message.TYPE_FATAL); 348 } 349 return null; 350 } 351 352 /** 353 * Checks if the transaction amount of the given transaction is ZERO 354 * 355 * @param transaction the given transaction 356 * @return null if the transaction amount is not ZERO or null; otherwise, return error message 357 */ 358 public static Message checkZeroTotalAmount(LaborTransaction transaction) { 359 KualiDecimal amount = transaction.getTransactionLedgerEntryAmount(); 360 if (amount == null || amount.isZero()) { 361 return MessageBuilder.buildMessage(LaborKeyConstants.ERROR_ZERO_TOTAL_AMOUNT, Message.TYPE_FATAL); 362 } 363 return null; 364 } 365 366 /** 367 * Checks if the given transaction contains the valid employee id 368 * 369 * @param transaction the given transaction 370 * @param unpostableObjectCodes the list of unpostable object codes 371 * @return null if the object code of the transaction is not in unpostableObjectCodes; otherwise, return error message 372 */ 373 public static Message checkEmplid(LaborTransaction transaction) { 374 String emplid = transaction.getEmplid(); 375 if (StringUtils.isBlank(emplid)) { 376 return MessageBuilder.buildMessage(LaborKeyConstants.MISSING_EMPLOYEE_ID, Message.TYPE_FATAL); 377 } 378 return null; 379 } 380 381 /** 382 * When in Rome... This method checks if the encumbrance update code is valid 383 * @param transaction the transaction to check 384 * @return a Message if the encumbrance update code is not valid, or null if all is well 385 */ 386 public static Message checkEncumbranceUpdateCode(LaborTransaction transaction) { 387 // The encumbrance update code can only be space, N, R or D. Nothing else 388 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()))) { 389 return new Message("Invalid Encumbrance Update Code (" + transaction.getTransactionEncumbranceUpdateCode() + ")", Message.TYPE_FATAL); 390 } 391 return null; 392 } 393 394 static LaborAccountingCycleCachingService getAccountingCycleCachingService() { 395 if (accountingCycleCachingService == null) { 396 accountingCycleCachingService = SpringContext.getBean(LaborAccountingCycleCachingService.class); 397 } 398 return accountingCycleCachingService; 399 } 400 401 static KualiConfigurationService getKualiConfigurationService() { 402 if (kualiConfigurationService == null) { 403 kualiConfigurationService = SpringContext.getBean(KualiConfigurationService.class); 404 } 405 return kualiConfigurationService; 406 } 407 }