/* * 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.sys.document.service.impl; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.kuali.kfs.gl.service.impl.StringHelper; import org.kuali.kfs.sys.KFSConstants; import org.kuali.kfs.sys.businessobject.AccountingLine; import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySourceDetail; import org.kuali.kfs.sys.businessobject.SystemOptions; import org.kuali.kfs.sys.document.AccountingDocument; import org.kuali.kfs.sys.document.GeneralLedgerPendingEntrySource; import org.kuali.kfs.sys.document.service.AccountingDocumentRuleHelperService; import org.kuali.kfs.sys.document.service.DebitDeterminerService; import org.kuali.kfs.sys.service.OptionsService; import org.kuali.rice.core.api.util.type.KualiDecimal; /** * Default implementation of the DebitDeterminerService. */ public class DebitDeterminerServiceImpl implements DebitDeterminerService { private static Logger LOG = Logger.getLogger(DebitDeterminerServiceImpl.class); protected static final String isDebitCalculationIllegalStateExceptionMessage = "an invalid debit/credit check state was detected"; protected static final String isErrorCorrectionIllegalStateExceptionMessage = "invalid (error correction) document not allowed"; protected static final String isInvalidLineTypeIllegalArgumentExceptionMessage = "invalid accounting line type"; private AccountingDocumentRuleHelperService accountingDocumentRuleUtil; private OptionsService optionsService; /** * @see org.kuali.kfs.sys.document.service.DebitDeterminerService#disallowErrorCorrectionDocumentCheck(org.kuali.kfs.sys.document.GeneralLedgerPendingEntrySource) */ @Override public void disallowErrorCorrectionDocumentCheck(GeneralLedgerPendingEntrySource poster) { LOG.debug("disallowErrorCorrectionDocumentCheck(AccountingDocumentRuleBase, AccountingDocument) - start"); if (isErrorCorrection(poster)) { throw new IllegalStateException(isErrorCorrectionIllegalStateExceptionMessage); } LOG.debug("disallowErrorCorrectionDocumentCheck(AccountingDocumentRuleBase, AccountingDocument) - end"); } /** * @see org.kuali.kfs.sys.document.service.DebitDeterminerService#isAsset(org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySourceDetail) */ @Override public boolean isAsset(GeneralLedgerPendingEntrySourceDetail postable) { LOG.debug("isAsset(AccountingLine) - start"); boolean returnboolean = isAssetTypeCode(accountingDocumentRuleUtil.getObjectCodeTypeCodeWithoutSideEffects(postable)); LOG.debug("isAsset(AccountingLine) - end"); return returnboolean; } /** * @see org.kuali.kfs.sys.document.service.DebitDeterminerService#isAssetTypeCode(java.lang.String) */ @Override public boolean isAssetTypeCode(String objectTypeCode) { LOG.debug("isAssetTypeCode(String) - start"); boolean returnboolean = optionsService.getCurrentYearOptions().getFinancialObjectTypeAssetsCd().equals(objectTypeCode); LOG.debug("isAssetTypeCode(String) - end"); return returnboolean; } /** * @see org.kuali.kfs.sys.document.service.DebitDeterminerService#isDebitCode(java.lang.String) */ @Override public boolean isDebitCode(String debitCreditCode) { LOG.debug("isDebitCode(String) - start"); boolean returnboolean = StringUtils.equals(KFSConstants.GL_DEBIT_CODE, debitCreditCode); LOG.debug("isDebitCode(String) - end"); return returnboolean; } /** * @see org.kuali.kfs.sys.document.service.DebitDeterminerService#isDebitConsideringNothingPositiveOnly(org.kuali.kfs.sys.document.GeneralLedgerPendingEntrySource, org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySourceDetail) */ @Override public boolean isDebitConsideringNothingPositiveOnly(GeneralLedgerPendingEntrySource poster, GeneralLedgerPendingEntrySourceDetail postable) { LOG.debug("isDebitConsideringNothingPositiveOnly(AccountingDocumentRuleBase, AccountingDocument, AccountingLine) - start"); boolean isDebit = isDebitConsideringNothingPositiveOrNegative(poster,postable); // non error correction document with non positive amount if (!isErrorCorrection(poster) && !isDebit) { throw new IllegalStateException(isDebitCalculationIllegalStateExceptionMessage); } LOG.debug("isDebitConsideringNothingPositiveOnly(AccountingDocumentRuleBase, AccountingDocument, AccountingLine) - end"); return isDebit; } /** * @see org.kuali.kfs.sys.document.service.DebitDeterminerService#isDebitConsideringNothingPositiveOrNegative(org.kuali.kfs.sys.document.GeneralLedgerPendingEntrySource, org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySourceDetail) */ @Override public boolean isDebitConsideringNothingPositiveOrNegative(GeneralLedgerPendingEntrySource poster, GeneralLedgerPendingEntrySourceDetail postable) { LOG.debug("isDebitConsideringNothingPositiveOrNegative(AccountingDocumentRuleBase, AccountingDocument, AccountingLine) - start"); boolean isDebit = false; KualiDecimal amount = postable.getAmount(); boolean isPositiveAmount = amount.isPositive(); // isDebit if income/liability/expense/asset and line amount is positive if (isPositiveAmount && (isIncomeOrLiability(postable) || isExpenseOrAsset(postable))) { isDebit = true; } else { isDebit = false; } LOG.debug("isDebitConsideringNothingPositiveOnly(AccountingDocumentRuleBase, AccountingDocument, AccountingLine) - end"); return isDebit; } /** * @see org.kuali.kfs.sys.document.service.DebitDeterminerService#isDebitConsideringSection(org.kuali.kfs.sys.document.AccountingDocument, org.kuali.kfs.sys.businessobject.AccountingLine) */ @Override public boolean isDebitConsideringSection(AccountingDocument accountingDocument, AccountingLine accountingLine) { LOG.debug("isDebitConsideringSection(AccountingDocumentRuleBase, AccountingDocument, AccountingLine) - start"); KualiDecimal amount = accountingLine.getAmount(); // zero amounts are not allowed if (amount.isZero()) { throw new IllegalStateException(isDebitCalculationIllegalStateExceptionMessage); } boolean isDebit = false; boolean isPositiveAmount = accountingLine.getAmount().isPositive(); // source line if (accountingLine.isSourceAccountingLine()) { // income/liability/expense/asset if (isIncomeOrLiability(accountingLine) || isExpenseOrAsset(accountingLine)) { isDebit = !isPositiveAmount; } else { throw new IllegalStateException(isDebitCalculationIllegalStateExceptionMessage); } } // target line else { if (accountingLine.isTargetAccountingLine()) { if (isIncomeOrLiability(accountingLine) || isExpenseOrAsset(accountingLine)) { isDebit = isPositiveAmount; } else { throw new IllegalStateException(isDebitCalculationIllegalStateExceptionMessage); } } else { throw new IllegalArgumentException(isInvalidLineTypeIllegalArgumentExceptionMessage); } } LOG.debug("isDebitConsideringSection(AccountingDocumentRuleBase, AccountingDocument, AccountingLine) - end"); return isDebit; } /** * @see org.kuali.kfs.sys.document.service.DebitDeterminerService#isDebitConsideringSectionAndTypePositiveOnly(org.kuali.kfs.sys.document.AccountingDocument, org.kuali.kfs.sys.businessobject.AccountingLine) */ @Override public boolean isDebitConsideringSectionAndTypePositiveOnly(AccountingDocument accountingDocument, AccountingLine accountingLine) { LOG.debug("isDebitConsideringSectionAndTypePositiveOnly(AccountingDocumentRuleBase, AccountingDocument, AccountingLine) - start"); boolean isDebit = false; KualiDecimal amount = accountingLine.getAmount(); boolean isPositiveAmount = amount.isPositive(); // non error correction - only allow amount >0 if (!isPositiveAmount && !isErrorCorrection(accountingDocument)) { throw new IllegalStateException(isDebitCalculationIllegalStateExceptionMessage); } // source line if (accountingLine.isSourceAccountingLine()) { // could write below block in one line using == as XNOR operator, but that's confusing to read: // isDebit = (rule.isIncomeOrLiability(accountingLine) == isPositiveAmount); if (isPositiveAmount) { isDebit = isIncomeOrLiability(accountingLine); } else { isDebit = isExpenseOrAsset(accountingLine); } } // target line else { if (accountingLine.isTargetAccountingLine()) { if (isPositiveAmount) { isDebit = isExpenseOrAsset(accountingLine); } else { isDebit = isIncomeOrLiability(accountingLine); } } else { throw new IllegalArgumentException(isInvalidLineTypeIllegalArgumentExceptionMessage); } } LOG.debug("isDebitConsideringSectionAndTypePositiveOnly(AccountingDocumentRuleBase, AccountingDocument, AccountingLine) - end"); return isDebit; } /** * @see org.kuali.kfs.sys.document.service.DebitDeterminerService#isDebitConsideringType(org.kuali.kfs.sys.document.GeneralLedgerPendingEntrySource, org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySourceDetail) */ @Override public boolean isDebitConsideringType(GeneralLedgerPendingEntrySource poster, GeneralLedgerPendingEntrySourceDetail postable) { LOG.debug("isDebitConsideringType(AccountingDocumentRuleBase, AccountingDocument, AccountingLine) - start"); KualiDecimal amount = postable.getAmount(); // zero amounts are not allowed if (amount.isZero()) { throw new IllegalStateException(isDebitCalculationIllegalStateExceptionMessage); } boolean isDebit = false; boolean isPositiveAmount = postable.getAmount().isPositive(); // income/liability if (isIncomeOrLiability(postable)) { isDebit = !isPositiveAmount; } // expense/asset else { if (isExpenseOrAsset(postable)) { isDebit = isPositiveAmount; } else { throw new IllegalStateException(isDebitCalculationIllegalStateExceptionMessage); } } LOG.debug("isDebitConsideringType(AccountingDocumentRuleBase, AccountingDocument, AccountingLine) - end"); return isDebit; } /** * @see org.kuali.kfs.sys.document.service.DebitDeterminerService#isErrorCorrection(org.kuali.kfs.sys.document.GeneralLedgerPendingEntrySource) */ @Override public boolean isErrorCorrection(GeneralLedgerPendingEntrySource poster) { return StringUtils.isNotBlank(poster.getFinancialSystemDocumentHeader().getFinancialDocumentInErrorNumber()); } /** * @see org.kuali.kfs.sys.document.service.DebitDeterminerService#isExpense(org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySourceDetail) */ @Override public boolean isExpense(GeneralLedgerPendingEntrySourceDetail postable) { LOG.debug("isExpense(AccountingLine) - start"); boolean returnboolean = accountingDocumentRuleUtil.isExpense(postable); LOG.debug("isExpense(AccountingLine) - end"); return returnboolean; } /** * @see org.kuali.kfs.sys.document.service.DebitDeterminerService#isExpenseOrAsset(org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySourceDetail) */ @Override public boolean isExpenseOrAsset(GeneralLedgerPendingEntrySourceDetail postable) { LOG.debug("isExpenseOrAsset(AccountingLine) - start"); boolean returnboolean = isAsset(postable) || isExpense(postable); LOG.debug("isExpenseOrAsset(AccountingLine) - end"); return returnboolean; } /** * @see org.kuali.kfs.sys.document.service.DebitDeterminerService#isIncome(org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySourceDetail) */ @Override public boolean isIncome(GeneralLedgerPendingEntrySourceDetail postable) { LOG.debug("isIncome(AccountingLine) - start"); boolean returnboolean = accountingDocumentRuleUtil.isIncome(postable); LOG.debug("isIncome(AccountingLine) - end"); return returnboolean; } /** * @see org.kuali.kfs.sys.document.service.DebitDeterminerService#isIncomeOrLiability(org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySourceDetail) */ @Override public boolean isIncomeOrLiability(GeneralLedgerPendingEntrySourceDetail postable) { LOG.debug("isIncomeOrLiability(AccountingLine) - start"); boolean returnboolean = isLiability(postable) || isIncome(postable); LOG.debug("isIncomeOrLiability(AccountingLine) - end"); return returnboolean; } /** * @see org.kuali.kfs.sys.document.service.DebitDeterminerService#isLiability(org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySourceDetail) */ @Override public boolean isLiability(GeneralLedgerPendingEntrySourceDetail postable) { LOG.debug("isLiability(AccountingLine) - start"); boolean returnboolean = isLiabilityTypeCode(accountingDocumentRuleUtil.getObjectCodeTypeCodeWithoutSideEffects(postable)); LOG.debug("isLiability(AccountingLine) - end"); return returnboolean; } /** * @see org.kuali.kfs.sys.document.service.DebitDeterminerService#isLiabilityTypeCode(java.lang.String) */ @Override public boolean isLiabilityTypeCode(String objectTypeCode) { LOG.debug("isLiabilityTypeCode(String) - start"); boolean returnboolean = optionsService.getCurrentYearOptions().getFinObjectTypeLiabilitiesCode().equals(objectTypeCode); LOG.debug("isLiabilityTypeCode(String) - end"); return returnboolean; } @Override public String getConvertedAmount(String objectType , String debitCreditCode , String amount ) { SystemOptions systemOption = optionsService.getCurrentYearOptions(); // If entries lack a debit and credit code that means they're budget entries and should already be signed appropriately + or -. // so we should just be able to grab those without having to do any change. if(StringHelper.isNullOrEmpty(debitCreditCode)) { return amount; } if(systemOption.getFinancialObjectTypeAssetsCd().equals(objectType) ||systemOption.getFinObjTypeExpendNotExpCode().equals(objectType) ||systemOption.getFinObjTypeExpenditureexpCd().equals(objectType) ||systemOption.getFinObjTypeExpendNotExpCode().equals(objectType) ||systemOption.getFinancialObjectTypeTransferExpenseCd().equals(objectType)) { if (KFSConstants.GL_CREDIT_CODE.equals(debitCreditCode)) { amount = "-" + amount; } } else if (systemOption.getFinObjTypeCshNotIncomeCd().equals(objectType) || systemOption.getFinObjectTypeFundBalanceCd().equals(objectType) || systemOption.getFinObjectTypeIncomecashCode().equals(objectType) || systemOption.getFinObjTypeCshNotIncomeCd().equals(objectType) || systemOption.getFinObjectTypeLiabilitiesCode().equals(objectType) || systemOption.getFinancialObjectTypeTransferIncomeCd().equals(objectType)) { if (KFSConstants.GL_DEBIT_CODE.equals(debitCreditCode)) { amount = "-" + amount; } } return amount; } /** * @see org.kuali.kfs.sys.document.service.DebitDeterminerService#isRevenue(org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySourceDetail) */ @Override public boolean isRevenue(GeneralLedgerPendingEntrySourceDetail postable) { LOG.debug("isRevenue(AccountingLine) - start"); boolean returnboolean = !isExpense(postable); LOG.debug("isRevenue(AccountingLine) - end"); return returnboolean; } /** * Sets the accountingDocumentRuleUtils attribute value. * @param accountingDocumentRuleUtils The accountingDocumentRuleUtils to set. */ public void setAccountingDocumentRuleUtils(AccountingDocumentRuleHelperService accountingDocumentRuleUtil) { this.accountingDocumentRuleUtil = accountingDocumentRuleUtil; } /** * Sets the optionsService attribute value. * @param optionsService The optionsService to set. */ public void setOptionsService(OptionsService optionsService) { this.optionsService = optionsService; } /** * Gets the isDebitCalculationIllegalStateExceptionMessage attribute. * @return Returns the isDebitCalculationIllegalStateExceptionMessage. */ @Override public String getDebitCalculationIllegalStateExceptionMessage() { return isDebitCalculationIllegalStateExceptionMessage; } /** * Gets the isErrorCorrectionIllegalStateExceptionMessage attribute. * @return Returns the isErrorCorrectionIllegalStateExceptionMessage. */ @Override public String getErrorCorrectionIllegalStateExceptionMessage() { return isErrorCorrectionIllegalStateExceptionMessage; } /** * Gets the isInvalidLineTypeIllegalArgumentExceptionMessage attribute. * @return Returns the isInvalidLineTypeIllegalArgumentExceptionMessage. */ @Override public String getInvalidLineTypeIllegalArgumentExceptionMessage() { return isInvalidLineTypeIllegalArgumentExceptionMessage; } }