/* * 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.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.kuali.kfs.fp.businessobject.Check; import org.kuali.kfs.fp.businessobject.CheckBase; import org.kuali.kfs.fp.businessobject.CoinDetail; import org.kuali.kfs.fp.businessobject.CurrencyDetail; import org.kuali.kfs.fp.businessobject.DepositCashReceiptControl; import org.kuali.kfs.fp.document.service.CashReceiptService; import org.kuali.kfs.fp.document.validation.event.AddCheckEvent; import org.kuali.kfs.fp.document.validation.event.DeleteCheckEvent; import org.kuali.kfs.fp.document.validation.event.UpdateCheckEvent; import org.kuali.kfs.fp.service.CheckService; import org.kuali.kfs.sys.KFSConstants; import org.kuali.kfs.sys.KFSConstants.CurrencyCoinSources; import org.kuali.kfs.sys.KFSConstants.DocumentStatusCodes; import org.kuali.kfs.sys.KFSConstants.DocumentStatusCodes.CashReceipt; import org.kuali.kfs.sys.businessobject.ChartOrgHolder; import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySourceDetail; import org.kuali.kfs.sys.businessobject.SufficientFundsItem; import org.kuali.kfs.sys.context.SpringContext; import org.kuali.kfs.sys.document.AmountTotaling; import org.kuali.kfs.sys.document.service.DebitDeterminerService; import org.kuali.rice.core.api.datetime.DateTimeService; import org.kuali.rice.core.api.util.type.KualiDecimal; import org.kuali.rice.core.web.format.CurrencyFormatter; import org.kuali.rice.kew.api.WorkflowDocument; import org.kuali.rice.kew.api.document.DocumentStatus; import org.kuali.rice.kew.api.exception.WorkflowException; import org.kuali.rice.kew.framework.postprocessor.DocumentRouteStatusChange; import org.kuali.rice.kim.api.identity.Person; 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; import org.kuali.rice.krad.service.BusinessObjectService; import org.kuali.rice.krad.util.GlobalVariables; import org.kuali.rice.krad.util.ObjectUtils; /** * This is the business object that represents the CashReceiptDocument in Kuali. This is a transactional document that will * eventually post transactions to the G/L. It integrates with workflow. Since a Cash Receipt 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 CashReceiptDocument extends CashReceiptFamilyBase implements Copyable, AmountTotaling, CapitalAssetEditable { private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(CashReceiptDocument.class); public static final String CHECK_ENTRY_DETAIL = "individual"; public static final String CHECK_ENTRY_TOTAL = "totals"; public static final String DOCUMENT_TYPE = "CR"; public static final String REQUIRE_REVIEW_SPLIT = "RequireChangeRequestReview"; // child object containers - for all the different reconciliation detail sections protected String checkEntryMode = CHECK_ENTRY_DETAIL; protected List<Check> checks = new ArrayList<Check>(); protected List<Check> confirmedChecks = new ArrayList<Check>(); // deposit controls protected List<DepositCashReceiptControl> depositCashReceiptControl = new ArrayList<DepositCashReceiptControl>(); // incrementers for detail lines protected Integer nextCheckSequenceId = new Integer(1); protected Integer nextConfirmedCheckSequenceId = new Integer(1); // monetary attributes // These total amount fields except the check ones aren't needed, since their values are computed by the getters; // the setters are never used and aren't needed; also these fields exist in DB, but the values are never set. protected KualiDecimal totalCurrencyAmount = KualiDecimal.ZERO; protected KualiDecimal totalCheckAmount = KualiDecimal.ZERO; protected KualiDecimal totalCoinAmount = KualiDecimal.ZERO; protected KualiDecimal sumTotalAmount = KualiDecimal.ZERO; protected KualiDecimal totalConfirmedCurrencyAmount = KualiDecimal.ZERO; protected KualiDecimal totalConfirmedCheckAmount = KualiDecimal.ZERO; protected KualiDecimal totalConfirmedCoinAmount = KualiDecimal.ZERO; protected KualiDecimal totalChangeAmount = KualiDecimal.ZERO; protected CurrencyDetail currencyDetail; protected CoinDetail coinDetail; protected CurrencyDetail confirmedCurrencyDetail; protected CoinDetail confirmedCoinDetail; protected CurrencyDetail changeCurrencyDetail; protected CoinDetail changeCoinDetail; protected CurrencyDetail confirmedChangeCurrencyDetail; protected CoinDetail confirmedChangeCoinDetail; protected boolean recategorized; protected String createDate; /** * Initializes the array lists and line incrementers. */ public CashReceiptDocument() { super(); initializeCampusLocationCode(); initializeCashChangeDetails(); } /** * Initializes all currency/coin and change currency/coin details. * This ensures that these details won't be null. */ protected void initializeCashChangeDetails() { currencyDetail = new CurrencyDetail(getDocumentNumber(), CashReceiptDocument.DOCUMENT_TYPE, CurrencyCoinSources.CASH_RECEIPTS); coinDetail = new CoinDetail(getDocumentNumber(), CashReceiptDocument.DOCUMENT_TYPE, CurrencyCoinSources.CASH_RECEIPTS); confirmedCurrencyDetail = new CurrencyDetail(getDocumentNumber(), CashReceiptDocument.DOCUMENT_TYPE, CurrencyCoinSources.CASH_MANAGEMENT_IN); confirmedCoinDetail = new CoinDetail(getDocumentNumber(), CashReceiptDocument.DOCUMENT_TYPE, CurrencyCoinSources.CASH_MANAGEMENT_IN); changeCurrencyDetail = new CurrencyDetail(getDocumentNumber(), CashReceiptDocument.DOCUMENT_TYPE, CurrencyCoinSources.CASH_CHANGE_REQUEST); changeCoinDetail = new CoinDetail(getDocumentNumber(), CashReceiptDocument.DOCUMENT_TYPE, CurrencyCoinSources.CASH_CHANGE_REQUEST); confirmedChangeCurrencyDetail = new CurrencyDetail(getDocumentNumber(), CashReceiptDocument.DOCUMENT_TYPE, CurrencyCoinSources.CASH_CHANGE_GRANTED); confirmedChangeCoinDetail = new CoinDetail(getDocumentNumber(), CashReceiptDocument.DOCUMENT_TYPE, CurrencyCoinSources.CASH_CHANGE_GRANTED); } /** * Sets the non-amount primary key fields for all currency/coin and change currency/coin details. * This ensures that the key fields of these details are populated properly, in case they weren't during initialization. */ protected void setCashChangeDetailKeys() { currencyDetail.setKeys(getDocumentNumber(), CashReceiptDocument.DOCUMENT_TYPE, CurrencyCoinSources.CASH_RECEIPTS); coinDetail.setKeys(getDocumentNumber(), CashReceiptDocument.DOCUMENT_TYPE, CurrencyCoinSources.CASH_RECEIPTS); confirmedCurrencyDetail.setKeys(getDocumentNumber(), CashReceiptDocument.DOCUMENT_TYPE, CurrencyCoinSources.CASH_MANAGEMENT_IN); confirmedCoinDetail.setKeys(getDocumentNumber(), CashReceiptDocument.DOCUMENT_TYPE, CurrencyCoinSources.CASH_MANAGEMENT_IN); changeCurrencyDetail.setKeys(getDocumentNumber(), CashReceiptDocument.DOCUMENT_TYPE, CurrencyCoinSources.CASH_CHANGE_REQUEST); changeCoinDetail.setKeys(getDocumentNumber(), CashReceiptDocument.DOCUMENT_TYPE, CurrencyCoinSources.CASH_CHANGE_REQUEST); confirmedChangeCurrencyDetail.setKeys(getDocumentNumber(), CashReceiptDocument.DOCUMENT_TYPE, CurrencyCoinSources.CASH_CHANGE_GRANTED); confirmedChangeCoinDetail.setKeys(getDocumentNumber(), CashReceiptDocument.DOCUMENT_TYPE, CurrencyCoinSources.CASH_CHANGE_GRANTED); } /** * Gets the totalCurrencyAmount attribute. * * @return Returns the totalCurrencyAmount. */ public KualiDecimal getTotalCurrencyAmount() { return (currencyDetail != null) ? currencyDetail.getTotalAmount() : KualiDecimal.ZERO; } /** * Gets the totalConfirmedCurrencyAmount attribute. * * @return Returns the totalConfirmedCurrencyAmount. */ public KualiDecimal getTotalConfirmedCurrencyAmount() { return (confirmedCurrencyDetail != null) ? confirmedCurrencyDetail.getTotalAmount() : KualiDecimal.ZERO; } /** * Gets the totalChangeCurrencyAmount attribute. * * @return Returns the totalChangeCurrencyAmount. */ public KualiDecimal getTotalChangeCurrencyAmount() { return (changeCurrencyDetail != null) ? changeCurrencyDetail.getTotalAmount() : KualiDecimal.ZERO; } /** * Gets the total amount of depositable checks * @return */ public KualiDecimal getTotalDepositableCheckAmount() { KualiDecimal totalDepositableCheckAmount = KualiDecimal.ZERO; List<Check> checks = getConfirmedChecks(); if (checks != null && !checks.isEmpty()) { for (Check check : checks) { if(check.getCashieringStatus().equals("C")) { totalDepositableCheckAmount = totalDepositableCheckAmount.add(check.getAmount()); } } } return totalDepositableCheckAmount; } /** * Checks if there are checks to be deposited * @return */ public boolean existDepositableChecks() { return getTotalDepositableCheckAmount().isGreaterThan(KualiDecimal.ZERO) ? true : false; } /** * This method returns the total depositable check amount as a currency formatted string. * * @return */ public String getCurrencyFormattedTotalDepositableCheckAmount() { return (String) new CurrencyFormatter().format(getTotalDepositableCheckAmount()); } /** * This method returns the cash total amount as a currency formatted string. * * @return String */ public String getCurrencyFormattedTotalCurrencyAmount() { return (String) new CurrencyFormatter().format(getTotalCurrencyAmount()); } public String getCurrencyFormattedTotalConfirmedCurrencyAmount() { return (String) new CurrencyFormatter().format(getTotalConfirmedCurrencyAmount()); } /** * Sets the totalCurrencyAmount attribute value. * * @param totalCurrencyAmount The totalCurrencyAmount to set. */ public void setTotalCurrencyAmount(KualiDecimal totalCurrencyAmount) { this.totalCurrencyAmount = totalCurrencyAmount; } /** * Sets the totalConfirmedCurrencyAmount attribute value. * * @param totalConfirmedCurrencyAmount The totalConfirmedCurrencyAmount to set. */ public void setTotalConfirmedCurrencyAmount(KualiDecimal totalConfirmedCurrencyAmount) { this.totalConfirmedCurrencyAmount = totalConfirmedCurrencyAmount; } /** * @param checkEntryMode */ public void setCheckEntryMode(String checkEntryMode) { this.checkEntryMode = checkEntryMode; } /** * @return checkEntryMode */ public String getCheckEntryMode() { return checkEntryMode; } /** * Gets the checks attribute. * * @return Returns the checks. */ public List<Check> getChecks() { return checks; } /** * Sets the checks attribute value. * * @param checks The checks to set. */ public void setChecks(List<Check> checks) { this.checks = checks; } /** * Gets the confirmed checks attribute. * * @return Returns the confirmed checks. */ public List<Check> getConfirmedChecks() { return confirmedChecks; } /** * Sets the confirmed checks attribute value. * * @param confirmedChecks The checks to set. */ public void setConfirmedChecks(List<Check> confirmedChecks) { this.confirmedChecks = confirmedChecks; } /** * Gets the number of checks, since Sun doesn't have a direct getter for collection size * * @return the number of checks */ public int getCheckCount() { int count = 0; if (ObjectUtils.isNotNull(checks)) { count = checks.size(); } return count; } /** * Gets the number of confirmed checks * * @return the number of checks */ public int getConfirmedCheckCount() { int count = 0; if (ObjectUtils.isNotNull(confirmedChecks)) { count = confirmedChecks.size(); } return count; } /** * Adds a new check to the list. * * @param check */ public void addCheck(Check check) { check.setSequenceId(this.nextCheckSequenceId); this.checks.add(check); this.nextCheckSequenceId = new Integer(this.nextCheckSequenceId.intValue() + 1); setTotalCheckAmount(getTotalCheckAmount().add(check.getAmount())); } /** * Adds a new confirmed check to the list. * * @param check */ public void addConfirmedCheck(Check check) { check.setSequenceId(this.nextConfirmedCheckSequenceId); this.confirmedChecks.add(check); this.nextConfirmedCheckSequenceId = new Integer(this.nextConfirmedCheckSequenceId.intValue() + 1); setTotalConfirmedCheckAmount(getTotalConfirmedCheckAmount().add(check.getAmount())); } /** * Retrieve a particular check at a given index in the list of checks. * * @param index * @return Check */ public Check getCheck(int index) { while (this.checks.size() <= index) { checks.add(createNewCheck()); } return checks.get(index); } /** * Retrieve a particular check at a given index in the list of confirmed checks. * * @param index * @return Check */ public Check getConfirmedCheck(int index) { while (this.confirmedChecks.size() <= index) { confirmedChecks.add(createNewConfirmedCheck()); } return confirmedChecks.get(index); } /** * @see org.kuali.kfs.sys.document.AccountingDocumentBase#checkSufficientFunds() */ @Override public List<SufficientFundsItem> checkSufficientFunds() { LOG.debug("checkSufficientFunds() started"); // This document does not do sufficient funds checking return new ArrayList<SufficientFundsItem>(); } /** * This method removes a check from the list and updates the total appropriately. * * @param index */ public void removeCheck(int index) { Check check = checks.remove(index); KualiDecimal newTotalCheckAmount = getTotalCheckAmount().subtract(check.getAmount()); // if the totalCheckAmount goes negative, bring back to zero. if (newTotalCheckAmount.isNegative()) { newTotalCheckAmount = KualiDecimal.ZERO; } setTotalCheckAmount(newTotalCheckAmount); } /** * This method removes a confirmed check from the list and updates the total appropriately. * * @param index */ public void removeConfirmedCheck(int index) { Check check = confirmedChecks.remove(index); KualiDecimal newTotalCheckAmount = getTotalConfirmedCheckAmount().subtract(check.getAmount()); // if the totalCheckAmount goes negative, bring back to zero. if (newTotalCheckAmount.isNegative()) { newTotalCheckAmount = KualiDecimal.ZERO; } setTotalConfirmedCheckAmount(newTotalCheckAmount); } /** * Gets the nextCheckSequenceId attribute. * * @return Returns the nextCheckSequenceId. */ public Integer getNextCheckSequenceId() { return nextCheckSequenceId; } /** * Sets the nextCheckSequenceId attribute value. * * @param nextCheckSequenceId The nextCheckSequenceId to set. */ public void setNextCheckSequenceId(Integer nextCheckSequenceId) { this.nextCheckSequenceId = nextCheckSequenceId; } /** * Gets the nextConfirmedCheckSequenceId attribute. * * @return Returns the nextConfirmedCheckSequenceId. */ public Integer getNextConfirmedCheckSequenceId() { return nextConfirmedCheckSequenceId; } /** * Sets the nextCheckSequenceId attribute value. * * @param nextCheckSequenceId The nextCheckSequenceId to set. */ public void setNextConfirmedCheckSequenceId(Integer nextConfirmedCheckSequenceId) { this.nextConfirmedCheckSequenceId = nextConfirmedCheckSequenceId; } /** * Gets the totalCheckAmount attribute. * * @return Returns the totalCheckAmount. */ public KualiDecimal getTotalCheckAmount() { if (totalCheckAmount == null) { setTotalCheckAmount(KualiDecimal.ZERO); } return totalCheckAmount; } /** * Gets the totalConfirmedCheckAmount attribute. * * @return Returns the totalCheckAmount. */ public KualiDecimal getTotalConfirmedCheckAmount() { if (totalConfirmedCheckAmount == null) { setTotalConfirmedCheckAmount(KualiDecimal.ZERO); } return totalConfirmedCheckAmount; } /** * Gets the totalConfirmedCheckAmount attribute. * * @return Returns the totalCheckAmount. */ public KualiDecimal getTotalConfirmedCheckAmountForInterim() { if (totalConfirmedCheckAmount == null || getFinancialSystemDocumentHeader().getFinancialDocumentStatusCode().equals(CashReceipt.INTERIM)) { setTotalConfirmedCheckAmount(KualiDecimal.ZERO); } return totalConfirmedCheckAmount; } /** * This method returns the check total amount as a currency formatted string. * * @return String */ public String getCurrencyFormattedTotalCheckAmount() { return (String) new CurrencyFormatter().format(getTotalCheckAmount()); } /** * This method returns the confirmed check total amount as a currency formatted string. * * @return String */ public String getCurrencyFormattedTotalConfirmedCheckAmount() { String amountString = (String) new CurrencyFormatter().format(getTotalConfirmedCheckAmount()); return amountString; } /** * Sets the totalCheckAmount attribute value. * * @param totalCheckAmount The totalCheckAmount to set. */ public void setTotalCheckAmount(KualiDecimal totalCheckAmount) { this.totalCheckAmount = totalCheckAmount; } /** * Sets the totalCheckAmount attribute value. * * @param totalCheckAmount The totalCheckAmount to set. */ public void setTotalConfirmedCheckAmount(KualiDecimal totalConfirmedCheckAmount) { this.totalConfirmedCheckAmount = totalConfirmedCheckAmount; } /** * Gets the totalCoinAmount attribute. * * @return Returns the totalCoinAmount. */ public KualiDecimal getTotalCoinAmount() { return (coinDetail != null) ? coinDetail.getTotalAmount() : KualiDecimal.ZERO; } /** * Gets the totalConfirmedCoinAmount attribute. * * @return Returns the totalConfirmedCoinAmount. */ public KualiDecimal getTotalConfirmedCoinAmount() { return (confirmedCoinDetail != null) ? confirmedCoinDetail.getTotalAmount() : KualiDecimal.ZERO; } /** * Gets the totalChangeCoinAmount attribute. * * @return Returns the totalChangeCoinAmount. */ public KualiDecimal getTotalChangeCoinAmount() { return (changeCoinDetail != null) ? changeCoinDetail.getTotalAmount() : KualiDecimal.ZERO; } /** * This method returns the coin total amount as a currency formatted string. * * @return String */ public String getCurrencyFormattedTotalCoinAmount() { return (String) new CurrencyFormatter().format(getTotalCoinAmount()); } /** * This method returns the confirmed coin total amount as a currency formatted string. * * @return String */ public String getCurrencyFormattedTotalConfirmedCoinAmount() { return (String) new CurrencyFormatter().format(getTotalConfirmedCoinAmount()); } /** * Deprecated: Use getTotalConfirmedNetAmount() instead. * returns (confirmed currency + confirmed coin - change amount) * * @return */ @Deprecated public KualiDecimal getGrandTotalConfirmedCashAmount() { return getTotalConfirmedNetAmount(); } /** * returns (confirmed currency + confirmed coin - change amount) as a currency formatted string * @return */ public String getCurrencyFormattedGrandTotalConfirmedCashAmount() { return (String) new CurrencyFormatter().format(getTotalConfirmedNetAmount()); } /** * Sets the totalCoinAmount attribute value. * * @param totalCoinAmount The totalCoinAmount to set. */ public void setTotalCoinAmount(KualiDecimal totalCoinAmount) { this.totalCoinAmount = totalCoinAmount; } /** * Sets the totalConfirmedCoinAmount attribute value. * * @param totalCoinAmount The totalConfirmedCoinAmount to set. */ public void setTotalConfirmedCoinAmount(KualiDecimal totalConfirmedCoinAmount) { this.totalConfirmedCoinAmount = totalConfirmedCoinAmount; } /** * Gets the overall total dollar amount of the document. * This is the amount displayed in the document header, and used as one of the document search criteria. * It is the net deposit total amount of the CR, and should equal to the accounting line total amount. * net deposit = money in - money out; * money in = check + currency + coin; * money out = change currency + change coin = change total. * * @see org.kuali.kfs.sys.document.AccountingDocumentBase#getTotalDollarAmount() * @return KualiDecimal */ @Override public KualiDecimal getTotalDollarAmount() { // We should return the net deposit total, which doesn't match the accounting line total when change request is not zero. // if CR is preroute, use the original amount; if(isPreRoute()) { return getTotalNetAmount(); } // otherwise, otherwise use the confirmed amount else { return getTotalConfirmedNetAmount(); } } public KualiDecimal getTotalChangeAmount() { return getTotalChangeCoinAmount().add(getTotalChangeCurrencyAmount()); } /** * Gets the coinDetail attribute. * * @return Returns the coinDetail. */ public CoinDetail getCoinDetail() { return coinDetail; } /** * Sets the coinDetail attribute value. * * @param coinDetail The coinDetail to set. */ public void setCoinDetail(CoinDetail coinDetail) { this.coinDetail = coinDetail; } /** * Gets the confirmedCoinDetail attribute. * * @return Returns the confirmedCoinDetail. */ public CoinDetail getConfirmedCoinDetail() { return confirmedCoinDetail; } /** * Sets the confirmedCoinDetail attribute value. * * @param coinDetail The confirmedCoinDetail to set. */ public void setConfirmedCoinDetail(CoinDetail coinDetail) { this.confirmedCoinDetail = coinDetail; } /** * Gets the changeCoinDetail attribute. * * @return Returns the changeCoinDetail. */ public CoinDetail getChangeCoinDetail() { return changeCoinDetail; } /** * Sets the changeCoinDetail attribute value. * * @param coinDetail The changeCoinDetail to set. */ public void setChangeCoinDetail(CoinDetail changeCoinDetail) { this.changeCoinDetail = changeCoinDetail; } /** * Gets the currencyDetail attribute. * * @return Returns the currencyDetail. */ public CurrencyDetail getCurrencyDetail() { return currencyDetail; } /** * Sets the currencyDetail attribute value. * * @param currencyDetail The currencyDetail to set. */ public void setCurrencyDetail(CurrencyDetail currencyDetail) { this.currencyDetail = currencyDetail; } /** * Gets the confirmedCurrencyDetail attribute. * * @return Returns the confirmedCurrencyDetail. */ public CurrencyDetail getConfirmedCurrencyDetail() { return confirmedCurrencyDetail; } /** * Sets the confirmedCurrencyDetail attribute value. * * @param currencyDetail The confirmedCurrencyDetail to set. */ public void setConfirmedCurrencyDetail(CurrencyDetail confirmedCurrencyDetail) { this.confirmedCurrencyDetail = confirmedCurrencyDetail; } /** * Gets the changeCurrencyDetail attribute. * * @return Returns the changeCurrencyDetail. */ public CurrencyDetail getChangeCurrencyDetail() { return changeCurrencyDetail; } /** * Sets the changeCurrencyDetail attribute value. * * @param currencyDetail The changeCurrencyDetail to set. */ public void setChangeCurrencyDetail(CurrencyDetail changeCurrencyDetail) { this.changeCurrencyDetail = changeCurrencyDetail; } /** * Retrieves the change total amount in a currency format with commas. * * @return String */ @Deprecated public String getCurrencyFormattedTotalChangeAmount() { return (String) new CurrencyFormatter().format(getTotalChangeAmount()); } /** * Gets the recategorized attribute. * @return Returns the recategorized. */ public boolean isRecategorized() { return recategorized; } /** * Sets the recategorized attribute value. * @param recategorized The recategorized to set. */ public void setRecategorized(boolean recategorized) { this.recategorized = recategorized; } /** * @return sum of the amounts of the current list of checks */ public KualiDecimal calculateCheckTotal() { KualiDecimal total = KualiDecimal.ZERO; for (Iterator<Check> i = getChecks().iterator(); i.hasNext();) { Check c = i.next(); if (null != c.getAmount()) { total = total.add(c.getAmount()); } } return total; } /** * @return sum of the amounts of the current list of checks */ public KualiDecimal calculateConfirmedCheckTotal() { KualiDecimal total = KualiDecimal.ZERO; for (Iterator<Check> i = getConfirmedChecks().iterator(); i.hasNext();) { Check c = i.next(); if (null != c.getAmount()) { total = total.add(c.getAmount()); } } return total; } /** * @see org.kuali.rice.kns.document.DocumentBase#prepareForSave() */ @Override public void prepareForSave() { super.prepareForSave(); // clear check list if mode is checkTotal if (CHECK_ENTRY_TOTAL.equals(getCheckEntryMode())) { getChecks().clear(); } // update total if mode is checkDetail else { setTotalCheckAmount(calculateCheckTotal()); } } /** * @see org.kuali.rice.kns.document.DocumentBase#processAfterRetrieve() */ @Override public void processAfterRetrieve() { super.processAfterRetrieve(); // set to checkTotal mode if no checks List<Check> checkList = getChecks(); if (ObjectUtils.isNull(checkList) || checkList.isEmpty()) { setCheckEntryMode(CHECK_ENTRY_TOTAL); } // set to checkDetail mode if checks (and update the checkTotal, while you're here) else { setCheckEntryMode(CHECK_ENTRY_DETAIL); setTotalCheckAmount(calculateCheckTotal()); } refreshCashDetails(); } /** * Override to set the document status to VERIFIED ("V") when the document is FINAL. When the Cash Management document that this * is associated with is FINAL approved, this status will be set to APPROVED ("A") to be picked up by the GL for processing. * That's done in the doRouteStatusChange() method in the CashManagementDocument. * * @see org.kuali.rice.kns.document.Document#doRouteStatusChange() */ @Override public void doRouteStatusChange(DocumentRouteStatusChange statusChangeEvent) { super.doRouteStatusChange(statusChangeEvent); WorkflowDocument workflowDocument = getDocumentHeader().getWorkflowDocument(); // Workflow Status of PROCESSED --> Kuali Doc Status of Verified if (workflowDocument.isProcessed()) { this.getFinancialSystemDocumentHeader().setFinancialDocumentStatusCode(CashReceipt.VERIFIED); LOG.info("Adding Cash to Cash Drawer"); SpringContext.getBean(CashReceiptService.class).addCashDetailsToCashDrawer(this); } this.getCapitalAssetManagementModuleService().deleteDocumentAssetLocks(this); } /** * @see org.kuali.rice.kns.document.DocumentBase#postProcessSave(org.kuali.rice.kns.rule.event.KualiDocumentEvent) */ @Override public void postProcessSave(KualiDocumentEvent event) { super.postProcessSave(event); /* * If we only saves a detail when its total is not zero, when the detail was non-zero and saved before, and now it is cleared to 0, * it won't be saved, leaving the previously saved non-zero detail still in DB. * Next time CR is loaded, the old value will show up again. Basically it won't allow clearing a detail to 0. */ // Set primary keys for all cash/change details, in case the documentNumber was null when it was set during document initialization. // The documentNumber would be null if the CR was created from scratch (instead of from copying, or retrieved from DB) // and is being saved for the first time at this point. setCashChangeDetailKeys(); BusinessObjectService boService = SpringContext.getBean(BusinessObjectService.class); // if CR is not enroute yet, confirmed details aren't filled yet, so only need to save original details if (isPreRoute()) { // if currencyDetail not empty, save it if (!currencyDetail.isEmpty()) { boService.save(currencyDetail); } // otherwise, check if it is previously saved, if yes, delete old currencyDetail else if (retrieveCurrencyDetail() != null) { boService.delete(currencyDetail); } // if coinDetail not empty, save it if (!coinDetail.isEmpty()) { boService.save(coinDetail); } // otherwise, check if it is previously saved, if yes, delete old coinDetail else if (retrieveCoinDetail() != null) { boService.delete(coinDetail); } // if changeCurrencyDetail not empty, save it if (!changeCurrencyDetail.isEmpty()) { boService.save(changeCurrencyDetail); } // otherwise, check if it is previously saved, if yes, delete old changeCurrencyDetail else if (retrieveChangeCurrencyDetail() != null) { boService.delete(changeCurrencyDetail); } // if changeCoinDetail not empty, save it if (!changeCoinDetail.isEmpty()) { boService.save(changeCoinDetail); } // otherwise, check if it is previously saved, if yes, delete old changeCoinDetail else if (retrieveChangeCoinDetail() != null) { boService.delete(changeCoinDetail); } } // if CR is enroute, original details are not editable, so only need to save the confirmed details else { // if confirmedCurrencyDetail not empty, save it if (!confirmedCurrencyDetail.isEmpty()) { boService.save(confirmedCurrencyDetail); } // otherwise, check if it is previously saved, if yes, delete old confirmedCurrencyDetail else if (retrieveConfirmedCurrencyDetail() != null) { boService.delete(confirmedCurrencyDetail); } // if confirmedCoinDetail not empty, save it if (!confirmedCoinDetail.isEmpty()) { boService.save(confirmedCoinDetail); } // otherwise, check if it is previously saved, if yes, delete old confirmedCoinDetail else if (retrieveConfirmedCoinDetail() != null) { boService.delete(confirmedCoinDetail); } // if confirmedChangeCurrencyDetail not empty, save it if (!confirmedChangeCurrencyDetail.isEmpty()) { boService.save(confirmedChangeCurrencyDetail); } // otherwise, check if it is previously saved, if yes, delete old confirmedChangeCurrencyDetail else if (retrieveConfirmedChangeCurrencyDetail() != null) { boService.delete(confirmedChangeCurrencyDetail); } // if confirmedChangeCoinDetail not empty, save it if (!confirmedChangeCoinDetail.isEmpty()) { boService.save(confirmedChangeCoinDetail); } // otherwise, check if it is previously saved, if yes, delete old confirmedChangeCoinDetail else if (retrieveConfirmedChangeCoinDetail() != null) { boService.delete(confirmedChangeCoinDetail); } } if (!(event instanceof SaveDocumentEvent)) { // don't lock until they route String documentTypeName = SpringContext.getBean(DataDictionaryService.class).getDocumentTypeNameByClass(this.getClass()); this.getCapitalAssetManagementModuleService().generateCapitalAssetLock(this,documentTypeName); } } /** * This method refreshes the currency/coin details for this cash receipt document */ public void refreshCashDetails() { CurrencyDetail retrievedCurrencyDetail = retrieveCurrencyDetail(); CoinDetail retrievedCoinDetail = retrieveCoinDetail(); CurrencyDetail retrievedConfirmedCurrencyDetail = retrieveConfirmedCurrencyDetail(); CoinDetail retrievedConfirmedCoinDetail = retrieveConfirmedCoinDetail(); CurrencyDetail retrievedChangeCurrencyDetail = retrieveChangeCurrencyDetail(); CoinDetail retrievedChangeCoinDetail = retrieveChangeCoinDetail(); CurrencyDetail retrievedConfirmedChangeCurrencyDetail = retrieveConfirmedChangeCurrencyDetail(); CoinDetail retrievedConfirmedChangeCoinDetail = retrieveConfirmedChangeCoinDetail(); // If this method is called before the cash detail records are inserted into the DB, reset the detail to initial values. // In normal cases, when this happens, the details shall be in initial status anyways, since this method should only be called // as a post-process after retrieving CR from DB. if(retrievedCurrencyDetail != null) { currencyDetail = retrievedCurrencyDetail; } else { currencyDetail = new CurrencyDetail(getDocumentNumber(), CashReceiptDocument.DOCUMENT_TYPE, CurrencyCoinSources.CASH_RECEIPTS); } if(retrievedCoinDetail != null) { coinDetail = retrievedCoinDetail; } else { coinDetail = new CoinDetail(getDocumentNumber(), CashReceiptDocument.DOCUMENT_TYPE, CurrencyCoinSources.CASH_RECEIPTS); } if(retrievedConfirmedCurrencyDetail != null) { confirmedCurrencyDetail = retrievedConfirmedCurrencyDetail; } else { confirmedCurrencyDetail = new CurrencyDetail(getDocumentNumber(), CashReceiptDocument.DOCUMENT_TYPE, CurrencyCoinSources.CASH_MANAGEMENT_IN); } if(retrievedConfirmedCoinDetail != null) { confirmedCoinDetail = retrievedConfirmedCoinDetail; } else { confirmedCoinDetail = new CoinDetail(getDocumentNumber(), CashReceiptDocument.DOCUMENT_TYPE, CurrencyCoinSources.CASH_MANAGEMENT_IN); } if(retrievedChangeCurrencyDetail != null) { changeCurrencyDetail = retrievedChangeCurrencyDetail; } else { changeCurrencyDetail = new CurrencyDetail(getDocumentNumber(), CashReceiptDocument.DOCUMENT_TYPE, CurrencyCoinSources.CASH_CHANGE_REQUEST); } if(retrievedChangeCoinDetail != null) { changeCoinDetail = retrievedChangeCoinDetail; } else { changeCoinDetail = new CoinDetail(getDocumentNumber(), CashReceiptDocument.DOCUMENT_TYPE, CurrencyCoinSources.CASH_CHANGE_REQUEST); } if(retrievedConfirmedChangeCurrencyDetail != null) { confirmedChangeCurrencyDetail = retrievedConfirmedChangeCurrencyDetail; } else { confirmedChangeCurrencyDetail = new CurrencyDetail(getDocumentNumber(), CashReceiptDocument.DOCUMENT_TYPE, CurrencyCoinSources.CASH_CHANGE_GRANTED); } if(retrievedConfirmedChangeCoinDetail != null) { confirmedChangeCoinDetail = retrievedConfirmedChangeCoinDetail; } else { confirmedChangeCoinDetail = new CoinDetail(getDocumentNumber(), CashReceiptDocument.DOCUMENT_TYPE, CurrencyCoinSources.CASH_CHANGE_GRANTED); } } /** * Get this document's currency detail from the database * * @return the currency detail record for this cash receipt document */ protected CurrencyDetail retrieveCurrencyDetail() { return SpringContext.getBean(BusinessObjectService.class).findByPrimaryKey(CurrencyDetail.class, getCashDetailPrimaryKey()); } /** * Grab this document's coin detail from the database * * @return the coin detail record for this cash receipt document */ protected CoinDetail retrieveCoinDetail() { return SpringContext.getBean(BusinessObjectService.class).findByPrimaryKey(CoinDetail.class, getCashDetailPrimaryKey()); } /** * Get this document's confirmed currency detail from the database * * @return the currency detail record for this cash receipt document */ protected CurrencyDetail retrieveConfirmedCurrencyDetail() { return SpringContext.getBean(BusinessObjectService.class).findByPrimaryKey(CurrencyDetail.class, getConfirmedCashDetailPrimaryKey()); } /** * Get this document's confirmed coin detail from the database * * @return the coin detail record for this cash receipt document */ protected CoinDetail retrieveConfirmedCoinDetail() { return SpringContext.getBean(BusinessObjectService.class).findByPrimaryKey(CoinDetail.class, getConfirmedCashDetailPrimaryKey()); } /** * Get this document's change currency detail from the database * * @return the change currency detail record for this cash receipt document */ protected CurrencyDetail retrieveChangeCurrencyDetail() { return SpringContext.getBean(BusinessObjectService.class).findByPrimaryKey(CurrencyDetail.class, getChangeCashDetailPrimaryKey()); } /** * Get this document's change coin detail from the database * * @return the change coin detail record for this cash receipt document */ protected CoinDetail retrieveChangeCoinDetail() { return SpringContext.getBean(BusinessObjectService.class).findByPrimaryKey(CoinDetail.class, getChangeCashDetailPrimaryKey()); } /** * Get this document's confirmed change currency detail from the database * * @return the change currency detail record for this cash receipt document */ protected CurrencyDetail retrieveConfirmedChangeCurrencyDetail() { return SpringContext.getBean(BusinessObjectService.class).findByPrimaryKey(CurrencyDetail.class, getConfirmedChangeCashDetailPrimaryKey()); } /** * Get this document's confirmed change coin detail from the database * * @return the change coin detail record for this cash receipt document */ protected CoinDetail retrieveConfirmedChangeCoinDetail() { return SpringContext.getBean(BusinessObjectService.class).findByPrimaryKey(CoinDetail.class, getConfirmedChangeCashDetailPrimaryKey()); } /** * * This method... * @return */ public String getCreateDate() { return SpringContext.getBean(DateTimeService.class).toDateString(getDocumentHeader().getWorkflowDocument().getDateCreated().toDate()); } /** * Generate the primary key for a currency or coin detail related to this document * * @return a map with a representation of the proper primary key */ protected Map getCashDetailPrimaryKey() { Map pk = new HashMap(); pk.put("documentNumber", this.getDocumentNumber()); pk.put("financialDocumentTypeCode", CashReceiptDocument.DOCUMENT_TYPE); pk.put("cashieringStatus", KFSConstants.CurrencyCoinSources.CASH_RECEIPTS); return pk; } protected Map getConfirmedCashDetailPrimaryKey() { Map pk = new HashMap(); pk.put("documentNumber", this.getDocumentNumber()); pk.put("financialDocumentTypeCode", CashReceiptDocument.DOCUMENT_TYPE); pk.put("cashieringStatus", KFSConstants.CurrencyCoinSources.CASH_MANAGEMENT_IN); return pk; } protected Map getChangeCashDetailPrimaryKey() { Map pk = new HashMap(); pk.put("documentNumber", this.getDocumentNumber()); pk.put("financialDocumentTypeCode", CashReceiptDocument.DOCUMENT_TYPE); pk.put("cashieringStatus", KFSConstants.CurrencyCoinSources.CASH_CHANGE_REQUEST); return pk; } protected Map getConfirmedChangeCashDetailPrimaryKey() { Map pk = new HashMap(); pk.put("documentNumber", this.getDocumentNumber()); pk.put("financialDocumentTypeCode", CashReceiptDocument.DOCUMENT_TYPE); pk.put("cashieringStatus", KFSConstants.CurrencyCoinSources.CASH_CHANGE_GRANTED); return pk; } /** * @see org.kuali.rice.kns.document.TransactionalDocumentBase#buildListOfDeletionAwareLists() */ @Override public List buildListOfDeletionAwareLists() { List managedLists = super.buildListOfDeletionAwareLists(); managedLists.add(getChecks()); return managedLists; } @Override public List generateSaveEvents() { // 1. retrieve persisted checks for document // 2. retrieve current checks from given document // 3. compare, creating add/delete/update events as needed // 4. apply rules as appropriate returned events Collection<CheckBase> persistedChecks = SpringContext.getBean(CheckService.class).getByDocumentHeaderId(getDocumentNumber()); List currentChecks = getChecks(); List events = generateEvents(persistedChecks, currentChecks, KFSConstants.EXISTING_CHECK_PROPERTY_NAME, this); events.addAll(super.generateSaveEvents()); return events; } /** * Generates a List of instances of CheckEvent subclasses, one for each changed check in the union of the persistedLines and * currentLines lists. Events in the list will be grouped in order by event-type (update, add, delete). * * @param persistedChecks * @param currentChecks * @param errorPathPrefix * @param crdoc * @return List of CheckEvent subclass instances */ protected List generateEvents(Collection persistedChecks, List currentChecks, String errorPathPrefix, CashReceiptFamilyBase crdoc) { List addEvents = new ArrayList(); List updateEvents = new ArrayList(); List deleteEvents = new ArrayList(); // // generate events Map persistedCheckMap = buildCheckMap(persistedChecks); // (iterate through current lines to detect additions and updates, removing affected lines from persistedLineMap as we go // so deletions can be detected by looking at whatever remains in persistedLineMap) int index = 0; for (Iterator i = currentChecks.iterator(); i.hasNext(); index++) { Check currentCheck = (Check) i.next(); Integer key = currentCheck.getSequenceId(); Check persistedCheck = (Check) persistedCheckMap.get(key); // if line is both current and persisted... if (persistedCheck != null) { // ...check for updates if (!currentCheck.isLike(persistedCheck)) { UpdateCheckEvent updateEvent = new UpdateCheckEvent(errorPathPrefix, crdoc, currentCheck); updateEvents.add(updateEvent); } else { // do nothing, since this line hasn't changed } persistedCheckMap.remove(key); } else { // it must be a new addition AddCheckEvent addEvent = new AddCheckEvent(errorPathPrefix, crdoc, currentCheck); addEvents.add(addEvent); } } // detect deletions for (Iterator i = persistedCheckMap.entrySet().iterator(); i.hasNext();) { Map.Entry e = (Map.Entry) i.next(); Check persistedCheck = (Check) e.getValue(); DeleteCheckEvent deleteEvent = new DeleteCheckEvent(errorPathPrefix, crdoc, persistedCheck); deleteEvents.add(deleteEvent); } // // merge the lists List lineEvents = new ArrayList(); lineEvents.addAll(updateEvents); lineEvents.addAll(addEvents); lineEvents.addAll(deleteEvents); return lineEvents; } /** * @param checks * @return Map containing Checks from the given List, indexed by their sequenceId */ protected Map buildCheckMap(Collection checks) { Map checkMap = new HashMap(); for (Iterator i = checks.iterator(); i.hasNext();) { Check check = (Check) i.next(); Integer sequenceId = check.getSequenceId(); Object oldCheck = checkMap.put(sequenceId, check); // verify that sequence numbers are unique... if (oldCheck != null) { throw new IllegalStateException("sequence id collision detected for sequence id " + sequenceId); } } return checkMap; } public Check createNewCheck() { Check newCheck = new CheckBase(); newCheck.setFinancialDocumentTypeCode(DOCUMENT_TYPE); newCheck.setCashieringStatus(KFSConstants.CheckSources.CASH_RECEIPTS); return newCheck; } public Check createNewConfirmedCheck() { Check newCheck = new CheckBase(); newCheck.setFinancialDocumentTypeCode(DOCUMENT_TYPE); newCheck.setCashieringStatus(KFSConstants.CheckSources.CASH_MANAGEMENT); return newCheck; } /** * Gets the depositCashReceiptControl attribute. * @return Returns the depositCashReceiptControl. */ public List getDepositCashReceiptControl() { return depositCashReceiptControl; } /** * Sets the depositCashReceiptControl attribute value. * @param depositCashReceiptControl The depositCashReceiptControl to set. */ public void setDepositCashReceiptControl(List depositCashReceiptControl) { this.depositCashReceiptControl = depositCashReceiptControl; } /** * Override the campus code on the copied document to whatever the campus of the copying user is * @see org.kuali.kfs.sys.document.AccountingDocumentBase#toCopy() */ @Override public void toCopy() throws WorkflowException { super.toCopy(); /* * Currently copying a CR always copies the pre-verified cash details, even after CR is already verified. * It's probably more desirable to copy the confirmed details from the source CR to the new CR doc as original details, * after the source CR is verified. */ initializeCampusLocationCode(); if ((getChecks() == null || getChecks().isEmpty()) && getTotalCheckAmount().equals(KualiDecimal.ZERO)) { setCheckEntryMode(CashReceiptDocument.CHECK_ENTRY_DETAIL); } /* KFSMI-9914 (IU ref: FSKD-5275): Confirmed amounts were copied causing problems when copied doc was confirmed */ setTotalConfirmedCheckAmount(KualiDecimal.ZERO); setTotalConfirmedCoinAmount(KualiDecimal.ZERO); totalChangeAmount = KualiDecimal.ZERO; confirmedChecks = new ArrayList<Check>(); confirmedCurrencyDetail = new CurrencyDetail(getDocumentNumber(), CashReceiptDocument.DOCUMENT_TYPE, CurrencyCoinSources.CASH_MANAGEMENT_IN); confirmedCoinDetail = new CoinDetail(getDocumentNumber(), CashReceiptDocument.DOCUMENT_TYPE, CurrencyCoinSources.CASH_MANAGEMENT_IN); confirmedChangeCurrencyDetail = new CurrencyDetail(getDocumentNumber(), CashReceiptDocument.DOCUMENT_TYPE, CurrencyCoinSources.CASH_CHANGE_GRANTED); confirmedChangeCoinDetail = new CoinDetail(getDocumentNumber(), CashReceiptDocument.DOCUMENT_TYPE, CurrencyCoinSources.CASH_CHANGE_GRANTED); } /** * Initializes the campus location code based on kfs user role chart org */ protected void initializeCampusLocationCode(){ if (GlobalVariables.getUserSession() != null && GlobalVariables.getUserSession().getPerson() != null) { Person currentUser = GlobalVariables.getUserSession().getPerson(); ChartOrgHolder chartOrg = SpringContext.getBean(org.kuali.kfs.sys.service.FinancialSystemUserService.class).getPrimaryOrganization(currentUser, KFSConstants.ParameterNamespaces.FINANCIAL); // Does a valid campus code exist for this person? If so, simply grab // the campus code via the business object service. if (chartOrg != null && chartOrg.getOrganization() != null) { setCampusLocationCode(chartOrg.getOrganization().getOrganizationPhysicalCampusCode()); } // A valid campus code was not found; therefore, use the default affiliated // campus code. else { String affiliatedCampusCode = currentUser.getCampusCode(); setCampusLocationCode(affiliatedCampusCode); } } } /** Deprecated: never used * Gets the sumTotalAmount attribute. * * @return Returns the sumTotalAmount */ @Deprecated public KualiDecimal getSumTotalAmount() { return sumTotalAmount; } /** Deprecated: never used * Sets the sumTotalAmount attribute. * * @param sumTotalAmount The sumTotalAmount to set. */ @Deprecated public void setSumTotalAmount(KualiDecimal sumTotalAmount) { this.sumTotalAmount = sumTotalAmount; } public CurrencyDetail getConfirmedChangeCurrencyDetail() { return confirmedChangeCurrencyDetail; } public void setConfirmedChangeCurrencyDetail(CurrencyDetail confirmedChangeCurrencyDetail) { this.confirmedChangeCurrencyDetail = confirmedChangeCurrencyDetail; } public CoinDetail getConfirmedChangeCoinDetail() { return confirmedChangeCoinDetail; } public void setConfirmedChangeCoinDetail(CoinDetail confirmedChangeCoinDetail) { this.confirmedChangeCoinDetail = confirmedChangeCoinDetail; } /** * Gets the original (i.e. before Cash Manager adjustment) cash in total amount: * cash in = currency + coin; */ public KualiDecimal getTotalCashInAmount() { return getTotalCurrencyAmount().add(getTotalCoinAmount()); } /** * Gets the original (i.e. before Cash Manager adjustment) money in total amount: * money in = check + cash in; * cash in = currency + coin; */ public KualiDecimal getTotalMoneyInAmount() { return getTotalCheckAmount().add(getTotalCashInAmount()); } /** * Gets the original (i.e. before Cash Manager adjustment) reconciliation net total amount: * recon net = money in - money out = net deposit; * money in = check + currency + coin; * money out = change currency + change coin = change total. */ public KualiDecimal getTotalNetAmount() { return getTotalMoneyInAmount().subtract(getTotalChangeAmount()); } /** * Gets the total confirmed change currency amount. */ public KualiDecimal getTotalConfirmedChangeCurrencyAmount() { return (confirmedChangeCurrencyDetail != null) ? confirmedChangeCurrencyDetail.getTotalAmount() : KualiDecimal.ZERO; } /** * Gets the total confirmed change coin amount. */ public KualiDecimal getTotalConfirmedChangeCoinAmount() { return (confirmedChangeCoinDetail != null) ? confirmedChangeCoinDetail.getTotalAmount() : KualiDecimal.ZERO; } /** * Gets the confirmed (i.e. after Cash Manager adjustment) change request (money out) total amount: * money out = change currency + change coin = change total. */ public KualiDecimal getTotalConfirmedChangeAmount() { return getTotalConfirmedChangeCurrencyAmount().add(getTotalConfirmedChangeCoinAmount()); } /** * Gets the confirmed (i.e. before Cash Manager adjustment) cash in total amount: * cash in = currency + coin; */ public KualiDecimal getTotalConfirmedCashInAmount() { return getTotalConfirmedCurrencyAmount().add(getTotalConfirmedCoinAmount()); } /** * Gets the confirmed (i.e. after Cash Manager adjustment) money in total amount: * money in = check + cash in; * cash in = currency + coin; */ public KualiDecimal getTotalConfirmedMoneyInAmount() { return getTotalConfirmedCheckAmount().add(getTotalConfirmedCashInAmount()); } /** * Gets the confirmed (i.e. after Cash Manager adjustment) net currency total amount: * net currency = currency - change currency */ public KualiDecimal getTotalConfirmedNetCurrencyAmount() { return getTotalConfirmedCurrencyAmount().subtract(getTotalConfirmedChangeCurrencyAmount()); } /** * Gets the confirmed (i.e. after Cash Manager adjustment) net coin total amount: * net coin = coin - change coin */ public KualiDecimal getTotalConfirmedNetCoinAmount() { return getTotalConfirmedCoinAmount().subtract(getTotalConfirmedChangeCoinAmount()); } /** * Gets the confirmed (i.e. after Cash Manager adjustment) net cash total amount: * net cash = net currency + net coin * net currency = currency - change currency * net coin = coin - change coin * or: * net cash = cash in - cash out * cash in = currency + coin; * cash out = change currency + change coin = change total. */ public KualiDecimal getTotalConfirmedNetCashAmount() { return getTotalConfirmedNetCurrencyAmount().add(getTotalConfirmedNetCoinAmount()); } /** * Gets the confirmed (i.e. after Cash Manager adjustment) reconciliation net total amount: * recon net = money in - money out = net deposit; * money in = check + currency + coin; * money out = change currency + change coin = change total. */ public KualiDecimal getTotalConfirmedNetAmount() { return getTotalConfirmedMoneyInAmount().subtract(getTotalConfirmedChangeAmount()); } /** * Returns true if the original change request has any field populated. */ public boolean isOriginalChangeRequested() { return !changeCurrencyDetail.isEmpty() || !changeCoinDetail.isEmpty(); //return getTotalChangeAmount().isNonZero(); } /** * Returns true if the confirmed change request has any field populated. */ public boolean isConfirmedChangeRequested() { return !confirmedChangeCurrencyDetail.isEmpty() || !confirmedChangeCoinDetail.isEmpty(); //return getTotalConfirmedChangeAmount().isNonZero(); } /** * Returns true if the change request tab is used, i.e. the original and/or the confirmed change request has any field populated. */ public boolean isChangeRequested() { return isOriginalChangeRequested() || isConfirmedChangeRequested(); } /** * Returns true if CR document status code is pre-route, i.e. it hasn't been routed for CashManager approval. * The impact is that, during this stage, any amount we use for actions (for ex, for validation or deposit) * should be pointing to the original one; otherwise, it should be pointing to the confirmed one. */ public boolean isPreRoute() { // pre-route FinancialDocumentStatusCode should be INITIATED or CANCELLED; during CM routing the status is "R"; // pre-route WorkflowDocumentStatusCode should be INITIATED, SAVED, or CANCELLED; // however, it's better to use Workflow status here, since if notes are added to the doc, // using the FinancialDocumentStatusCode won't be right. String statusCode = getFinancialSystemDocumentHeader().getWorkflowDocument().getStatus().getCode(); boolean isPreRoute = DocumentStatus.INITIATED.getCode().equals(statusCode) || DocumentStatus.SAVED.getCode().equals(statusCode) || DocumentStatus.CANCELED.getCode().equals(statusCode); return isPreRoute; } /** * Returns true if CR document has been confirmed, i.e. approved by CashManagerstatus. * The impact is that, after CM confirmation, the confirmed checks and cash details won't be editable anymore, * but they still should be shown on the CR doc along with the original one. * Note that we don't include DISAPPROVED status here, since when CM disapproves the CR, CR doesn't get saved, * so no confirmed details will be saved anyways, thus no need to consider them. */ public boolean isConfirmed() { // post CM approval status could be VERIFIED, INTERIM, FINAL, APPROVED String statusCode = getFinancialSystemDocumentHeader().getFinancialDocumentStatusCode(); boolean isConfirmed = CashReceipt.VERIFIED.equals(statusCode) || CashReceipt.INTERIM.equals(statusCode) || CashReceipt.FINAL.equals(statusCode) || DocumentStatusCodes.APPROVED.equals(statusCode); return isConfirmed; } /** * Returns true if accounting line is debit * * @param financialDocument * @param accountingLine * @param true if accountline line * @see IsDebitUtils#isDebitConsideringType(FinancialDocumentRuleBase, FinancialDocument, AccountingLine) * @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) { // error corrections are not allowed on CR SpringContext.getBean(DebitDeterminerService.class).disallowErrorCorrectionDocumentCheck(this); return super.isDebit(postable); } /** * Provides answers to the following splits: AcknowledgeChangeRequest * @returns true if change request is used, false otherwise. * @see org.kuali.kfs.sys.document.FinancialSystemTransactionalDocumentBase#answerSplitNodeQuestion(java.lang.String) */ @Override public boolean answerSplitNodeQuestion(String nodeName) throws UnsupportedOperationException { if (nodeName.equals(REQUIRE_REVIEW_SPLIT)) { return isChangeRequested(); } throw new UnsupportedOperationException("Cannot answer split question for this node you call \""+nodeName+"\""); } }