/*
* 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 static org.kuali.kfs.fp.document.validation.impl.AuxiliaryVoucherDocumentRuleConstants.AUXILIARY_VOUCHER_ACCOUNTING_PERIOD_GRACE_PERIOD;
import static org.kuali.kfs.fp.document.validation.impl.AuxiliaryVoucherDocumentRuleConstants.GENERAL_LEDGER_PENDING_ENTRY_OFFSET_CODE;
import static org.kuali.kfs.sys.KFSConstants.GL_CREDIT_CODE;
import static org.kuali.kfs.sys.KFSConstants.GL_DEBIT_CODE;
import static org.kuali.kfs.sys.KFSConstants.AuxiliaryVoucher.ACCRUAL_DOC_TYPE;
import static org.kuali.kfs.sys.KFSConstants.AuxiliaryVoucher.ADJUSTMENT_DOC_TYPE;
import static org.kuali.kfs.sys.KFSConstants.AuxiliaryVoucher.RECODE_DOC_TYPE;
import java.sql.Date;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.kuali.kfs.coa.businessobject.AccountingPeriod;
import org.kuali.kfs.coa.service.AccountingPeriodService;
import org.kuali.kfs.gl.service.SufficientFundsService;
import org.kuali.kfs.sys.KFSConstants;
import org.kuali.kfs.sys.businessobject.AccountingLine;
import org.kuali.kfs.sys.businessobject.AccountingLineBase;
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.DebitDeterminerService;
import org.kuali.kfs.sys.service.OptionsService;
import org.kuali.kfs.sys.service.UniversityDateService;
import org.kuali.rice.core.api.datetime.DateTimeService;
import org.kuali.rice.core.api.util.type.KualiDecimal;
import org.kuali.rice.coreservice.framework.parameter.ParameterService;
import org.kuali.rice.kew.api.exception.WorkflowException;
import org.kuali.rice.kew.framework.postprocessor.DocumentRouteStatusChange;
import org.kuali.rice.krad.document.Copyable;
/**
* This is the business object that represents the AuxiliaryVoucherDocument in Kuali. This is a transactional document that will
* eventually post transactions to the G/L. It integrates with workflow and also contains two groupings of accounting lines: Expense
* and target. Expense is the expense and target is the income lines.
*/
public class AuxiliaryVoucherDocument extends AccountingDocumentBase implements VoucherDocument, Copyable, Correctable, AmountTotaling {
protected static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(AuxiliaryVoucherDocument.class);
protected String typeCode = ADJUSTMENT_DOC_TYPE;
protected java.sql.Date reversalDate;
/**
* @see org.kuali.kfs.sys.document.AccountingDocumentBase#documentPerformsSufficientFundsCheck()
*/
@Override
public boolean documentPerformsSufficientFundsCheck() {
if (isRecodeType()) {
return super.documentPerformsSufficientFundsCheck();
}
else {
return false;
}
}
/**
* Initializes the array lists and some basic info.
*/
public AuxiliaryVoucherDocument() {
super();
}
/**
* Read Accessor for Reversal Date
*
* @return java.sql.Date
*/
public java.sql.Date getReversalDate() {
return reversalDate;
}
/**
* Write Accessor for Reversal Date
*
* @param reversalDate
*/
public void setReversalDate(java.sql.Date reversalDate) {
this.reversalDate = reversalDate;
}
/**
* Read Accessor for Auxiliary Voucher Type
*
* @return String
*/
public String getTypeCode() {
return typeCode;
}
/**
* Write Accessor for Auxiliary Voucher Type
*
* @param typeCode
*/
public void setTypeCode(String typeCode) {
this.typeCode = typeCode;
}
/**
* A helper to test whether this document is an adjustment type AV.
*
* @return boolean
*/
public boolean isAdjustmentType() {
return ADJUSTMENT_DOC_TYPE.equals(typeCode);
}
/**
* A helper to test whether this document is an recode type AV.
*
* @return boolean
*/
public boolean isRecodeType() {
return RECODE_DOC_TYPE.equals(typeCode);
}
/**
* A helper to test whether this document is an accrual type AV.
*
* @return boolean
*/
public boolean isAccrualType() {
return ACCRUAL_DOC_TYPE.equals(typeCode);
}
/**
* This method calculates the debit total for a JV document keying off of the debit/debit code, only summing the accounting
* lines with a debitDebitCode that matched the debit constant, and returns the results.
*
* @return KualiDecimal
*/
public KualiDecimal getDebitTotal() {
KualiDecimal debitTotal = KualiDecimal.ZERO;
AccountingLineBase al = null;
Iterator<?> iter = sourceAccountingLines.iterator();
while (iter.hasNext()) {
al = (AccountingLineBase) iter.next();
if (StringUtils.isNotBlank(al.getDebitCreditCode()) && al.getDebitCreditCode().equals(KFSConstants.GL_DEBIT_CODE)) {
debitTotal = debitTotal.add(al.getAmount());
}
}
return debitTotal;
}
/**
* This method calculates the credit total for a JV document keying off of the debit/credit code, only summing the accounting
* lines with a debitCreditCode that matched the debit constant, and returns the results.
*
* @return KualiDecimal
*/
public KualiDecimal getCreditTotal() {
KualiDecimal creditTotal = KualiDecimal.ZERO;
AccountingLineBase al = null;
Iterator<?> iter = sourceAccountingLines.iterator();
while (iter.hasNext()) {
al = (AccountingLineBase) iter.next();
if (StringUtils.isNotBlank(al.getDebitCreditCode()) && al.getDebitCreditCode().equals(KFSConstants.GL_CREDIT_CODE)) {
creditTotal = creditTotal.add(al.getAmount());
}
}
return creditTotal;
}
/**
* Same as default implementation but uses debit / credit totals instead. Meaning it returns either credit or if 0, debit.
*
* @see org.kuali.kfs.sys.document.AccountingDocumentBase#getTotalDollarAmount()
* @return KualiDecimal
*/
@Override
public KualiDecimal getTotalDollarAmount() {
return getCreditTotal().equals(KualiDecimal.ZERO) ? getDebitTotal() : getCreditTotal();
}
/**
* @see org.kuali.kfs.sys.document.AccountingDocumentBase#toCopy()
*/
@Override
public void toCopy() throws WorkflowException {
super.toCopy();
refreshReversalDate();
}
/**
* Overrides to call super and then change the reversal date if the type is accrual and the date is greater than the set
* reversal date.
*/
@Override
public void doRouteStatusChange(DocumentRouteStatusChange statusChangeEvent) {
LOG.debug("In doRouteStatusChange() for AV documents");
super.doRouteStatusChange(statusChangeEvent);
if (this.getDocumentHeader().getWorkflowDocument().isProcessed()) { // only do this stuff if the document has been
// processed and approved
// update the reversal data accoringdingly
updateReversalDate();
}
}
/**
* This method handles updating the reversal data on the document in addition to all of the GLPEs, but only for the accrual and
* recode types.
*/
protected void updateReversalDate() {
if (refreshReversalDate()) {
// set the reversal date on each GLPE for the document too
List<GeneralLedgerPendingEntry> glpes = getGeneralLedgerPendingEntries();
for (GeneralLedgerPendingEntry entry : glpes) {
if (entry.getFinancialDocumentTypeCode().equals(KFSConstants.FinancialDocumentTypeCodes.DISTRIBUTION_OF_INCOME_AND_EXPENSE)) {
continue;
}
entry.setFinancialDocumentReversalDate(getReversalDate());
}
}
}
/**
* If the reversal date on this document is in need of refreshing, refreshes the reveral date. THIS METHOD MAY CHANGE DOCUMENT STATE!
* @return true if the reversal date ended up getting refreshed, false otherwise
*/
protected boolean refreshReversalDate() {
boolean refreshed = false;
if ((isAccrualType() || isRecodeType()) && getReversalDate() != null) {
java.sql.Date today = SpringContext.getBean(DateTimeService.class).getCurrentSqlDateMidnight();
if (getReversalDate().before(today)) {
// set the reversal date on the document
setReversalDate(today);
refreshed = true;
}
}
return refreshed;
}
/**
* @see org.kuali.kfs.sys.document.AccountingDocumentBase#toErrorCorrection()
*/
@Override
public void toErrorCorrection() throws WorkflowException {
super.toErrorCorrection();
processAuxiliaryVoucherErrorCorrections();
}
/**
* KULEDOCS-1700 This method iterates over each source line and flip the sign on the amount to nullify the super's effect, then
* flip the debit/credit code b/c an error corrected AV flips the debit/credit code.
*/
protected void processAuxiliaryVoucherErrorCorrections() {
Iterator<?> i = getSourceAccountingLines().iterator();
int index = 0;
while (i.hasNext()) {
SourceAccountingLine sLine = (SourceAccountingLine) i.next();
String debitCreditCode = sLine.getDebitCreditCode();
if (StringUtils.isNotBlank(debitCreditCode)) {
// negate the amount to to nullify the effects of the super, b/c super flipped it the first time through
sLine.setAmount(sLine.getAmount().negated()); // offsets the effect the super
// now just flip the debit/credit code
if (GL_DEBIT_CODE.equals(debitCreditCode)) {
sLine.setDebitCreditCode(GL_CREDIT_CODE);
}
else if (GL_CREDIT_CODE.equals(debitCreditCode)) {
sLine.setDebitCreditCode(GL_DEBIT_CODE);
}
else {
throw new IllegalStateException("SourceAccountingLine at index " + index + " does not have a debit/credit " + "code associated with it. This should never have occured. Please contact your system administrator.");
}
index++;
}
}
}
/**
* Returns true if an accounting line is a debit or credit The following are credits (return false)
* <ol>
* <li> debitCreditCode != 'D'
* </ol>
* the following are debits (return true)
* <ol>
* <li> debitCreditCode == 'D'
* </ol>
* the following are invalid ( throws an <code>IllegalStateException</code>)
* <ol>
* <li> debitCreditCode isBlank
* </ol>
*
* @param financialDocument submitted accounting document
* @param accounttingLine accounting line being tested if it is a debit or not
* @see org.kuali.rice.krad.rule.AccountingLineRule#isDebit(org.kuali.rice.krad.document.FinancialDocument,
* org.kuali.rice.krad.bo.AccountingLine)
*/
public boolean isDebit(GeneralLedgerPendingEntrySourceDetail postable) throws IllegalStateException {
String debitCreditCode = ((AccountingLine)postable).getDebitCreditCode();
DebitDeterminerService isDebitUtils = SpringContext.getBean(DebitDeterminerService.class);
if (StringUtils.isBlank(debitCreditCode)) {
throw new IllegalStateException(isDebitUtils.getDebitCalculationIllegalStateExceptionMessage());
}
return isDebitUtils.isDebitCode(debitCreditCode);
}
/**
* This method sets the appropriate document type and object type codes into the GLPEs based on the type of AV document chosen.
*
* @param document submitted AccountingDocument
* @param accountingLine represents accounting line where object type code is retrieved from
* @param explicitEntry GeneralPendingLedgerEntry object that has its document type, object type, period code, and fiscal year
* set
* @see FinancialDocumentRuleBase#customizeExplicitGeneralLedgerPendingEntry(FinancialDocument, AccountingLine,
* GeneralLedgerPendingEntry)
* @see org.kuali.module.financial.rules.FinancialDocumentRuleBase#processExplicitGeneralLedgerPendingEntry(org.kuali.rice.krad.document.FinancialDocument,
* org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySequenceHelper, org.kuali.rice.krad.bo.AccountingLine,
* org.kuali.module.gl.bo.GeneralLedgerPendingEntry)
*/
@Override
public void customizeExplicitGeneralLedgerPendingEntry(GeneralLedgerPendingEntrySourceDetail postable, GeneralLedgerPendingEntry explicitEntry) {
java.sql.Date reversalDate = getReversalDate();
if (reversalDate != null) {
explicitEntry.setFinancialDocumentReversalDate(reversalDate);
}
else {
explicitEntry.setFinancialDocumentReversalDate(null);
}
explicitEntry.setFinancialDocumentTypeCode(getTypeCode()); // make sure to use the accrual type as the document
// type
explicitEntry.setFinancialObjectTypeCode(getObjectTypeCode(postable));
explicitEntry.setUniversityFiscalPeriodCode(getPostingPeriodCode()); // use chosen posting period code
explicitEntry.setUniversityFiscalYear(getPostingYear()); // use chosen posting year
}
/**
* Offset entries are created for recodes (AVRC) always, so this method is one of 2 offsets that get created for an AVRC. Its
* document type is set to DI. This uses the explicit entry as its model. In addition, an offset is generated for accruals
* (AVAE) and adjustments (AVAD), but only if the document contains accounting lines for more than one account.
*
* @param financialDocument submitted accounting document
* @param accountingLine accounting line from accounting document
* @param explicitEntry represents explicit entry
* @param offsetEntry represents offset entry
* @see org.kuali.module.financial.rules.FinancialDocumentRuleBase#customizeOffsetGeneralLedgerPendingEntry(org.kuali.rice.krad.document.FinancialDocument,
* org.kuali.rice.krad.bo.AccountingLine, org.kuali.module.gl.bo.GeneralLedgerPendingEntry,
* org.kuali.module.gl.bo.GeneralLedgerPendingEntry)
*/
@Override
public boolean customizeOffsetGeneralLedgerPendingEntry(GeneralLedgerPendingEntrySourceDetail postable, GeneralLedgerPendingEntry explicitEntry, GeneralLedgerPendingEntry offsetEntry) {
// set the document type to that of a Distrib. Of Income and Expense if it's a recode
if (isRecodeType()) {
offsetEntry.setFinancialDocumentTypeCode(KFSConstants.FinancialDocumentTypeCodes.DISTRIBUTION_OF_INCOME_AND_EXPENSE);
// set the posting period
java.sql.Date today = SpringContext.getBean(DateTimeService.class).getCurrentSqlDateMidnight();
offsetEntry.setUniversityFiscalPeriodCode(SpringContext.getBean(AccountingPeriodService.class).getByDate(today).getUniversityFiscalPeriodCode()); // use
}
// now set the offset entry to the specific offset object code for the AV generated offset fund balance; only if it's an
// accrual or adjustment
if (isAccrualType() || isAdjustmentType()) {
String glpeOffsetObjectCode = SpringContext.getBean(ParameterService.class).getParameterValueAsString(this.getClass(), getGeneralLedgerPendingEntryOffsetObjectCode());
offsetEntry.setFinancialObjectCode(glpeOffsetObjectCode);
// set the posting period
offsetEntry.setUniversityFiscalPeriodCode(getPostingPeriodCode()); // use chosen posting period code
}
// set the reversal date to null
offsetEntry.setFinancialDocumentReversalDate(null);
// set the year to current
offsetEntry.setUniversityFiscalYear(getPostingYear()); // use chosen posting year
// although they are offsets, we need to set the offset indicator to false
offsetEntry.setTransactionEntryOffsetIndicator(false);
//KFSMI-798 - refreshNonUpdatableReferences() used instead of refresh(),
//GeneralLedgerPendingEntry does not have any updatable references
offsetEntry.refreshNonUpdateableReferences(); // may have changed foreign keys here; need accurate object code and account BOs at least
offsetEntry.setAcctSufficientFundsFinObjCd(SpringContext.getBean(SufficientFundsService.class).getSufficientFundsObjectCode(offsetEntry.getFinancialObject(), offsetEntry.getAccount().getAccountSufficientFundsCode()));
return true;
}
/**
* This method examines the accounting line passed in and returns the appropriate object type code. This rule converts specific
* objects types from an object code on an accounting line to more general values. This is specific to the AV document.
*
* @param line accounting line where object type code is retrieved from
* @return object type from a accounting line ((either financial object type code, financial object type not expenditure code,
* or financial object type income not cash code))
*/
protected String getObjectTypeCode(GeneralLedgerPendingEntrySourceDetail line) {
SystemOptions options = SpringContext.getBean(OptionsService.class).getCurrentYearOptions();
String objectTypeCode = line.getObjectCode().getFinancialObjectTypeCode();
if (options.getFinObjTypeExpenditureexpCd().equals(objectTypeCode) || options.getFinObjTypeExpendNotExpCode().equals(objectTypeCode)) {
objectTypeCode = options.getFinObjTypeExpNotExpendCode();
}
else if (options.getFinObjectTypeIncomecashCode().equals(objectTypeCode) || options.getFinObjTypeExpendNotExpCode().equals(objectTypeCode)) {
objectTypeCode = options.getFinObjTypeIncomeNotCashCd();
}
return objectTypeCode;
}
/**
* Get from APC the offset object code that is used for the <code>{@link GeneralLedgerPendingEntry}</code>
*
* @return String returns GLPE parameter name
*/
protected String getGeneralLedgerPendingEntryOffsetObjectCode() {
return GENERAL_LEDGER_PENDING_ENTRY_OFFSET_CODE;
}
/**
* An Accrual Voucher only generates offsets if it is a recode (AVRC). So this method overrides to do nothing more than return
* true if it's not a recode. If it is a recode, then it is responsible for generating two offsets with a document type of DI.
*
* @param financialDocument submitted accounting document
* @param sequenceHelper helper class which will allows us to increment a reference without using an Integer
* @param accountingLineCopy accounting line from accounting document
* @param explicitEntry represents explicit entry
* @param offsetEntry represents offset entry
* @return true if general ledger pending entry is processed successfully for accurals, adjustments, and recodes
* @see org.kuali.module.financial.rules.FinancialDocumentRuleBase#processOffsetGeneralLedgerPendingEntry(org.kuali.rice.krad.document.FinancialDocument,
* org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySequenceHelper, org.kuali.rice.krad.bo.AccountingLine,
* org.kuali.module.gl.bo.GeneralLedgerPendingEntry, org.kuali.module.gl.bo.GeneralLedgerPendingEntry)
*/
@Override
public boolean processOffsetGeneralLedgerPendingEntry(GeneralLedgerPendingEntrySequenceHelper sequenceHelper, GeneralLedgerPendingEntrySourceDetail glpeSourceDetail, GeneralLedgerPendingEntry explicitEntry, GeneralLedgerPendingEntry offsetEntry) {
AccountingLine accountingLineCopy = (AccountingLine)glpeSourceDetail;
boolean success = true;
// do not generate an offset entry if this is a normal or adjustment AV type
if (isAccrualType() || isAdjustmentType()) {
success &= processOffsetGeneralLedgerPendingEntryForAccrualsAndAdjustments(sequenceHelper, accountingLineCopy, explicitEntry, offsetEntry);
}
else if (isRecodeType()) { // recodes generate offsets
success &= processOffsetGeneralLedgerPendingEntryForRecodes(sequenceHelper, accountingLineCopy, explicitEntry, offsetEntry);
}
else {
throw new IllegalStateException("Illegal auxiliary voucher type: " + getTypeCode());
}
return success;
}
/**
* This method handles generating or not generating the appropriate offsets if the AV type is a recode.
*
* @param financialDocument submitted accounting document
* @param sequenceHelper helper class which will allows us to increment a reference without using an Integer
* @param accountingLineCopy accounting line from accounting document
* @param explicitEntry represents explicit entry
* @param offsetEntry represents offset entry
* @return true if offset general ledger pending entry is processed
*/
protected boolean processOffsetGeneralLedgerPendingEntryForRecodes(GeneralLedgerPendingEntrySequenceHelper sequenceHelper, AccountingLine accountingLineCopy, GeneralLedgerPendingEntry explicitEntry, GeneralLedgerPendingEntry offsetEntry) {
// the explicit entry has already been generated and added to the list, so to get the right offset, we have to set the value
// of the document type code on the explicit
// to the type code for a DI document so that it gets passed into the next call and we retrieve the right offset definition
// since these offsets are
// specific to Distrib. of Income and Expense documents - we need to do a deep copy though so we don't do this by reference
GeneralLedgerPendingEntry explicitEntryDeepCopy = new GeneralLedgerPendingEntry(explicitEntry);
explicitEntryDeepCopy.setFinancialDocumentTypeCode(KFSConstants.FinancialDocumentTypeCodes.DISTRIBUTION_OF_INCOME_AND_EXPENSE);
// set the posting period to current, because DI GLPEs for recodes should post to the current period
java.sql.Date today = SpringContext.getBean(DateTimeService.class).getCurrentSqlDateMidnight();
explicitEntryDeepCopy.setUniversityFiscalPeriodCode(SpringContext.getBean(AccountingPeriodService.class).getByDate(today).getUniversityFiscalPeriodCode()); // use
// call the super to process an offset entry; see the customize method below for AVRC specific attribute values
// pass in the explicit deep copy
boolean success = super.processOffsetGeneralLedgerPendingEntry(sequenceHelper, accountingLineCopy, explicitEntryDeepCopy, offsetEntry);
// increment the sequence appropriately
sequenceHelper.increment();
// now generate the AVRC DI entry
// pass in the explicit deep copy
processAuxiliaryVoucherRecodeDistributionOfIncomeAndExpenseGeneralLedgerPendingEntry( sequenceHelper, explicitEntryDeepCopy);
return success;
}
/**
* This method handles generating or not generating the appropriate offsets if the AV type is accrual or adjustment.
*
* @param financialDocument submitted accounting document
* @param sequenceHelper helper class which will allows us to increment a reference without using an Integer
* @param accountingLineCopy accounting line from accounting document
* @param explicitEntry represents explicit entry
* @param offsetEntry represents offset entry
* @return true if offset general ledger pending entry is processed successfully
*/
protected boolean processOffsetGeneralLedgerPendingEntryForAccrualsAndAdjustments(GeneralLedgerPendingEntrySequenceHelper sequenceHelper, AccountingLine accountingLineCopy, GeneralLedgerPendingEntry explicitEntry, GeneralLedgerPendingEntry offsetEntry) {
boolean success = true;
if (this.isDocumentForMultipleAccounts()) {
success = super.processOffsetGeneralLedgerPendingEntry(sequenceHelper, accountingLineCopy, explicitEntry, offsetEntry);
}
else {
sequenceHelper.decrement(); // the parent already increments; b/c it assumes that all documents have offset entries all
// of the time
}
return success;
}
/**
* This method is responsible for iterating through all of the accounting lines in the document (source only) and checking to
* see if they are all for the same account or not. It recognizes the first account element as the base, and then it iterates
* through the rest. If it comes across one that doesn't match, then we know it's for multiple accounts.
*
* @param financialDocument submitted accounting document
* @return true if multiple accounts are being used
*/
protected boolean isDocumentForMultipleAccounts() {
String baseAccountNumber = "";
int index = 0;
List<AccountingLine> lines = this.getSourceAccountingLines();
for (AccountingLine line : lines) {
if (index == 0) {
baseAccountNumber = line.getAccountNumber();
}
else if (!baseAccountNumber.equals(line.getAccountNumber())) {
return true;
}
index++;
}
return false;
}
/**
* This method creates an AV recode specific GLPE with a document type of DI. The sequence is managed outside of this method. It
* uses the explicit entry as its model and then tweaks values appropriately.
*
* @param financialDocument submitted accounting document
* @param sequenceHelper helper class which will allows us to increment a reference without using an Integer
* @param explicitEntry represents explicit entry
* @return true if recode GLPE is added to the financial document successfully
*/
protected void processAuxiliaryVoucherRecodeDistributionOfIncomeAndExpenseGeneralLedgerPendingEntry(GeneralLedgerPendingEntrySequenceHelper sequenceHelper, GeneralLedgerPendingEntry explicitEntry) {
// create a new instance based off of the explicit entry
GeneralLedgerPendingEntry recodeGlpe = new GeneralLedgerPendingEntry(explicitEntry);
// set the sequence number according to what was passed in - this is managed external to this method
recodeGlpe.setTransactionLedgerEntrySequenceNumber(new Integer(sequenceHelper.getSequenceCounter()));
// set the document type to that of a Distrib. Of Income and Expense
recodeGlpe.setFinancialDocumentTypeCode(KFSConstants.FinancialDocumentTypeCodes.DISTRIBUTION_OF_INCOME_AND_EXPENSE);
// set the object type code base on the value of the explicit entry
recodeGlpe.setFinancialObjectTypeCode(getObjectTypeCodeForRecodeDistributionOfIncomeAndExpenseEntry(explicitEntry));
// set the reversal date to null
recodeGlpe.setFinancialDocumentReversalDate(null);
// although this is an offsets, we need to set the offset indicator to false
recodeGlpe.setTransactionEntryOffsetIndicator(false);
// add the new recode offset entry to the document now
addPendingEntry(recodeGlpe);
}
/**
* This method examines the explicit entry's object type and returns the appropriate object type code. This is specific to AV
* recodes (AVRCs).
*
* @param explicitEntry
* @return object type code from explicit entry (either financial object type code, financial object type expenditure code, or
* financial object type income cash code)
*/
protected String getObjectTypeCodeForRecodeDistributionOfIncomeAndExpenseEntry(GeneralLedgerPendingEntry explicitEntry) {
SystemOptions options = SpringContext.getBean(OptionsService.class).getCurrentYearOptions();
String objectTypeCode = explicitEntry.getFinancialObjectTypeCode();
if (options.getFinObjTypeExpNotExpendCode().equals(objectTypeCode)) {
objectTypeCode = options.getFinObjTypeExpenditureexpCd();
}
else if (options.getFinObjTypeIncomeNotCashCd().equals(objectTypeCode)) {
objectTypeCode = options.getFinObjectTypeIncomecashCode();
}
return objectTypeCode;
}
/**
* This method checks if a given moment of time is within an accounting period, or its auxiliary voucher grace period.
*
* @param today a date to check if it is within the period
* @param periodToCheck the account period to check against
* @return true if a given moment in time is within an accounting period or an auxiliary voucher grace period
*/
public boolean calculateIfWithinGracePeriod(Date today, AccountingPeriod periodToCheck) {
boolean result = false;
final int todayAsComparableDate = comparableDateForm(today);
final int periodClose = new Integer(comparableDateForm(periodToCheck.getUniversityFiscalPeriodEndDate()));
final int periodBegin = comparableDateForm(calculateFirstDayOfMonth(periodToCheck.getUniversityFiscalPeriodEndDate()));
final int gracePeriodClose = periodClose + new Integer(SpringContext.getBean(ParameterService.class).getParameterValueAsString(getClass(), AUXILIARY_VOUCHER_ACCOUNTING_PERIOD_GRACE_PERIOD)).intValue();
return (todayAsComparableDate >= periodBegin && todayAsComparableDate <= gracePeriodClose);
}
/**
* This method returns a date as an approximate count of days since the BCE epoch.
*
* @param d the date to convert
* @return an integer count of days, very approximate
*/
public int comparableDateForm(Date d) {
java.util.Calendar cal = new java.util.GregorianCalendar();
cal.setTime(d);
return cal.get(java.util.Calendar.YEAR) * 365 + cal.get(java.util.Calendar.DAY_OF_YEAR);
}
/**
* Given a day, this method calculates what the first day of that month was.
*
* @param d date to find first of month for
* @return date of the first day of the month
*/
public Date calculateFirstDayOfMonth(Date d) {
java.util.Calendar cal = new java.util.GregorianCalendar();
cal.setTime(d);
int dayOfMonth = cal.get(java.util.Calendar.DAY_OF_MONTH) - 1;
cal.add(java.util.Calendar.DAY_OF_YEAR, -1 * dayOfMonth);
return new Date(cal.getTimeInMillis());
}
/**
* This method checks if the given accounting period ends on the last day of the previous fiscal year
*
* @param acctPeriod accounting period to check
* @return true if the accounting period ends with the fiscal year, false if otherwise
*/
public boolean isEndOfPreviousFiscalYear(AccountingPeriod acctPeriod) {
final UniversityDateService universityDateService = SpringContext.getBean(UniversityDateService.class);
final Date firstDayOfCurrFiscalYear = new Date(universityDateService.getFirstDateOfFiscalYear(universityDateService.getCurrentFiscalYear()).getTime());
final Date periodClose = acctPeriod.getUniversityFiscalPeriodEndDate();
java.util.Calendar cal = new java.util.GregorianCalendar();
cal.setTime(periodClose);
cal.add(java.util.Calendar.DATE, 1);
return (firstDayOfCurrFiscalYear.equals(new Date(cal.getTimeInMillis())));
}
}