/*
* 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.List;
import org.kuali.kfs.fp.businessobject.AdvanceDepositDetail;
import org.kuali.kfs.sys.KFSConstants;
import org.kuali.kfs.sys.KFSKeyConstants;
import org.kuali.kfs.sys.KFSPropertyConstants;
import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntry;
import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySequenceHelper;
import org.kuali.kfs.sys.context.SpringContext;
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.service.BankService;
import org.kuali.kfs.sys.service.ElectronicPaymentClaimingService;
import org.kuali.kfs.sys.service.GeneralLedgerPendingEntryService;
import org.kuali.rice.core.api.util.type.KualiDecimal;
import org.kuali.rice.core.web.format.CurrencyFormatter;
import org.kuali.rice.kew.api.exception.WorkflowException;
import org.kuali.rice.kew.framework.postprocessor.DocumentRouteStatusChange;
import org.kuali.rice.kns.service.DataDictionaryService;
import org.kuali.rice.krad.document.Copyable;
import org.kuali.rice.krad.rules.rule.event.KualiDocumentEvent;
import org.kuali.rice.krad.rules.rule.event.SaveDocumentEvent;
/**
* This is the business object that represents the AdvanceDeposit document in Kuali. This is a transactional document that will
* eventually post transactions to the G/L. It integrates with workflow. Since an Advance Deposit document is a one sided
* transactional document, only accepting funds into the university, the accounting line data will be held in the source accounting
* line data structure only.
*/
public class AdvanceDepositDocument extends CashReceiptFamilyBase implements Copyable, AmountTotaling, Correctable {
public static final String ADVANCE_DEPOSIT_DOCUMENT_TYPE_CODE = "AD";
// holds details about each advance deposit
protected List<AdvanceDepositDetail> advanceDeposits = new ArrayList<AdvanceDepositDetail>();
// incrementers for detail lines
protected Integer nextAdvanceDepositLineNumber = 1;
// monetary attributes
protected KualiDecimal totalAdvanceDepositAmount = KualiDecimal.ZERO;
/**
* Default constructor that calls super.
*/
public AdvanceDepositDocument() {
super();
}
/**
* Gets the total advance deposit amount.
*
* @return KualiDecimal
*/
public KualiDecimal getTotalAdvanceDepositAmount() {
return totalAdvanceDepositAmount;
}
/**
* This method returns the advance deposit total amount as a currency formatted string.
*
* @return String
*/
public String getCurrencyFormattedTotalAdvanceDepositAmount() {
return (String) new CurrencyFormatter().format(totalAdvanceDepositAmount);
}
/**
* Sets the total advance deposit amount which is the sum of all advance deposits on this document.
*
* @param advanceDepositAmount
*/
public void setTotalAdvanceDepositAmount(KualiDecimal advanceDepositAmount) {
this.totalAdvanceDepositAmount = advanceDepositAmount;
}
/**
* Gets the list of advance deposits which is a list of AdvanceDepositDetail business objects.
*
* @return List
*/
public List<AdvanceDepositDetail> getAdvanceDeposits() {
return advanceDeposits;
}
/**
* Sets the advance deposits list.
*
* @param advanceDeposits
*/
public void setAdvanceDeposits(List<AdvanceDepositDetail> advanceDeposits) {
this.advanceDeposits = advanceDeposits;
}
/**
* Adds a new advance deposit to the list.
*
* @param advanceDepositDetail
*/
public void addAdvanceDeposit(AdvanceDepositDetail advanceDepositDetail) {
// these three make up the primary key for an advance deposit detail record
prepareNewAdvanceDeposit(advanceDepositDetail);
// add the new detail record to the list
this.advanceDeposits.add(advanceDepositDetail);
// increment line number
this.nextAdvanceDepositLineNumber++;
// update the overall amount
this.totalAdvanceDepositAmount = this.totalAdvanceDepositAmount.add(advanceDepositDetail.getFinancialDocumentAdvanceDepositAmount());
}
/**
* This is a helper method that automatically populates document specfic information into the advance deposit
* (AdvanceDepositDetail) instance.
*
* @param advanceDepositDetail
*/
public final void prepareNewAdvanceDeposit(AdvanceDepositDetail advanceDepositDetail) {
advanceDepositDetail.setFinancialDocumentLineNumber(this.nextAdvanceDepositLineNumber);
advanceDepositDetail.setDocumentNumber(this.getDocumentNumber());
advanceDepositDetail.setFinancialDocumentTypeCode(SpringContext.getBean(DataDictionaryService.class).getDocumentTypeNameByClass(this.getClass()));
}
/**
* Retrieve a particular advance deposit at a given index in the list of advance deposits.
*
* @param index
* @return AdvanceDepositDetail
*/
public AdvanceDepositDetail getAdvanceDepositDetail(int index) {
while (this.advanceDeposits.size() <= index) {
advanceDeposits.add(new AdvanceDepositDetail());
}
return advanceDeposits.get(index);
}
/**
* This method removes an advance deposit from the list and updates the total appropriately.
*
* @param index
*/
public void removeAdvanceDeposit(int index) {
AdvanceDepositDetail advanceDepositDetail = advanceDeposits.remove(index);
this.totalAdvanceDepositAmount = this.totalAdvanceDepositAmount.subtract(advanceDepositDetail.getFinancialDocumentAdvanceDepositAmount());
}
/**
* @return Integer
*/
public Integer getNextAdvanceDepositLineNumber() {
return nextAdvanceDepositLineNumber;
}
/**
* @param nextAdvanceDepositLineNumber
*/
public void setNextAdvanceDepositLineNumber(Integer nextAdvanceDepositLineNumber) {
this.nextAdvanceDepositLineNumber = nextAdvanceDepositLineNumber;
}
/**
* This method returns the overall total of the document - the advance deposit total.
*
* @see org.kuali.kfs.sys.document.AccountingDocumentBase#getTotalDollarAmount()
* @return KualiDecimal
*/
@Override
public KualiDecimal getTotalDollarAmount() {
return this.totalAdvanceDepositAmount;
}
/**
* This method defers to its parent's version of handleRouteStatusChange, but then, if the document is processed, it creates
* ElectronicPaymentClaim records for any qualifying accountings lines in the document.
*
* @see org.kuali.kfs.sys.document.GeneralLedgerPostingDocumentBase#doRouteStatusChange()
*/
@Override
public void doRouteStatusChange(DocumentRouteStatusChange statusChangeEvent) {
super.doRouteStatusChange(statusChangeEvent);
if (getDocumentHeader().getWorkflowDocument().isProcessed()) {
SpringContext.getBean(ElectronicPaymentClaimingService.class).generateElectronicPaymentClaimRecords(this);
}
this.getCapitalAssetManagementModuleService().deleteDocumentAssetLocks(this);
}
/**
* Overrides super to call super and then also add in the new list of advance deposits that have to be managed.
*
* @see org.kuali.rice.krad.document.TransactionalDocumentBase#buildListOfDeletionAwareLists()
*/
@Override
public List buildListOfDeletionAwareLists() {
List managedLists = super.buildListOfDeletionAwareLists();
managedLists.add(getAdvanceDeposits());
return managedLists;
}
/**
* Generates bank offset GLPEs for deposits, if enabled.
*
* @param financialDocument submitted financial document
* @param sequenceHelper helper class which will allows us to increment a reference without using an Integer
* @return true if there are no issues creating GLPE's
* @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;
GeneralLedgerPendingEntryService glpeService = SpringContext.getBean(GeneralLedgerPendingEntryService.class);
if (SpringContext.getBean(BankService.class).isBankSpecificationEnabled()) {
int displayedDepositNumber = 1;
for (AdvanceDepositDetail detail : getAdvanceDeposits()) {
detail.refreshReferenceObject(KFSPropertyConstants.BANK);
GeneralLedgerPendingEntry bankOffsetEntry = new GeneralLedgerPendingEntry();
if (!glpeService.populateBankOffsetGeneralLedgerPendingEntry(detail.getBank(), detail.getFinancialDocumentAdvanceDepositAmount(), this, getPostingYear(), sequenceHelper, bankOffsetEntry, KFSConstants.ADVANCE_DEPOSITS_LINE_ERRORS)) {
success = false;
continue; // An unsuccessfully populated bank offset entry may contain invalid relations, so don't add it
}
AccountingDocumentRuleHelperService accountingDocumentRuleUtil = SpringContext.getBean(AccountingDocumentRuleHelperService.class);
bankOffsetEntry.setTransactionLedgerEntryDescription(accountingDocumentRuleUtil.formatProperty(KFSKeyConstants.AdvanceDeposit.DESCRIPTION_GLPE_BANK_OFFSET, displayedDepositNumber++));
addPendingEntry(bankOffsetEntry);
sequenceHelper.increment();
GeneralLedgerPendingEntry offsetEntry = new GeneralLedgerPendingEntry(bankOffsetEntry);
success &= glpeService.populateOffsetGeneralLedgerPendingEntry(getPostingYear(), bankOffsetEntry, sequenceHelper, offsetEntry);
addPendingEntry(offsetEntry);
sequenceHelper.increment();
}
}
return success;
}
@Override
public void postProcessSave(KualiDocumentEvent event) {
super.postProcessSave(event);
if (!(event instanceof SaveDocumentEvent)) { // don't lock until they route
String documentTypeName = SpringContext.getBean(DataDictionaryService.class).getDocumentTypeNameByClass(this.getClass());
this.getCapitalAssetManagementModuleService().generateCapitalAssetLock(this,documentTypeName);
}
}
/**
* @see org.kuali.kfs.sys.document.AccountingDocumentBase#toErrorCorrection()
*/
@Override
public void toErrorCorrection() throws WorkflowException {
super.toErrorCorrection();
correctAdvanceDepositDetails();
correctCapitalAccountingLines();
}
/**
* Upon error correction, negates amount in each advance deposit detail, and updates the documentNumber to point to the new document.
*/
protected void correctAdvanceDepositDetails() {
for (AdvanceDepositDetail deposit: advanceDeposits) {
deposit.setVersionNumber(new Long(1));
deposit.setDocumentNumber(documentNumber);
deposit.setFinancialDocumentAdvanceDepositAmount(deposit.getFinancialDocumentAdvanceDepositAmount().negated());
}
}
}