/*
* 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.fp.document;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.kuali.kfs.coa.businessobject.Account;
import org.kuali.kfs.coa.service.AccountService;
import org.kuali.kfs.fp.businessobject.BudgetAdjustmentAccountingLine;
import org.kuali.kfs.fp.businessobject.BudgetAdjustmentSourceAccountingLine;
import org.kuali.kfs.fp.businessobject.BudgetAdjustmentTargetAccountingLine;
import org.kuali.kfs.fp.businessobject.FiscalYearFunctionControl;
import org.kuali.kfs.fp.document.validation.impl.BudgetAdjustmentDocumentRuleConstants;
import org.kuali.kfs.fp.document.validation.impl.TransferOfFundsDocumentRuleConstants;
import org.kuali.kfs.fp.service.FiscalYearFunctionControlService;
import org.kuali.kfs.sys.KFSConstants;
import org.kuali.kfs.sys.businessobject.AccountingLine;
import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntry;
import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySequenceHelper;
import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySourceDetail;
import org.kuali.kfs.sys.businessobject.SourceAccountingLine;
import org.kuali.kfs.sys.businessobject.SystemOptions;
import org.kuali.kfs.sys.context.SpringContext;
import org.kuali.kfs.sys.document.AccountingDocumentBase;
import org.kuali.kfs.sys.document.AmountTotaling;
import org.kuali.kfs.sys.document.Correctable;
import org.kuali.kfs.sys.document.service.AccountingDocumentRuleHelperService;
import org.kuali.kfs.sys.document.service.DebitDeterminerService;
import org.kuali.kfs.sys.service.GeneralLedgerPendingEntryService;
import org.kuali.kfs.sys.service.OptionsService;
import org.kuali.kfs.sys.service.UniversityDateService;
import org.kuali.rice.core.api.parameter.ParameterEvaluatorService;
import org.kuali.rice.core.api.util.type.KualiDecimal;
import org.kuali.rice.core.api.util.type.KualiInteger;
import org.kuali.rice.core.web.format.CurrencyFormatter;
import org.kuali.rice.coreservice.framework.parameter.ParameterService;
import org.kuali.rice.kew.api.exception.WorkflowException;
import org.kuali.rice.kim.api.identity.Person;
import org.kuali.rice.kim.api.services.KimApiServiceLocator;
import org.kuali.rice.krad.document.Copyable;
import org.kuali.rice.krad.exception.InfrastructureException;
import org.kuali.rice.krad.util.ObjectUtils;
/**
* This is the business object that represents the BudgetAdjustment document in Kuali.
*/
public class BudgetAdjustmentDocument extends AccountingDocumentBase implements Copyable, Correctable, AmountTotaling {
protected static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(BudgetAdjustmentDocument.class);
protected static final String REQUIRES_FULL_APPROVAL_SPLIT_NODE_NAME = "RequiresFullApproval";
protected Integer nextPositionSourceLineNumber;
protected Integer nextPositionTargetLineNumber;
/**
* Default constructor.
*/
public BudgetAdjustmentDocument() {
super();
}
/*******************************************************************************************************************************
* BA Documents should only do SF checking on PLEs with a Balance Type of 'CB' - not 'BB' or 'MB'.
*
*
* @see org.kuali.kfs.sys.document.AccountingDocumentBase#getPendingLedgerEntriesForSufficientFundsChecking()
*/
@Override
public List<GeneralLedgerPendingEntry> getPendingLedgerEntriesForSufficientFundsChecking() {
List<GeneralLedgerPendingEntry> pendingLedgerEntries = new ArrayList();
GeneralLedgerPendingEntrySequenceHelper glpeSequenceHelper = new GeneralLedgerPendingEntrySequenceHelper();
BudgetAdjustmentDocument copiedBa = (BudgetAdjustmentDocument) ObjectUtils.deepCopy(this);
copiedBa.getGeneralLedgerPendingEntries().clear();
for (BudgetAdjustmentAccountingLine fromLine : (List<BudgetAdjustmentAccountingLine>) copiedBa.getSourceAccountingLines()) {
copiedBa.generateGeneralLedgerPendingEntries(fromLine, glpeSequenceHelper);
}
for (GeneralLedgerPendingEntry ple : copiedBa.getGeneralLedgerPendingEntries()) {
if (!KFSConstants.BALANCE_TYPE_BASE_BUDGET.equals(ple.getFinancialBalanceTypeCode()) && !KFSConstants.BALANCE_TYPE_MONTHLY_BUDGET.equals(ple.getFinancialBalanceTypeCode())) {
pendingLedgerEntries.add(ple);
}
}
return pendingLedgerEntries;
}
/**
* generic, shared logic used to iniate a ba document
*/
public void initiateDocument() {
// setting default posting year. Trying to set currentYear first if it's allowed, if it isn't,
// just set first allowed year. Note: allowedYears will never be empty because then
// BudgetAdjustmentDocumentAuthorizer.canInitiate would have failed.
List allowedYears = SpringContext.getBean(FiscalYearFunctionControlService.class).getBudgetAdjustmentAllowedYears();
Integer currentYearParam = SpringContext.getBean(UniversityDateService.class).getCurrentFiscalYear();
FiscalYearFunctionControl fiscalYearFunctionControl = new FiscalYearFunctionControl();
fiscalYearFunctionControl.setUniversityFiscalYear(currentYearParam);
// use 'this.postingYear =' because setPostingYear has logic we want to circumvent on initiateDocument
if (allowedYears.contains(fiscalYearFunctionControl)) {
this.postingYear = currentYearParam;
}
else {
this.postingYear = ((FiscalYearFunctionControl) allowedYears.get(0)).getUniversityFiscalYear();
}
}
/**
* @return Integer
*/
public Integer getNextPositionSourceLineNumber() {
return nextPositionSourceLineNumber;
}
/**
* @param nextPositionSourceLineNumber
*/
public void setNextPositionSourceLineNumber(Integer nextPositionSourceLineNumber) {
this.nextPositionSourceLineNumber = nextPositionSourceLineNumber;
}
/**
* @return Integer
*/
public Integer getNextPositionTargetLineNumber() {
return nextPositionTargetLineNumber;
}
/**
* @param nextPositionTargetLineNumber
*/
public void setNextPositionTargetLineNumber(Integer nextPositionTargetLineNumber) {
this.nextPositionTargetLineNumber = nextPositionTargetLineNumber;
}
/**
* Returns the total current budget amount from the source lines.
*
* @return KualiDecimal
*/
public KualiDecimal getSourceCurrentBudgetTotal() {
KualiDecimal currentBudgetTotal = KualiDecimal.ZERO;
for (Iterator iter = sourceAccountingLines.iterator(); iter.hasNext();) {
BudgetAdjustmentAccountingLine line = (BudgetAdjustmentAccountingLine) iter.next();
currentBudgetTotal = currentBudgetTotal.add(line.getCurrentBudgetAdjustmentAmount());
}
return currentBudgetTotal;
}
/**
* This method retrieves the total current budget amount formatted as currency.
*
* @return String
*/
public String getCurrencyFormattedSourceCurrentBudgetTotal() {
return (String) new CurrencyFormatter().format(getSourceCurrentBudgetTotal());
}
/**
* Returns the total current budget income amount from the source lines.
*
* @return KualiDecimal
*/
public KualiDecimal getSourceCurrentBudgetIncomeTotal() {
KualiDecimal total = KualiDecimal.ZERO;
for (Iterator iter = sourceAccountingLines.iterator(); iter.hasNext();) {
BudgetAdjustmentAccountingLine line = (BudgetAdjustmentAccountingLine) iter.next();
AccountingDocumentRuleHelperService accountingDocumentRuleUtil = SpringContext.getBean(AccountingDocumentRuleHelperService.class);
if (accountingDocumentRuleUtil.isIncome(line)) {
total = total.add(line.getCurrentBudgetAdjustmentAmount());
}
}
return total;
}
/**
* Returns the total current budget expense amount from the source lines.
*
* @return KualiDecimal
*/
public KualiDecimal getSourceCurrentBudgetExpenseTotal() {
KualiDecimal total = KualiDecimal.ZERO;
for (Iterator iter = sourceAccountingLines.iterator(); iter.hasNext();) {
BudgetAdjustmentAccountingLine line = (BudgetAdjustmentAccountingLine) iter.next();
AccountingDocumentRuleHelperService accountingDocumentRuleUtil = SpringContext.getBean(AccountingDocumentRuleHelperService.class);
if (accountingDocumentRuleUtil.isExpense(line)) {
total = total.add(line.getCurrentBudgetAdjustmentAmount());
}
}
return total;
}
/**
* Returns the total current budget amount from the target lines.
*
* @return KualiDecimal
*/
public KualiDecimal getTargetCurrentBudgetTotal() {
KualiDecimal currentBudgetTotal = KualiDecimal.ZERO;
for (Iterator iter = targetAccountingLines.iterator(); iter.hasNext();) {
BudgetAdjustmentAccountingLine line = (BudgetAdjustmentAccountingLine) iter.next();
currentBudgetTotal = currentBudgetTotal.add(line.getCurrentBudgetAdjustmentAmount());
}
return currentBudgetTotal;
}
/**
* This method retrieves the total current budget amount formatted as currency.
*
* @return String
*/
public String getCurrencyFormattedTargetCurrentBudgetTotal() {
return (String) new CurrencyFormatter().format(getTargetCurrentBudgetTotal());
}
/**
* Returns the total current budget income amount from the target lines.
*
* @return KualiDecimal
*/
public KualiDecimal getTargetCurrentBudgetIncomeTotal() {
KualiDecimal total = KualiDecimal.ZERO;
AccountingDocumentRuleHelperService accountingDocumentRuleUtil = SpringContext.getBean(AccountingDocumentRuleHelperService.class);
for (Iterator iter = targetAccountingLines.iterator(); iter.hasNext();) {
BudgetAdjustmentAccountingLine line = (BudgetAdjustmentAccountingLine) iter.next();
if (accountingDocumentRuleUtil.isIncome(line)) {
total = total.add(line.getCurrentBudgetAdjustmentAmount());
}
}
return total;
}
/**
* Returns the total current budget expense amount from the target lines.
*
* @return KualiDecimal
*/
public KualiDecimal getTargetCurrentBudgetExpenseTotal() {
KualiDecimal total = KualiDecimal.ZERO;
AccountingDocumentRuleHelperService accountingDocumentRuleUtil = SpringContext.getBean(AccountingDocumentRuleHelperService.class);
for (Iterator iter = targetAccountingLines.iterator(); iter.hasNext();) {
BudgetAdjustmentAccountingLine line = (BudgetAdjustmentAccountingLine) iter.next();
if (accountingDocumentRuleUtil.isExpense(line)) {
total = total.add(line.getCurrentBudgetAdjustmentAmount());
}
}
return total;
}
/**
* Returns the total base budget amount from the source lines.
*
* @return KualiDecimal
*/
public KualiInteger getSourceBaseBudgetTotal() {
KualiInteger baseBudgetTotal = KualiInteger.ZERO;
for (Iterator iter = sourceAccountingLines.iterator(); iter.hasNext();) {
BudgetAdjustmentAccountingLine line = (BudgetAdjustmentAccountingLine) iter.next();
baseBudgetTotal = baseBudgetTotal.add(line.getBaseBudgetAdjustmentAmount());
}
return baseBudgetTotal;
}
/**
* This method retrieves the total base budget amount formatted as currency.
*
* @return String
*/
public String getCurrencyFormattedSourceBaseBudgetTotal() {
return (String) new CurrencyFormatter().format(getSourceBaseBudgetTotal());
}
/**
* Returns the total base budget income amount from the source lines.
*
* @return KualiDecimal
*/
public KualiInteger getSourceBaseBudgetIncomeTotal() {
KualiInteger total = KualiInteger.ZERO;
AccountingDocumentRuleHelperService accountingDocumentRuleUtil = SpringContext.getBean(AccountingDocumentRuleHelperService.class);
for (Iterator iter = sourceAccountingLines.iterator(); iter.hasNext();) {
BudgetAdjustmentAccountingLine line = (BudgetAdjustmentAccountingLine) iter.next();
if (accountingDocumentRuleUtil.isIncome(line)) {
total = total.add(line.getBaseBudgetAdjustmentAmount());
}
}
return total;
}
/**
* Returns the total base budget expense amount from the source lines.
*
* @return KualiDecimal
*/
public KualiInteger getSourceBaseBudgetExpenseTotal() {
KualiInteger total = KualiInteger.ZERO;
AccountingDocumentRuleHelperService accountingDocumentRuleUtil = SpringContext.getBean(AccountingDocumentRuleHelperService.class);
for (Iterator iter = sourceAccountingLines.iterator(); iter.hasNext();) {
BudgetAdjustmentAccountingLine line = (BudgetAdjustmentAccountingLine) iter.next();
if (accountingDocumentRuleUtil.isExpense(line)) {
total = total.add(line.getBaseBudgetAdjustmentAmount());
}
}
return total;
}
/**
* Returns the total base budget amount from the target lines.
*
* @return KualiDecimal
*/
public KualiInteger getTargetBaseBudgetTotal() {
KualiInteger baseBudgetTotal = KualiInteger.ZERO;
for (Iterator iter = targetAccountingLines.iterator(); iter.hasNext();) {
BudgetAdjustmentAccountingLine line = (BudgetAdjustmentAccountingLine) iter.next();
baseBudgetTotal = baseBudgetTotal.add(line.getBaseBudgetAdjustmentAmount());
}
return baseBudgetTotal;
}
/**
* This method retrieves the total base budget amount formatted as currency.
*
* @return String
*/
public String getCurrencyFormattedTargetBaseBudgetTotal() {
return (String) new CurrencyFormatter().format(getTargetBaseBudgetTotal());
}
/**
* Returns the total base budget income amount from the target lines.
*
* @return KualiDecimal
*/
public KualiInteger getTargetBaseBudgetIncomeTotal() {
KualiInteger total = KualiInteger.ZERO;
AccountingDocumentRuleHelperService accountingDocumentRuleUtil = SpringContext.getBean(AccountingDocumentRuleHelperService.class);
for (Iterator iter = targetAccountingLines.iterator(); iter.hasNext();) {
BudgetAdjustmentAccountingLine line = (BudgetAdjustmentAccountingLine) iter.next();
if (accountingDocumentRuleUtil.isIncome(line)) {
total = total.add(line.getBaseBudgetAdjustmentAmount());
}
}
return total;
}
/**
* Returns the total base budget expense amount from the target lines.
*
* @return KualiDecimal
*/
public KualiInteger getTargetBaseBudgetExpenseTotal() {
KualiInteger total = KualiInteger.ZERO;
AccountingDocumentRuleHelperService accountingDocumentRuleUtil = SpringContext.getBean(AccountingDocumentRuleHelperService.class);
for (Iterator iter = targetAccountingLines.iterator(); iter.hasNext();) {
BudgetAdjustmentAccountingLine line = (BudgetAdjustmentAccountingLine) iter.next();
if (accountingDocumentRuleUtil.isExpense(line)) {
total = total.add(line.getBaseBudgetAdjustmentAmount());
}
}
return total;
}
/**
* Same as default implementation but uses getTargetCurrentBudgetTotal and getSourceCurrentBudgetTotal instead.
*
* @see org.kuali.kfs.sys.document.AccountingDocumentBase#getTotalDollarAmount()
* @return KualiDecimal
*/
@Override
public KualiDecimal getTotalDollarAmount() {
// this convoluted chain is how the FIS decided what total to display
if (this.getTargetBaseBudgetExpenseTotal().isNonZero()) {
return this.getTargetBaseBudgetExpenseTotal().kualiDecimalValue();
}
if (this.getTargetCurrentBudgetExpenseTotal().isNonZero()){
return this.getTargetCurrentBudgetExpenseTotal();
}
if (this.getTargetBaseBudgetIncomeTotal().isNonZero()){
return this.getTargetBaseBudgetIncomeTotal().kualiDecimalValue();
}
if (this.getTargetCurrentBudgetIncomeTotal().isNonZero()){
return this.getTargetCurrentBudgetIncomeTotal();
}
if (this.getSourceBaseBudgetExpenseTotal().isNonZero()){
return this.getSourceBaseBudgetExpenseTotal().kualiDecimalValue();
}
if (this.getSourceCurrentBudgetExpenseTotal().isNonZero()){
return this.getSourceCurrentBudgetExpenseTotal();
}
return KualiDecimal.ZERO;
}
/**
* Negate accounting line budget amounts.
*
* @see org.kuali.kfs.sys.document.AccountingDocumentBase#toErrorCorrection()
*/
@Override
public void toErrorCorrection() throws WorkflowException {
super.toErrorCorrection();
if (this.getSourceAccountingLines() != null) {
for (Iterator iter = this.getSourceAccountingLines().iterator(); iter.hasNext();) {
BudgetAdjustmentAccountingLine sourceLine = (BudgetAdjustmentAccountingLine) iter.next();
sourceLine.setBaseBudgetAdjustmentAmount(sourceLine.getBaseBudgetAdjustmentAmount().negated());
sourceLine.setCurrentBudgetAdjustmentAmount(sourceLine.getCurrentBudgetAdjustmentAmount().negated());
sourceLine.setFinancialDocumentMonth1LineAmount(sourceLine.getFinancialDocumentMonth1LineAmount().negated());
sourceLine.setFinancialDocumentMonth2LineAmount(sourceLine.getFinancialDocumentMonth2LineAmount().negated());
sourceLine.setFinancialDocumentMonth3LineAmount(sourceLine.getFinancialDocumentMonth3LineAmount().negated());
sourceLine.setFinancialDocumentMonth4LineAmount(sourceLine.getFinancialDocumentMonth4LineAmount().negated());
sourceLine.setFinancialDocumentMonth5LineAmount(sourceLine.getFinancialDocumentMonth5LineAmount().negated());
sourceLine.setFinancialDocumentMonth6LineAmount(sourceLine.getFinancialDocumentMonth6LineAmount().negated());
sourceLine.setFinancialDocumentMonth7LineAmount(sourceLine.getFinancialDocumentMonth7LineAmount().negated());
sourceLine.setFinancialDocumentMonth8LineAmount(sourceLine.getFinancialDocumentMonth8LineAmount().negated());
sourceLine.setFinancialDocumentMonth9LineAmount(sourceLine.getFinancialDocumentMonth9LineAmount().negated());
sourceLine.setFinancialDocumentMonth10LineAmount(sourceLine.getFinancialDocumentMonth10LineAmount().negated());
sourceLine.setFinancialDocumentMonth11LineAmount(sourceLine.getFinancialDocumentMonth11LineAmount().negated());
sourceLine.setFinancialDocumentMonth12LineAmount(sourceLine.getFinancialDocumentMonth12LineAmount().negated());
}
}
if (this.getTargetAccountingLines() != null) {
for (Iterator iter = this.getTargetAccountingLines().iterator(); iter.hasNext();) {
BudgetAdjustmentAccountingLine targetLine = (BudgetAdjustmentAccountingLine) iter.next();
targetLine.setBaseBudgetAdjustmentAmount(targetLine.getBaseBudgetAdjustmentAmount().negated());
targetLine.setCurrentBudgetAdjustmentAmount(targetLine.getCurrentBudgetAdjustmentAmount().negated());
targetLine.setFinancialDocumentMonth1LineAmount(targetLine.getFinancialDocumentMonth1LineAmount().negated());
targetLine.setFinancialDocumentMonth2LineAmount(targetLine.getFinancialDocumentMonth2LineAmount().negated());
targetLine.setFinancialDocumentMonth3LineAmount(targetLine.getFinancialDocumentMonth3LineAmount().negated());
targetLine.setFinancialDocumentMonth4LineAmount(targetLine.getFinancialDocumentMonth4LineAmount().negated());
targetLine.setFinancialDocumentMonth5LineAmount(targetLine.getFinancialDocumentMonth5LineAmount().negated());
targetLine.setFinancialDocumentMonth6LineAmount(targetLine.getFinancialDocumentMonth6LineAmount().negated());
targetLine.setFinancialDocumentMonth7LineAmount(targetLine.getFinancialDocumentMonth7LineAmount().negated());
targetLine.setFinancialDocumentMonth8LineAmount(targetLine.getFinancialDocumentMonth8LineAmount().negated());
targetLine.setFinancialDocumentMonth9LineAmount(targetLine.getFinancialDocumentMonth9LineAmount().negated());
targetLine.setFinancialDocumentMonth10LineAmount(targetLine.getFinancialDocumentMonth10LineAmount().negated());
targetLine.setFinancialDocumentMonth11LineAmount(targetLine.getFinancialDocumentMonth11LineAmount().negated());
targetLine.setFinancialDocumentMonth12LineAmount(targetLine.getFinancialDocumentMonth12LineAmount().negated());
}
}
}
/**
* The base checks that the posting year is the current year, not a requirement for the ba document.
*
* @see org.kuali.rice.krad.document.TransactionalDocumentBase#getAllowsCopy()
*/
@Override
public boolean getAllowsCopy() {
return true;
}
/**
* @see org.kuali.kfs.sys.document.AccountingDocumentBase#getSourceAccountingLinesSectionTitle()
*/
@Override
public String getSourceAccountingLinesSectionTitle() {
return KFSConstants.BudgetAdjustmentDocumentConstants.SOURCE_BA;
}
/**
* @see org.kuali.kfs.sys.document.AccountingDocumentBase#getTargetAccountingLinesSectionTitle()
*/
@Override
public String getTargetAccountingLinesSectionTitle() {
return KFSConstants.BudgetAdjustmentDocumentConstants.TARGET_BA;
}
/**
* @see org.kuali.rice.krad.document.DocumentBase#populateDocumentForRouting()
*/
@Override
public void populateDocumentForRouting() {
super.populateDocumentForRouting();
// set amount fields of line for routing to current amount field
for (Iterator iter = this.getSourceAccountingLines().iterator(); iter.hasNext();) {
BudgetAdjustmentAccountingLine line = (BudgetAdjustmentAccountingLine) iter.next();
line.setAmount(line.getCurrentBudgetAdjustmentAmount());
}
for (Iterator iter = this.getTargetAccountingLines().iterator(); iter.hasNext();) {
BudgetAdjustmentAccountingLine line = (BudgetAdjustmentAccountingLine) iter.next();
line.setAmount(line.getCurrentBudgetAdjustmentAmount());
}
}
/**
* Returns true if accounting line is debit
*
* @param financialDocument submitted financial document
* @param accountingLine accounting line being evaluated as a debit or not
* @see org.kuali.rice.krad.rule.AccountingLineRule#isDebit(org.kuali.rice.krad.document.FinancialDocument,
* org.kuali.rice.krad.bo.AccountingLine)
*/
@Override
public boolean isDebit(GeneralLedgerPendingEntrySourceDetail postable) {
try {
DebitDeterminerService isDebitUtils = SpringContext.getBean(DebitDeterminerService.class);
return isDebitUtils.isDebitConsideringType(this, postable);
}
catch (IllegalStateException e) {
// for all accounting lines except the transfer lines, the line amount will be 0 and this exception will be thrown
return false;
}
}
/**
* The budget adjustment document creates GL pending entries much differently that common tp-edocs. The glpes are created for
* BB, CB, and MB balance types. Up to 14 entries per line can be created. Along with this, the BA will create TOF entries if
* needed to move funding.
*
* @param financialDocument submitted accounting document
* @param accountingLine validated accounting line
* @param sequenceHelper helper class for keeping track of sequence number
* @return true if GLPE entries are successfully created.
* @see org.kuali.module.financial.rules.FinancialDocumentRuleBase#processGenerateGeneralLedgerPendingEntries(org.kuali.rice.krad.document.FinancialDocument,
* org.kuali.rice.krad.bo.AccountingLine, org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySequenceHelper)
*/
@Override
public boolean generateGeneralLedgerPendingEntries(GeneralLedgerPendingEntrySourceDetail glpeSourceDetail, GeneralLedgerPendingEntrySequenceHelper sequenceHelper) {
AccountingLine accountingLine = (AccountingLine) glpeSourceDetail;
// determine if we are on increase or decrease side
KualiDecimal amountSign = null;
if (accountingLine instanceof SourceAccountingLine) {
amountSign = new KualiDecimal(-1);
}
else {
amountSign = new KualiDecimal(1);
}
BudgetAdjustmentAccountingLine budgetAccountingLine = (BudgetAdjustmentAccountingLine) glpeSourceDetail;
Integer currentFiscalYear = SpringContext.getBean(UniversityDateService.class).getCurrentFiscalYear();
/* Create Base Budget GLPE if base amount != 0 */
if (budgetAccountingLine.getBaseBudgetAdjustmentAmount().isNonZero()) {
GeneralLedgerPendingEntry explicitEntry = new GeneralLedgerPendingEntry();
getGeneralLedgerPendingEntryService().populateExplicitGeneralLedgerPendingEntry(this, accountingLine, sequenceHelper, explicitEntry);
/* D/C code is empty for BA, set correct balance type, correct amount */
explicitEntry.setTransactionDebitCreditCode("");
explicitEntry.setFinancialBalanceTypeCode(KFSConstants.BALANCE_TYPE_BASE_BUDGET);
explicitEntry.setTransactionLedgerEntryAmount(budgetAccountingLine.getBaseBudgetAdjustmentAmount().multiply(amountSign).kualiDecimalValue());
// set fiscal period, if next fiscal year set to 01, else leave to current period
if (currentFiscalYear.equals(getPostingYear() - 1)) {
explicitEntry.setUniversityFiscalPeriodCode(BudgetAdjustmentDocumentRuleConstants.MONTH_1_PERIOD_CODE);
}
customizeExplicitGeneralLedgerPendingEntry(accountingLine, explicitEntry);
addPendingEntry(explicitEntry);
// increment the sequence counter
sequenceHelper.increment();
}
/* Create Current Budget GLPE if current amount != 0 */
if (budgetAccountingLine.getCurrentBudgetAdjustmentAmount().isNonZero()) {
GeneralLedgerPendingEntry explicitEntry = new GeneralLedgerPendingEntry();
getGeneralLedgerPendingEntryService().populateExplicitGeneralLedgerPendingEntry(this, accountingLine, sequenceHelper, explicitEntry);
/* D/C code is empty for BA, set correct balance type, correct amount */
explicitEntry.setTransactionDebitCreditCode("");
explicitEntry.setFinancialBalanceTypeCode(KFSConstants.BALANCE_TYPE_CURRENT_BUDGET);
explicitEntry.setTransactionLedgerEntryAmount(budgetAccountingLine.getCurrentBudgetAdjustmentAmount().multiply(amountSign));
// set fiscal period, if next fiscal year set to 01, else leave to current period
if (currentFiscalYear.equals(getPostingYear() - 1)) {
explicitEntry.setUniversityFiscalPeriodCode("01");
}
customizeExplicitGeneralLedgerPendingEntry(accountingLine, explicitEntry);
addPendingEntry(explicitEntry);
// create monthly lines (MB)
if (budgetAccountingLine.getFinancialDocumentMonth1LineAmount().isNonZero()) {
sequenceHelper.increment();
createMonthlyBudgetGLPE(accountingLine, sequenceHelper, BudgetAdjustmentDocumentRuleConstants.MONTH_1_PERIOD_CODE, budgetAccountingLine.getFinancialDocumentMonth1LineAmount().multiply(amountSign));
}
if (budgetAccountingLine.getFinancialDocumentMonth2LineAmount().isNonZero()) {
sequenceHelper.increment();
createMonthlyBudgetGLPE(accountingLine, sequenceHelper, BudgetAdjustmentDocumentRuleConstants.MONTH_2_PERIOD_CODE, budgetAccountingLine.getFinancialDocumentMonth2LineAmount().multiply(amountSign));
}
if (budgetAccountingLine.getFinancialDocumentMonth3LineAmount().isNonZero()) {
sequenceHelper.increment();
createMonthlyBudgetGLPE(accountingLine, sequenceHelper, BudgetAdjustmentDocumentRuleConstants.MONTH_3_PERIOD_CODE, budgetAccountingLine.getFinancialDocumentMonth3LineAmount().multiply(amountSign));
}
if (budgetAccountingLine.getFinancialDocumentMonth4LineAmount().isNonZero()) {
sequenceHelper.increment();
createMonthlyBudgetGLPE(accountingLine, sequenceHelper, BudgetAdjustmentDocumentRuleConstants.MONTH_4_PERIOD_CODE, budgetAccountingLine.getFinancialDocumentMonth4LineAmount().multiply(amountSign));
}
if (budgetAccountingLine.getFinancialDocumentMonth5LineAmount().isNonZero()) {
sequenceHelper.increment();
createMonthlyBudgetGLPE(accountingLine, sequenceHelper, BudgetAdjustmentDocumentRuleConstants.MONTH_5_PERIOD_CODE, budgetAccountingLine.getFinancialDocumentMonth5LineAmount().multiply(amountSign));
}
if (budgetAccountingLine.getFinancialDocumentMonth6LineAmount().isNonZero()) {
sequenceHelper.increment();
createMonthlyBudgetGLPE(accountingLine, sequenceHelper, BudgetAdjustmentDocumentRuleConstants.MONTH_6_PERIOD_CODE, budgetAccountingLine.getFinancialDocumentMonth6LineAmount().multiply(amountSign));
}
if (budgetAccountingLine.getFinancialDocumentMonth7LineAmount().isNonZero()) {
sequenceHelper.increment();
createMonthlyBudgetGLPE(accountingLine, sequenceHelper, BudgetAdjustmentDocumentRuleConstants.MONTH_7_PERIOD_CODE, budgetAccountingLine.getFinancialDocumentMonth7LineAmount().multiply(amountSign));
}
if (budgetAccountingLine.getFinancialDocumentMonth8LineAmount().isNonZero()) {
sequenceHelper.increment();
createMonthlyBudgetGLPE(accountingLine, sequenceHelper, BudgetAdjustmentDocumentRuleConstants.MONTH_8_PERIOD_CODE, budgetAccountingLine.getFinancialDocumentMonth8LineAmount().multiply(amountSign));
}
if (budgetAccountingLine.getFinancialDocumentMonth9LineAmount().isNonZero()) {
sequenceHelper.increment();
createMonthlyBudgetGLPE(accountingLine, sequenceHelper, BudgetAdjustmentDocumentRuleConstants.MONTH_9_PERIOD_CODE, budgetAccountingLine.getFinancialDocumentMonth9LineAmount().multiply(amountSign));
}
if (budgetAccountingLine.getFinancialDocumentMonth10LineAmount().isNonZero()) {
sequenceHelper.increment();
createMonthlyBudgetGLPE(accountingLine, sequenceHelper, BudgetAdjustmentDocumentRuleConstants.MONTH_10_PERIOD_CODE, budgetAccountingLine.getFinancialDocumentMonth10LineAmount().multiply(amountSign));
}
if (budgetAccountingLine.getFinancialDocumentMonth11LineAmount().isNonZero()) {
sequenceHelper.increment();
createMonthlyBudgetGLPE(accountingLine, sequenceHelper, BudgetAdjustmentDocumentRuleConstants.MONTH_11_PERIOD_CODE, budgetAccountingLine.getFinancialDocumentMonth11LineAmount().multiply(amountSign));
}
if (budgetAccountingLine.getFinancialDocumentMonth12LineAmount().isNonZero()) {
sequenceHelper.increment();
createMonthlyBudgetGLPE(accountingLine, sequenceHelper, BudgetAdjustmentDocumentRuleConstants.MONTH_12_PERIOD_CODE, budgetAccountingLine.getFinancialDocumentMonth12LineAmount().multiply(amountSign));
}
}
return true;
}
/**
* Helper method for creating monthly budget pending entry lines.
*
* @param financialDocument submitted accounting document
* @param accountingLine validated accounting line
* @param sequenceHelper helper class for keeping track of sequence number
* @param fiscalPeriod fiscal year period code
* @param monthAmount ledger entry amount for the month
*/
protected void createMonthlyBudgetGLPE(AccountingLine accountingLine, GeneralLedgerPendingEntrySequenceHelper sequenceHelper, String fiscalPeriod, KualiDecimal monthAmount) {
GeneralLedgerPendingEntry explicitEntry = new GeneralLedgerPendingEntry();
getGeneralLedgerPendingEntryService().populateExplicitGeneralLedgerPendingEntry(this, accountingLine, sequenceHelper, explicitEntry);
/* D/C code is empty for BA, set correct balance type, correct amount */
explicitEntry.setTransactionDebitCreditCode("");
explicitEntry.setFinancialBalanceTypeCode(KFSConstants.BALANCE_TYPE_MONTHLY_BUDGET);
explicitEntry.setTransactionLedgerEntryAmount(monthAmount);
explicitEntry.setUniversityFiscalPeriodCode(fiscalPeriod);
customizeExplicitGeneralLedgerPendingEntry(accountingLine, explicitEntry);
addPendingEntry(explicitEntry);
}
/**
* Returns an implementation of the GeneralLedgerPendingEntryService
*
* @return an implementation of the GeneralLedgerPendingEntryService
*/
public GeneralLedgerPendingEntryService getGeneralLedgerPendingEntryService() {
return SpringContext.getBean(GeneralLedgerPendingEntryService.class);
}
/**
* Generates any necessary tof entries to transfer funds needed to make the budget adjustments. Based on income chart and
* accounts. If there is a difference in funds between an income chart and account, a tof entry needs to be created, along with
* a budget adjustment entry. Object code used is retrieved by a parameter.
*
* @param sequenceHelper helper class for keeping track of sequence number
* @return true general ledger pending entries are generated without any problems
* @see org.kuali.rice.krad.rule.GenerateGeneralLedgerDocumentPendingEntriesRule#processGenerateDocumentGeneralLedgerPendingEntries(org.kuali.rice.krad.document.FinancialDocument,
* org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySequenceHelper)
*/
@Override
public boolean generateDocumentGeneralLedgerPendingEntries(GeneralLedgerPendingEntrySequenceHelper sequenceHelper) {
boolean success = true;
// check on-off tof flag
boolean generateTransfer = SpringContext.getBean(ParameterService.class).getParameterValueAsBoolean(BudgetAdjustmentDocument.class, BudgetAdjustmentDocumentRuleConstants.GENERATE_TOF_GLPE_ENTRIES_PARM_NM);
String transferObjectCode = SpringContext.getBean(ParameterService.class).getParameterValueAsString(BudgetAdjustmentDocument.class, BudgetAdjustmentDocumentRuleConstants.TRANSFER_OBJECT_CODE_PARM_NM);
Integer currentFiscalYear = SpringContext.getBean(UniversityDateService.class).getCurrentFiscalYear();
if (generateTransfer) {
// map of income chart/accounts with balance as value
Map<String, KualiDecimal> incomeStreamMap = buildIncomeStreamBalanceMapForTransferOfFundsGeneration();
GeneralLedgerPendingEntryService glpeService = SpringContext.getBean(GeneralLedgerPendingEntryService.class);
for (Iterator iter = incomeStreamMap.keySet().iterator(); iter.hasNext();) {
String chartAccount = (String) iter.next();
KualiDecimal streamAmount = incomeStreamMap.get(chartAccount);
if (streamAmount.isNonZero()) {
// build dummy accounting line for gl population
AccountingLine accountingLine = null;
try {
accountingLine = (SourceAccountingLine) getSourceAccountingLineClass().newInstance();
}
catch (IllegalAccessException e) {
throw new InfrastructureException("unable to access sourceAccountingLineClass", e);
}
catch (InstantiationException e) {
throw new InfrastructureException("unable to instantiate sourceAccountingLineClass", e);
}
// set income chart and account in line
String[] incomeString = StringUtils.split(chartAccount, BudgetAdjustmentDocumentRuleConstants.INCOME_STREAM_CHART_ACCOUNT_DELIMITER);
accountingLine.setChartOfAccountsCode(incomeString[0]);
accountingLine.setAccountNumber(incomeString[1]);
accountingLine.setFinancialObjectCode(transferObjectCode);
// ////////////////// first create current budget entry/////////////////////////////////////////
GeneralLedgerPendingEntry explicitEntry = new GeneralLedgerPendingEntry();
glpeService.populateExplicitGeneralLedgerPendingEntry(this, accountingLine, sequenceHelper, explicitEntry);
/* override and set object type to income */
SystemOptions options = SpringContext.getBean(OptionsService.class).getCurrentYearOptions();
explicitEntry.setFinancialObjectTypeCode(options.getFinObjectTypeIncomecashCode());
/* D/C code is empty for BA, set correct balance type, correct amount */
explicitEntry.setTransactionDebitCreditCode("");
explicitEntry.setFinancialBalanceTypeCode(KFSConstants.BALANCE_TYPE_CURRENT_BUDGET);
explicitEntry.setTransactionLedgerEntryAmount(streamAmount);
// set fiscal period, if next fiscal year set to 01, else leave to current period
if (currentFiscalYear.equals(getPostingYear() - 1)) {
explicitEntry.setUniversityFiscalPeriodCode(BudgetAdjustmentDocumentRuleConstants.MONTH_1_PERIOD_CODE);
}
customizeExplicitGeneralLedgerPendingEntry(accountingLine, explicitEntry);
// add the new explicit entry to the document now
addPendingEntry(explicitEntry);
// increment the sequence counter
sequenceHelper.increment();
// ////////////////// now create actual TOF entry //////////////////////////////////////////////
/* set amount in line so Debit/Credit code can be set correctly */
accountingLine.setAmount(streamAmount);
explicitEntry = new GeneralLedgerPendingEntry();
glpeService.populateExplicitGeneralLedgerPendingEntry(this, accountingLine, sequenceHelper, explicitEntry);
/* override and set object type to transfer */
explicitEntry.setFinancialObjectTypeCode(options.getFinancialObjectTypeTransferIncomeCd());
/* set document type to tof */
explicitEntry.setFinancialDocumentTypeCode(getTransferDocumentType());
// set fiscal period, if next fiscal year set to 01, else leave to current period
if (currentFiscalYear.equals(getPostingYear() - 1)) {
explicitEntry.setUniversityFiscalPeriodCode(BudgetAdjustmentDocumentRuleConstants.MONTH_1_PERIOD_CODE);
}
// add the new explicit entry to the document now
addPendingEntry(explicitEntry);
customizeExplicitGeneralLedgerPendingEntry(accountingLine, explicitEntry);
// increment the sequence counter
sequenceHelper.increment();
// ////////////////// now create actual TOF offset //////////////////////////////////////////////
GeneralLedgerPendingEntry offsetEntry = new GeneralLedgerPendingEntry(explicitEntry);
success &= glpeService.populateOffsetGeneralLedgerPendingEntry(getPostingYear(), explicitEntry, sequenceHelper, offsetEntry);
customizeOffsetGeneralLedgerPendingEntry(accountingLine, explicitEntry, offsetEntry);
addPendingEntry(offsetEntry);
// increment the sequence counter
sequenceHelper.increment();
}
}
}
return success;
}
/**
* Builds a map used for balancing current adjustment amounts. The map contains income chart and accounts contained on the
* document as the keys, and transfer amounts as the values. The transfer amount is calculated from (curr_frm_inc - curr_frm_exp) - (curr_to_inc - curr_to_exp)
*
* @param baDocument budget adjustment document
* @return Map used to balance current amounts
*/
public Map buildIncomeStreamBalanceMapForTransferOfFundsGeneration() {
Map<String, KualiDecimal> incomeStreamBalance = new HashMap<String, KualiDecimal>();
List<BudgetAdjustmentAccountingLine> accountingLines = new ArrayList<BudgetAdjustmentAccountingLine>();
accountingLines.addAll(getSourceAccountingLines());
accountingLines.addAll(getTargetAccountingLines());
ParameterEvaluatorService parameterEvaluatorService = SpringContext.getBean(ParameterEvaluatorService.class);
for (BudgetAdjustmentAccountingLine budgetAccountingLine : accountingLines) {
Account baAccount = budgetAccountingLine.getAccount();
if(parameterEvaluatorService.getParameterEvaluator(BudgetAdjustmentDocument.class, KFSConstants.BudgetAdjustmentDocumentConstants.CROSS_INCOME_STREAM_GLPE_TRANSFER_GENERATING_FUND_GROUPS, baAccount.getSubFundGroup().getFundGroupCode()).evaluationSucceeds() &&
parameterEvaluatorService.getParameterEvaluator(BudgetAdjustmentDocument.class, KFSConstants.BudgetAdjustmentDocumentConstants.CROSS_INCOME_STREAM_GLPE_TRANSFER_GENERATING_SUB_FUND_GROUPS, baAccount.getSubFundGroupCode()).evaluationSucceeds()) {
String incomeStreamKey = baAccount.getIncomeStreamFinancialCoaCode() + BudgetAdjustmentDocumentRuleConstants.INCOME_STREAM_CHART_ACCOUNT_DELIMITER + baAccount.getIncomeStreamAccountNumber();
// place record in balance map
incomeStreamBalance.put(incomeStreamKey, getIncomeStreamAmount(budgetAccountingLine, incomeStreamBalance.get(incomeStreamKey)));
}
}
return incomeStreamBalance;
}
/**
* Builds a map used for balancing current adjustment amounts. The map contains income chart and accounts contained on the
* document as the keys, and transfer amounts as the values. The transfer amount is calculated from (curr_frm_inc - curr_frm_exp) - (curr_to_inc - curr_to_exp)
*
* @param baDocument budget adjustment document
* @return Map used to balance current amounts
*/
public Map buildIncomeStreamBalanceMapForDocumentBalance() {
Map<String, KualiDecimal> incomeStreamBalance = new HashMap<String, KualiDecimal>();
List<BudgetAdjustmentAccountingLine> accountingLines = new ArrayList<BudgetAdjustmentAccountingLine>();
accountingLines.addAll(getSourceAccountingLines());
accountingLines.addAll(getTargetAccountingLines());
for (BudgetAdjustmentAccountingLine budgetAccountingLine : accountingLines) {
String incomeStreamKey = budgetAccountingLine.getAccount().getIncomeStreamFinancialCoaCode() + BudgetAdjustmentDocumentRuleConstants.INCOME_STREAM_CHART_ACCOUNT_DELIMITER + budgetAccountingLine.getAccount().getIncomeStreamAccountNumber();
// place record in balance map
incomeStreamBalance.put(incomeStreamKey, getIncomeStreamAmount(budgetAccountingLine, incomeStreamBalance.get(incomeStreamKey)));
}
return incomeStreamBalance;
}
/**
*
* This method calculates the appropriate income stream amount for an account using the value provided and the provided accounting line.
*
* @param budgetAccountingLine
* @param incomeStreamAmount
* @return
*/
protected KualiDecimal getIncomeStreamAmount(BudgetAdjustmentAccountingLine budgetAccountingLine, KualiDecimal incomeStreamAmount) {
if(incomeStreamAmount == null) {
incomeStreamAmount = new KualiDecimal(0);
}
// amounts need to be reversed for source expense lines and target income lines
DebitDeterminerService isDebitUtils = SpringContext.getBean(DebitDeterminerService.class);
if ((budgetAccountingLine instanceof BudgetAdjustmentSourceAccountingLine && isDebitUtils.isExpense(budgetAccountingLine)) || (budgetAccountingLine instanceof BudgetAdjustmentTargetAccountingLine && isDebitUtils.isIncome(budgetAccountingLine))) {
incomeStreamAmount = incomeStreamAmount.subtract(budgetAccountingLine.getCurrentBudgetAdjustmentAmount());
}
else {
incomeStreamAmount = incomeStreamAmount.add(budgetAccountingLine.getCurrentBudgetAdjustmentAmount());
}
return incomeStreamAmount;
}
/**
* Returns the document type code for the Transfer of Funds document
*
* @return the document type name to be used for the income stream transfer glpe
*/
protected String getTransferDocumentType() {
return TransferOfFundsDocumentRuleConstants.TRANSFER_OF_FUNDS_DOC_TYPE_CODE;
}
/**
* @see org.kuali.kfs.sys.document.FinancialSystemTransactionalDocumentBase#answerSplitNodeQuestion(java.lang.String)
*/
@Override
public boolean answerSplitNodeQuestion(String nodeName) throws UnsupportedOperationException {
if (nodeName.equals(BudgetAdjustmentDocument.REQUIRES_FULL_APPROVAL_SPLIT_NODE_NAME)) {
return requiresFullApproval();
}
return super.answerSplitNodeQuestion(nodeName);
}
/**
* Determines if this document can be auto-approved or not. The conditions for auto-approval are: 1) Single account used on document 2) Initiator is
* fiscal officer or primary delegate for the account 3) Only current adjustments are being made 4) The fund group for the account
* is not contract and grants 5) current income/expense decrease amount must equal increase amount
* @return false if auto-approval can occur (and therefore, full approval is not required); true if a full approval is required
*/
protected boolean requiresFullApproval() {
List<BudgetAdjustmentAccountingLine> accountingLines = new ArrayList<BudgetAdjustmentAccountingLine>();
accountingLines.addAll(getSourceAccountingLines());
accountingLines.addAll(getTargetAccountingLines());
HashSet<String> distinctAccts = new HashSet<String>();
HashSet<String> distinctObjs = new HashSet<String>();
String accountKey = "";
String objCdKey = "";
for (BudgetAdjustmentAccountingLine account : accountingLines) {
if(account.getBaseBudgetAdjustmentAmount().isNonZero()){
return true;
}
accountKey = account.getChartOfAccountsCode() + "-" + account.getAccountNumber();
objCdKey = account.getPostingYear() + "-" + account.getChartOfAccountsCode() + "-" + account.getFinancialObjectCode();
distinctAccts.add(accountKey);
distinctObjs.add(objCdKey);
if (distinctAccts.size() > 1 || distinctObjs.size() > 1) {
return true;
}
}
// check remaining conditions
// initiator should be fiscal officer or primary delegate for account
Person initiator = KimApiServiceLocator.getPersonService().getPerson(getDocumentHeader().getWorkflowDocument().getInitiatorPrincipalId());
AccountService acctService = SpringContext.getBean(AccountService.class);
for (Iterator iter1 = accountingLines.iterator(); iter1.hasNext();) {
BudgetAdjustmentAccountingLine line = (BudgetAdjustmentAccountingLine) iter1.next();
Account account = acctService.getByPrimaryId(line.getChartOfAccountsCode(), line.getAccountNumber());
boolean hasResponsibilityOnAccount= acctService.hasResponsibilityOnAccount(initiator, account);
Account userAccount = null;
if(hasResponsibilityOnAccount){
userAccount=account;
}
if (userAccount == null) {
return true;
}
else {
// fund group should not be CG
if (userAccount.isForContractsAndGrants()) {
return true;
}
// current income/expense decrease amount must equal increase amount
if (!getSourceCurrentBudgetIncomeTotal().equals(getTargetCurrentBudgetIncomeTotal()) || !getSourceCurrentBudgetExpenseTotal().equals(getTargetCurrentBudgetExpenseTotal())) {
return true;
}
}// End of else block.
}// End of for loop
return false;
}
}