/* * 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.service.impl; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.commons.lang.StringUtils; import org.kuali.kfs.fp.businessobject.CashDrawer; import org.kuali.kfs.fp.businessobject.CashieringItemInProcess; import org.kuali.kfs.fp.businessobject.CashieringTransaction; import org.kuali.kfs.fp.businessobject.Check; import org.kuali.kfs.fp.businessobject.CoinDetail; import org.kuali.kfs.fp.businessobject.CurrencyDetail; import org.kuali.kfs.fp.businessobject.Deposit; import org.kuali.kfs.fp.businessobject.DepositCashReceiptControl; import org.kuali.kfs.fp.businessobject.format.CashDrawerStatusCodeFormatter; import org.kuali.kfs.fp.document.CashManagementDocument; import org.kuali.kfs.fp.document.CashReceiptDocument; import org.kuali.kfs.fp.document.dataaccess.CashManagementDao; import org.kuali.kfs.fp.document.service.CashManagementService; import org.kuali.kfs.fp.document.service.CashReceiptService; import org.kuali.kfs.fp.exception.CashDrawerStateException; import org.kuali.kfs.fp.exception.InvalidCashReceiptState; import org.kuali.kfs.fp.service.CashDrawerService; import org.kuali.kfs.sys.KFSConstants; import org.kuali.kfs.sys.KFSConstants.CashDrawerConstants; import org.kuali.kfs.sys.KFSConstants.CurrencyCoinSources; import org.kuali.kfs.sys.KFSConstants.DepositConstants; import org.kuali.kfs.sys.KFSConstants.DocumentStatusCodes; import org.kuali.kfs.sys.KFSConstants.DocumentStatusCodes.CashReceipt; import org.kuali.kfs.sys.KFSPropertyConstants; import org.kuali.kfs.sys.businessobject.Bank; import org.kuali.kfs.sys.businessobject.FinancialSystemDocumentHeader; import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntry; import org.kuali.kfs.sys.context.SpringContext; import org.kuali.rice.core.api.datetime.DateTimeService; import org.kuali.rice.core.api.util.type.KualiDecimal; import org.kuali.rice.kew.api.WorkflowDocument; import org.kuali.rice.kew.api.exception.WorkflowException; import org.kuali.rice.kim.api.identity.Person; import org.kuali.rice.kns.document.authorization.DocumentAuthorizer; import org.kuali.rice.kns.service.DataDictionaryService; import org.kuali.rice.kns.service.DocumentHelperService; import org.kuali.rice.krad.exception.InfrastructureException; import org.kuali.rice.krad.service.BusinessObjectService; import org.kuali.rice.krad.service.DocumentService; import org.kuali.rice.krad.util.GlobalVariables; import org.kuali.rice.krad.util.ObjectUtils; import org.springframework.transaction.annotation.Transactional; /** * This is the default implementation of the CashManagementService interface. */ @Transactional public class CashManagementServiceImpl implements CashManagementService { private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(CashManagementServiceImpl.class); private BusinessObjectService businessObjectService; private CashDrawerService cashDrawerService; private DateTimeService dateTimeService; private DocumentService documentService; private CashManagementDao cashManagementDao; private DataDictionaryService dataDictionaryService; /** * If a CMD is found that is associated with the CR document, then that CMD is returned; otherwise null is returned. * Currently the relationships are: * <ul> * <li>(CashReceipt to CashReceiptHeader) is (1 to 1)</li> * <li>(CashReceiptHeader to DepositCashReceiptControl) is (1 to 1)</li> * <li>(DepositCashReceiptControl to Deposit) is (many to 1)</li> * <li>(Deposit to CashManagementDocument) is (many to 1)</li> * </ul> * * @param documentId The id of the cash receipt document linked to the cash management document. * @return An instance of a CashManagementDocument matching the provided search criteria, or null if no value is found. * * @see org.kuali.kfs.fp.document.service.CashManagementService#getCashManagementDocumentForCashReceiptId(java.lang.String) */ @Override public CashManagementDocument getCashManagementDocumentForCashReceiptId(String documentId) { CashManagementDocument cmdoc = null; // get CashReceiptHeader for the CashReceipt, if any HashMap primaryKeys = new HashMap(); primaryKeys.put(KFSPropertyConstants.DOCUMENT_NUMBER, documentId); CashReceiptDocument crDoc = (CashReceiptDocument) businessObjectService.findByPrimaryKey(getDataDictionaryService().getDocumentClassByTypeName(KFSConstants.FinancialDocumentTypeCodes.CASH_RECEIPT), primaryKeys); // get the DepositCashReceiptControl for the CashReceiptHeader if (crDoc != null) { List crcList = crDoc.getDepositCashReceiptControl(); if (!crcList.isEmpty()) { DepositCashReceiptControl dpcrc = (DepositCashReceiptControl) crcList.get(0); // get the Deposit and follow it to the CashManagementDocument Deposit d = dpcrc.getDeposit(); cmdoc = d.getCashManagementDocument(); } } return cmdoc; } /** * This method creates a new cash management document and sets the provided values as attributes to the document. * The steps followed to create a new cash management document are as follows: * <ul> * <li>Find the drawer for the campus code given.</li> * <li>Make sure the drawer is closed, force the drawer closed if it is not already closed.</li> * <li>Create the cash management document, set the provided values to the document and link it to the cash drawer</li> * </ul> * * If the campusCode or docDescription values are null, an IllegalArgumentException will be thrown. * * TODO - annotation is not used or set at all in this method, remove it if appropriate. * * @param campusCode The campus code of the cash drawer. * @param docDescription The document description to be set on the new cash management document. * @param annotation * @return A new instance of a CashManagementDocument (not persisted). * * @see org.kuali.kfs.fp.document.service.CashManagementService#createCashManagementDocument(java.lang.String, * java.lang.String, java.lang.String) */ @Override public CashManagementDocument createCashManagementDocument(String campusCode, String docDescription, String annotation) { if (StringUtils.isBlank(campusCode)) { throw new IllegalArgumentException("invalid (blank) campus code"); } if (StringUtils.isBlank(docDescription)) { throw new IllegalArgumentException("invalid (blank) docDescription"); } // check user authorization Person user = GlobalVariables.getUserSession().getPerson(); DocumentAuthorizer documentAuthorizer = SpringContext.getBean(DocumentHelperService.class).getDocumentAuthorizer(KFSConstants.FinancialDocumentTypeCodes.CASH_MANAGEMENT); documentAuthorizer.canInitiate(KFSConstants.FinancialDocumentTypeCodes.CASH_MANAGEMENT, user); // check cash drawer CashDrawer cd = cashDrawerService.getByCampusCode(campusCode); if (cd == null) { throw new RuntimeException("No cash drawer exists for campus code "+campusCode+"; please create on via the Cash Drawer Maintenance Document before attemping to create a CashManagementDocument for campus "+campusCode); } String controllingDocId = cd.getReferenceFinancialDocumentNumber(); // KULEDOCS-1475: adding handling for two things which should never happen: // 1. CashDrawer is open or locked by document 'null' // 2. CashDrawer is open or locked by a document which doesn't exist if (!cd.isClosed() || cd.getStatusCode() == null) { boolean forceDrawerClosed = false; if (cd.getStatusCode() == null) { forceDrawerClosed = true; } if (StringUtils.isBlank(controllingDocId)) { forceDrawerClosed = true; } else if (!documentService.documentExists(controllingDocId)) { forceDrawerClosed = true; } if (forceDrawerClosed) { cashDrawerService.closeCashDrawer(cd); cd = cashDrawerService.getByCampusCode(campusCode); if (cd == null) { throw new RuntimeException("No cash drawer exists for campus code "+campusCode+"; please create on via the Cash Drawer Maintenance Document before attemping to create a CashManagementDocument for campus "+campusCode); } } } CashManagementDocument cmDoc = null; if (cd.isClosed()) { // create the document try { cmDoc = (CashManagementDocument) documentService.getNewDocument(CashManagementDocument.class); cmDoc.getDocumentHeader().setDocumentDescription(docDescription); cmDoc.setCampusCode(campusCode); cmDoc.setCashDrawer(cd); cmDoc.getCurrentTransaction().setCampusCode(cmDoc.getCampusCode()); cmDoc.getCurrentTransaction().setReferenceFinancialDocumentNumber(cmDoc.getDocumentNumber()); cmDoc.getCurrentTransaction().setOpenItemsInProcess(getOpenItemsInProcess(cmDoc)); } catch (WorkflowException e) { throw new InfrastructureException("unable to create CashManagementDocument", e); } } else { CashDrawerStatusCodeFormatter f = new CashDrawerStatusCodeFormatter(); throw new CashDrawerStateException(campusCode, controllingDocId, (String) f.format(CashDrawerConstants.STATUS_CLOSED), (String) f.format(cd.getStatusCode())); } return cmDoc; } /** * This method creates new cumulative currency and coin details for the document given. * * @param cmDoc The cash management document the cumulative details will be associated with. * @param cashieringSource The cashiering record source for the new details. */ @Override public void createNewCashDetails(CashManagementDocument cmDoc, String cashieringStatus) { CoinDetail coinDetail = new CoinDetail(); coinDetail.setDocumentNumber(cmDoc.getDocumentNumber()); coinDetail.setFinancialDocumentTypeCode(CashieringTransaction.DETAIL_DOCUMENT_TYPE); coinDetail.setCashieringStatus(cashieringStatus); businessObjectService.save(coinDetail); CurrencyDetail currencyDetail = new CurrencyDetail(); currencyDetail.setDocumentNumber(cmDoc.getDocumentNumber()); currencyDetail.setFinancialDocumentTypeCode(CashieringTransaction.DETAIL_DOCUMENT_TYPE); currencyDetail.setCashieringStatus(cashieringStatus); businessObjectService.save(currencyDetail); } /** * This method adds a new deposit to a the given CashManagementDocument. * <br/> * The following steps go into adding a deposit to a cash management document. * <ul> * <li>The given deposit parameters are validated. * <li>The corresponding cash drawer is locked * <li>The given cashiering check records are turned into check records * <li>The new deposit is created * <li>The deposit is added to the cash management document and persisted * <li>The list of cash receipts are associated with the new deposit * <li>The deposit is saved again to ensure all links and attributes of the deposit are set appropriately and persisted * <li>The drawer is unlocked * <ul> * * @param cashManagementDoc The document to have the deposit added to. * @param depositTicketNumber The ticket number of the deposit being added. * @param bankAccount The bank account on the deposit. * @param selectedCashReceipts The collection of cash receipts associated with the new deposit. * @param selectedCashieringChecks The collection of checks associated with the new deposit. * @param isFinalDeposit A flag used to identify if a deposit is the final deposit to be added to a cash management document. * * @see org.kuali.kfs.fp.document.service.CashManagementService#addInterimDeposit(org.kuali.kfs.fp.document.CashManagementDocument, * java.lang.String, org.kuali.kfs.fp.businessobject.BankAccount, java.util.List) */ @Override @SuppressWarnings("deprecation") public void addDeposit(CashManagementDocument cashManagementDoc, String depositTicketNumber, Bank bank, List selectedCashReceipts, List selectedCashieringChecks, boolean isFinalDeposit) { validateDepositParams(cashManagementDoc, bank, selectedCashReceipts); String depositTypeCode = DepositConstants.DEPOSIT_TYPE_INTERIM; if (isFinalDeposit) { depositTypeCode = DepositConstants.DEPOSIT_TYPE_FINAL; } // lock the cashDrawer cashDrawerService.lockCashDrawer(cashManagementDoc.getCashDrawer(), cashManagementDoc.getDocumentNumber()); // turn the list of selected check sequence ids into a list of actual check records Map<Integer, Check> checks = getUndepositedChecksAsMap(cashManagementDoc); List<Check> checksToSave = new ArrayList<Check>(); if (selectedCashieringChecks != null) { for (Object o: selectedCashieringChecks) { Integer sequenceId = (Integer)o; Check check = checks.get(sequenceId); checksToSave.add(check); } } // create the Deposit Deposit deposit = buildDeposit(cashManagementDoc, depositTypeCode, depositTicketNumber, bank, selectedCashReceipts, checksToSave); // attach the deposit to the document List deposits = cashManagementDoc.getDeposits(); deposits.add(deposit); documentService.updateDocument(cashManagementDoc); // associate the CashReceipts with the deposit List dccList = new ArrayList(); for (Iterator i = selectedCashReceipts.iterator(); i.hasNext();) { CashReceiptDocument crDoc = (CashReceiptDocument) i.next(); FinancialSystemDocumentHeader dh = crDoc.getFinancialSystemDocumentHeader(); // change the doc status if it is not interim String statusCode = isFinalDeposit ? CashReceipt.FINAL : CashReceipt.INTERIM; if (!dh.getFinancialDocumentStatusCode().equalsIgnoreCase(CashReceipt.INTERIM)) { dh.setFinancialDocumentStatusCode(statusCode); documentService.updateDocument(crDoc); } DepositCashReceiptControl dcc = new DepositCashReceiptControl(); dcc.setFinancialDocumentCashReceiptNumber(crDoc.getDocumentNumber()); dcc.setFinancialDocumentDepositNumber(deposit.getDocumentNumber()); dcc.setFinancialDocumentDepositLineNumber(deposit.getFinancialDocumentDepositLineNumber()); dcc.setCashReceiptDocument(crDoc); dcc.setDeposit(deposit); dccList.add(dcc); } // crHeaders get saved as side-effect of saving dccs businessObjectService.save(dccList); // make sure all checks have the right deposit line number for (Check check: checksToSave) { check.setFinancialDocumentDepositLineNumber(deposit.getFinancialDocumentDepositLineNumber()); } businessObjectService.save(checksToSave); // unlock the cashDrawer, if needed if (!isFinalDeposit) { cashDrawerService.unlockCashDrawer(cashManagementDoc.getCashDrawer(), cashManagementDoc.getDocumentNumber()); } } /** * Validates the given Deposit parameters, throwing various (runtime) exceptions if errors exist. * * @param cashManagementDoc The document the deposit will be added to. * @param bank The bank account of the deposit being added. * @param selectedCashReceipts The collection of cash receipts associated with the new deposit. */ protected void validateDepositParams(CashManagementDocument cashManagementDoc, Bank bank, List<CashReceiptDocument> selectedCashReceipts) { if (cashManagementDoc == null) { throw new IllegalArgumentException("invalid (null) cashManagementDoc"); } else if (!cashManagementDoc.getDocumentHeader().getWorkflowDocument().isSaved()) { throw new IllegalStateException("cashManagementDoc '" + cashManagementDoc.getDocumentNumber() + "' is not in 'saved' state"); } else if (cashManagementDoc.hasFinalDeposit()) { throw new IllegalStateException("cashManagementDoc '" + cashManagementDoc.getDocumentNumber() + "' hasFinalDeposit"); } if (bank == null) { throw new IllegalArgumentException("invalid (null) bank"); } if (selectedCashReceipts == null) { throw new IllegalArgumentException("invalid (null) cashReceipts list"); } else { for (CashReceiptDocument cashReceipt : selectedCashReceipts) { String statusCode = cashReceipt.getFinancialSystemDocumentHeader().getFinancialDocumentStatusCode(); //if (!StringUtils.equals(statusCode, DocumentStatusCodes.CashReceipt.VERIFIED)) { if (!StringUtils.equals(statusCode, DocumentStatusCodes.CashReceipt.VERIFIED) && !StringUtils.equals(statusCode, DocumentStatusCodes.CashReceipt.INTERIM)) { throw new InvalidCashReceiptState("cash receipt document " + cashReceipt.getDocumentNumber() + " has a status other than 'verified' or 'interim' "); } } } } /** * * This method builds a new deposit object from the parameters provided. * * @param cashManagementDoc The cash management document the deposit will be added to. * @param depositTypeCode The type code associated with the deposit. * @param depositTicketNumber The deposit ticket number to be set on the deposit object. * @param bank The bank account of the deposit. * @param selectedCashReceipts The cash receipts that make up the deposit. * @param selectedCashieringChecks The cashiering checks that make up the deposit. * @return A new instance of a deposit generated from all the parameters provided. */ protected Deposit buildDeposit(CashManagementDocument cashManagementDoc, String depositTypeCode, String depositTicketNumber, Bank bank, List<CashReceiptDocument> selectedCashReceipts, List selectedCashieringChecks) { Deposit deposit = new Deposit(); deposit.setDocumentNumber(cashManagementDoc.getDocumentNumber()); deposit.setCashManagementDocument(cashManagementDoc); deposit.setDepositTypeCode(depositTypeCode); deposit.setDepositDate(dateTimeService.getCurrentSqlDate()); deposit.setBank(bank); deposit.setDepositBankCode(bank.getBankCode()); // derive the line number int lineNumber = cashManagementDoc.getNextDepositLineNumber(); deposit.setFinancialDocumentDepositLineNumber(new Integer(lineNumber)); // trim depositTicketNumber to empty, because the field is optional deposit.setDepositTicketNumber(StringUtils.trimToEmpty(depositTicketNumber)); // total up the cash receipts KualiDecimal total = KualiDecimal.ZERO; for (Iterator i = selectedCashReceipts.iterator(); i.hasNext();) { CashReceiptDocument crDoc = (CashReceiptDocument) i.next(); if (crDoc.getFinancialSystemDocumentHeader().getFinancialDocumentStatusCode().equalsIgnoreCase(CashReceipt.VERIFIED)) { total = total.add(crDoc.getTotalConfirmedCheckAmount()); } } Check currCheck; for (Object checkObj: selectedCashieringChecks) { currCheck = (Check)checkObj; total = total.add(currCheck.getAmount()); } deposit.setDepositAmount(total); return deposit; } /** * This method returns all undeposited checks as a map with the key of each value in the map equal to the sequence id * of the corresponding check. * * @param cmDoc The cash management doc to find undeposited checks for. * @return A map of checks keyed on sequence id. */ protected Map<Integer, Check> getUndepositedChecksAsMap(CashManagementDocument cmDoc) { Map<Integer, Check> checks = new HashMap<Integer, Check>(); List<Check> checkList = cashManagementDao.selectUndepositedCashieringChecks(cmDoc.getDocumentNumber()); if (checkList != null && checkList.size() > 0) { for (Check check: checkList) { checks.put(check.getSequenceId(), check); } } return checks; } /** * This method cancels a cash management document, effectively nullifying all values and attributes associated with * the document. Canceling a CashManagementDocument results in the following: * <ul> * <li>Cancels (deletes) all deposits associated with the document.</li> * <li>Recloses the drawer</li> * <li>Remove all currency and coin records generated by the document.</li> * </ul> * <br> * NOTE: Method should only be called after the appropriate CashManagementDocumentRule has been successfully passed. * * @param cmDoc The CashManagementDocument to be canceled. * * @see org.kuali.kfs.fp.document.service.CashManagementService#cancelCashManagementDocument(org.kuali.kfs.fp.document.CashManagementDocument) */ @Override public void cancelCashManagementDocument(CashManagementDocument cmDoc) { if (cmDoc == null) { throw new IllegalArgumentException("invalid (null) CashManagementDocument"); } // cancel each deposit (which also deletes the records connecting the Deposit to a CashManagementDoc List deposits = cmDoc.getDeposits(); for (Iterator i = deposits.iterator(); i.hasNext();) { Deposit deposit = (Deposit) i.next(); cancelDeposit(deposit); } // reclose the cashDrawer String unitName = cmDoc.getCampusCode(); cashDrawerService.closeCashDrawer(cmDoc.getCashDrawer()); // cleanup the CMDoc, but let the postprocessor itself save it cmDoc.setDeposits(new ArrayList()); cmDoc.getFinancialSystemDocumentHeader().setFinancialDocumentStatusCode(DocumentStatusCodes.CANCELLED); // kill off cumulative currency/coin detail records for this document (canceling the deposits kills the deposit records) String[] cashieringSourcesToDelete = { KFSConstants.CurrencyCoinSources.CASH_RECEIPTS, CashieringTransaction.DETAIL_DOCUMENT_TYPE, KFSConstants.CurrencyCoinSources.CASH_MANAGEMENT_IN, KFSConstants.CurrencyCoinSources.CASH_MANAGEMENT_OUT }; for (String cashieringSourceToDelete : cashieringSourcesToDelete) { CurrencyDetail currencyDetail = cashManagementDao.findCurrencyDetailByCashieringStatus(cmDoc.getDocumentNumber(), CashieringTransaction.DETAIL_DOCUMENT_TYPE, cashieringSourceToDelete); if (currencyDetail != null) { businessObjectService.delete(currencyDetail); } CoinDetail coinDetail = cashManagementDao.findCoinDetailByCashieringStatus(cmDoc.getDocumentNumber(), CashieringTransaction.DETAIL_DOCUMENT_TYPE, cashieringSourceToDelete); if (coinDetail != null) { businessObjectService.delete(coinDetail); } } } /** * This method cancels a given deposit. This equates to the following: * <ul> * <li>Resetting all associated CashReceipts to a state of VERIFIED.</li> * <li>Update all associated cashiering checks to a be un-deposited.</li> * <li>Unlock the cash drawer if needed.</li> * <li>Delete the deposit.</li> * </ul> * * @see org.kuali.kfs.fp.document.service.CashManagementService#cancelDeposit(org.kuali.kfs.fp.businessobject.Deposit) */ @Override public void cancelDeposit(Deposit deposit) { if (deposit == null) { throw new IllegalArgumentException("invalid (null) deposit"); } // reload it, to forestall OptimisticLockExceptions deposit.refresh(); // save campus name, for possible later use String depositCampus = deposit.getCashManagementDocument().getCampusCode(); // update every CashReceipt associated with this Deposit List depositCashReceiptControls = deposit.getDepositCashReceiptControl(); for (Iterator j = depositCashReceiptControls.iterator(); j.hasNext();) { DepositCashReceiptControl dcc = (DepositCashReceiptControl) j.next(); if (!ObjectUtils.isNull(dcc)) { dcc.refreshReferenceObject("cashReceiptDocument"); CashReceiptDocument crDoc = dcc.getCashReceiptDocument(); if (!ObjectUtils.isNull(crDoc)) { crDoc.refreshReferenceObject("documentHeader"); FinancialSystemDocumentHeader crdh = crDoc.getFinancialSystemDocumentHeader(); if (!ObjectUtils.isNull(crdh)) { if (!(deposit.getDepositTypeCode().equalsIgnoreCase(DepositConstants.DEPOSIT_TYPE_FINAL) && crdh.getFinancialDocumentStatusCode().equalsIgnoreCase(CashReceipt.INTERIM))) { crdh.setFinancialDocumentStatusCode(CashReceipt.VERIFIED); } documentService.updateDocument(crDoc); } } } } // un-deposit all cashiering checks associated with the deposit List<Check> depositedChecks = selectCashieringChecksForDeposit(deposit.getDocumentNumber(), deposit.getFinancialDocumentDepositLineNumber()); for (Check check: depositedChecks) { check.setFinancialDocumentDepositLineNumber(null); } businessObjectService.save(depositedChecks); // unlock the cashDrawer, if needed if (LOG.isDebugEnabled()) { LOG.debug("deposit deposit type = "+deposit.getDepositTypeCode()+"; constant = "+KFSConstants.DepositConstants.DEPOSIT_TYPE_FINAL+"; are they equal? "+deposit.getDepositTypeCode().equals(KFSConstants.DepositConstants.DEPOSIT_TYPE_FINAL)); } if (deposit.getDepositTypeCode().equals(KFSConstants.DepositConstants.DEPOSIT_TYPE_FINAL)) { CashDrawer drawer = cashDrawerService.getByCampusCode(deposit.getCashManagementDocument().getCampusCode()); CurrencyDetail currencyDetail = cashManagementDao.findCurrencyDetailByCashieringStatus(deposit.getCashManagementDocument().getDocumentNumber(), CashieringTransaction.DETAIL_DOCUMENT_TYPE, KFSConstants.CurrencyCoinSources.DEPOSITS); if (currencyDetail != null) { drawer.addCurrency(currencyDetail); businessObjectService.delete(currencyDetail); } CoinDetail coinDetail = cashManagementDao.findCoinDetailByCashieringStatus(deposit.getCashManagementDocument().getDocumentNumber(), CashieringTransaction.DETAIL_DOCUMENT_TYPE, KFSConstants.CurrencyCoinSources.DEPOSITS); if (coinDetail != null) { drawer.addCoin(coinDetail); businessObjectService.delete(coinDetail); } businessObjectService.save(drawer); cashDrawerService.unlockCashDrawer(drawer, deposit.getDocumentNumber()); } // delete the Deposit from the database businessObjectService.delete(deposit); } /** * This method performs the necessary steps to finalize a cash management document. These steps include: * <ul> * <li>Finalize all associated cash receipts. * <li>Generate the master currency and coin details and persist them. * <li>Update the CashManagementDocument status to APPROVED. * </ul> * * <br> * NOTE: Method should only be called after the appropriate CashManagementDocumentRule has been successfully passed * * @param cmDoc The CashManagementDocument to be finalized. * * @see org.kuali.kfs.fp.document.service.CashManagementService#finalizeCashManagementDocument(org.kuali.kfs.fp.document.CashManagementDocument) */ @Override public void finalizeCashManagementDocument(CashManagementDocument cmDoc) { if (cmDoc == null) { throw new IllegalArgumentException("invalid (null) CashManagementDocument"); } if (!cmDoc.hasFinalDeposit()) { throw new IllegalStateException("cmDoc " + cmDoc.getDocumentNumber() + " is missing a FinalDeposit"); } String campusCode = cmDoc.getCampusCode(); cashDrawerService.closeCashDrawer(campusCode); CashDrawer cd = cashDrawerService.getByCampusCode(campusCode); // finalize the CashReceipts List<Deposit> deposits = cmDoc.getDeposits(); for (Deposit deposit : deposits) { List<CashReceiptDocument> receipts = retrieveCashReceipts(deposit); for (CashReceiptDocument receipt : receipts) { // marks GLPEs of CRs as APPROVED for (GeneralLedgerPendingEntry glpe : receipt.getGeneralLedgerPendingEntries()) { glpe.setFinancialDocumentApprovedCode(DocumentStatusCodes.APPROVED); } // mark CRs themselves as APPROVED receipt.getFinancialSystemDocumentHeader().setFinancialDocumentStatusCode(DocumentStatusCodes.APPROVED); // persist documentService.updateDocument(receipt); } } // generate the master currency and coin details; save those CurrencyDetail masterCurrencyDetail = this.generateMasterCurrencyDetail(cmDoc); //businessObjectService.save(masterCurrencyDetail); CoinDetail masterCoinDetail = this.generateMasterCoinDetail(cmDoc); //businessObjectService.save(masterCoinDetail); generateMasterRecord(cmDoc, masterCurrencyDetail, masterCoinDetail); // finalize the CMDoc, but let the postprocessor save it cmDoc.getFinancialSystemDocumentHeader().setFinancialDocumentStatusCode(DocumentStatusCodes.APPROVED); } /** * This method verifies that all cash receipts for the document are deposited. * * @param cmDoc The cash management document to verify. * @return True if all CashReceipts are deposited, false otherwise. */ @Override public boolean allVerifiedCashReceiptsAreDeposited(CashManagementDocument cmDoc) { boolean result = true; List verifiedReceipts = SpringContext.getBean(CashReceiptService.class).getCashReceipts(cmDoc.getCampusCode(), CashReceipt.VERIFIED); for (Object o: verifiedReceipts) { if (!verifyCashReceiptIsDeposited(cmDoc, (CashReceiptDocument)o)) { result = false; break; } } return result; } /** * This method returns a collection of cash receipts associated with the deposit given. * * @param deposit The deposit to retrieve all the cash receipts for. * @return A collection of cash receipts associated with the deposit given. * * @see org.kuali.kfs.fp.document.service.CashManagementService#retrieveCashReceipts(org.kuali.kfs.fp.businessobject.Deposit) */ @Override public List<CashReceiptDocument> retrieveCashReceipts(Deposit deposit) { List<CashReceiptDocument> cashReceiptDocuments = new ArrayList<CashReceiptDocument>(); if (ObjectUtils.isNull(deposit.getDepositCashReceiptControl()) || deposit.getDepositCashReceiptControl().isEmpty() ) { deposit.refreshReferenceObject("depositCashReceiptControl"); } Map pkMap = new HashMap(); for (Object dcrcAsObject : deposit.getDepositCashReceiptControl()) { final DepositCashReceiptControl dcrc = (DepositCashReceiptControl)dcrcAsObject; try { CashReceiptDocument crDoc = (CashReceiptDocument)documentService.getByDocumentHeaderId(dcrc.getFinancialDocumentCashReceiptNumber()); final WorkflowDocument headerWorkflowDoc = crDoc.getDocumentHeader().getWorkflowDocument(); crDoc.refreshReferenceObject("documentHeader"); crDoc.getDocumentHeader().setWorkflowDocument(headerWorkflowDoc); cashReceiptDocuments.add(crDoc); } catch (WorkflowException we) { throw new RuntimeException("Could not retrieve related Cash Receipts", we); } } return cashReceiptDocuments; } /** * Verifies if a given cash receipt is deposited as part of the given cash management document. * * @param cmDoc The cash management document to search through. * @param crDoc The cash receipt to check the deposited status of. * @return True if the given cash receipt document is deposited as part of the given cash management document, false otherwise. */ @Override public boolean verifyCashReceiptIsDeposited(CashManagementDocument cmDoc, CashReceiptDocument crDoc) { boolean thisCRDeposited = false; for (Deposit deposit: cmDoc.getDeposits()) { if (deposit.containsCashReceipt(crDoc)) { thisCRDeposited = true; break; } } return thisCRDeposited; } /** * This method turns the last interim deposit into the final deposit and locks the cash drawer. A deposit is turned into * a final deposit by updating the deposit type code. * <br> * NOTE: This method throws an IllegalStateException if a final deposit already exists for this document or if there * are any undeposited cash receipts. * * @param cmDoc The cash management document to take deposits from for finalization. */ @Override public void finalizeLastInterimDeposit(CashManagementDocument cmDoc) { // if there's already a final deposit, throw an IllegalStateException if (cmDoc.hasFinalDeposit()) { throw new IllegalStateException("CashManagementDocument #"+cmDoc.getDocumentNumber()+" already has a final deposit"); } // if there are still verified un-deposited cash receipts, throw an IllegalStateException List verifiedReceipts = SpringContext.getBean(CashReceiptService.class).getCashReceipts(cmDoc.getCampusCode(), CashReceipt.VERIFIED); for (Object o: verifiedReceipts) { CashReceiptDocument crDoc = (CashReceiptDocument)o; if (!verifyCashReceiptIsDeposited(cmDoc, crDoc)) { throw new IllegalStateException("Verified Cash Receipt Document #"+crDoc.getDocumentNumber()+" must be deposited for this to be a final deposit"); } } // lock the cash drawer cashDrawerService.lockCashDrawer(cmDoc.getCashDrawer(), cmDoc.getDocumentNumber()); // change the deposit type code for the last deposit List<Deposit> allDeposits = cmDoc.getDeposits(); Deposit lastInterim = allDeposits.get(allDeposits.size() - 1); lastInterim.setDepositTypeCode(DepositConstants.DEPOSIT_TYPE_FINAL); finalizeCashReceiptsForDeposit(lastInterim); documentService.updateDocument(cmDoc); } /** * This method switches cash receipts to "final" status as opposed to "interim" status. * * @param deposit The deposit the cash receipts are associated with. */ protected void finalizeCashReceiptsForDeposit(Deposit deposit) { List cashReceipts = this.retrieveCashReceipts(deposit); for (Object o: cashReceipts) { CashReceiptDocument crDoc = (CashReceiptDocument)o; crDoc.getFinancialSystemDocumentHeader().setFinancialDocumentStatusCode(CashReceipt.FINAL); documentService.updateDocument(crDoc); } } /** * This method applies the cashiering transaction to the given CashManagementDocument. This is accomplished by * retrieving a CashieringTransactionRule object and running the appropriate methods to process the cashiering * application rules. * * @see org.kuali.kfs.fp.document.service.CashManagementService#applyCashieringTransaction(org.kuali.kfs.fp.document.CashManagementDocument, org.kuali.kfs.fp.businessobject.CashieringTransaction) * @see org.kuali.kfs.fp.document.validation.impl.CashieringTransactionRule#processCashieringTransactionApplicationRules(CashManagementDocument) */ @Override public void applyCashieringTransaction(CashManagementDocument cmDoc) { if (cmDoc.getCashDrawer() == null) { cmDoc.setCashDrawer(cashDrawerService.getByCampusCode(cmDoc.getCampusCode())); } this.transferChecksToCashManagementDocument(cmDoc, cmDoc.getCurrentTransaction()); this.saveChecks(cmDoc); this.completeNewItemInProcess(cmDoc.getCurrentTransaction()); if (cmDoc.getCurrentTransaction().getNewItemInProcess() != null) { this.saveNewItemInProcess(cmDoc, cmDoc.getCurrentTransaction()); } this.saveExisingItemsInProcess(cmDoc, cmDoc.getCurrentTransaction()); this.saveMoneyInCash(cmDoc, cmDoc.getCurrentTransaction()); this.saveMoneyOutCash(cmDoc, cmDoc.getCurrentTransaction()); this.updateCashDrawer(cmDoc.getCashDrawer(), cmDoc.getCurrentTransaction()); cmDoc.resetCurrentTransaction(); } /** * This method puts money from the "money in" portion of the transaction into the cash drawer, and takes money from the * "money out" portion of the cash drawer out. * * @param drawer The cash drawer to operate on. * @param trans The transaction that is the operation. */ protected void updateCashDrawer(CashDrawer drawer, CashieringTransaction trans) { // add money in to cash drawer if (!trans.getMoneyInCurrency().isEmpty()) { drawer.addCurrency(trans.getMoneyInCurrency()); } if (!trans.getMoneyInCoin().isEmpty()) { drawer.addCoin(trans.getMoneyInCoin()); } // subtract money out from cash drawer if (!trans.getMoneyOutCurrency().isEmpty()) { drawer.removeCurrency(trans.getMoneyOutCurrency()); } if (!trans.getMoneyOutCoin().isEmpty()) { drawer.removeCoin(trans.getMoneyOutCoin()); } businessObjectService.save(drawer); } /** * * This method completes the new item in process by setting the item remaining amount equal to the item amount. * * @param trans The transaction being performed. */ protected void completeNewItemInProcess(CashieringTransaction trans) { if (trans.getNewItemInProcess().isPopulated()) { trans.getNewItemInProcess().setItemRemainingAmount(trans.getNewItemInProcess().getItemAmount()); } else { trans.setNewItemInProcess(null); // we don't want to save it or deal with it } } /** * * This method retrieves all the checks for the given document and persists them. * * @param cmDoc The cash management document the checks will be saved against. */ protected void saveChecks(CashManagementDocument cmDoc) { if (cmDoc.getChecks() != null) { for (Check check: cmDoc.getChecks()) { check.setDocumentNumber(cmDoc.getDocumentNumber()); check.setFinancialDocumentTypeCode(CashieringTransaction.DETAIL_DOCUMENT_TYPE); check.setCashieringStatus(KFSConstants.CheckSources.CASH_MANAGEMENT); businessObjectService.save(check); } } } /** * * This method retrieves the checks from the transaction and adds them to the cash management document. * * @param cmDoc The document the checks will be transferred to. * @param trans The transaction the checks are associated with. */ protected void transferChecksToCashManagementDocument(CashManagementDocument cmDoc, CashieringTransaction trans) { for (Check check: trans.getMoneyInChecks()) { check.setFinancialDocumentTypeCode(CashieringTransaction.DETAIL_DOCUMENT_TYPE); check.setCashieringStatus(KFSConstants.CheckSources.CASH_MANAGEMENT); check.setDocumentNumber(cmDoc.getDocumentNumber()); cmDoc.addCheck(check); } } /** * This methods checks if data was actually entered for the new item in process; if so, it saves that item in process. * * @param cmDoc The cash management doc that the new item in process will be associated with. * @param trans The cashiering transaction that created the new item in process. */ protected void saveNewItemInProcess(CashManagementDocument cmDoc, CashieringTransaction trans) { if (trans.getNewItemInProcess().isPopulated()) { trans.getNewItemInProcess().setItemRemainingAmount(trans.getNewItemInProcess().getItemAmount()); trans.getNewItemInProcess().setItemReducedAmount(KualiDecimal.ZERO); trans.getNewItemInProcess().setCampusCode(cmDoc.getCampusCode()); businessObjectService.save(trans.getNewItemInProcess()); // put it in the list of open items in process trans.getOpenItemsInProcess().add(trans.getNewItemInProcess()); CashDrawer drawer = cmDoc.getCashDrawer(); if (drawer.getFinancialDocumentMiscellaneousAdvanceAmount() == null) { drawer.setFinancialDocumentMiscellaneousAdvanceAmount(trans.getNewItemInProcess().getItemAmount()); } else { drawer.setFinancialDocumentMiscellaneousAdvanceAmount(drawer.getFinancialDocumentMiscellaneousAdvanceAmount().add(trans.getNewItemInProcess().getItemAmount())); } } } /** * This method checks the cashiering transaction to see if any open items in process were at least partially paid back; * it then saves the changes. * * @param cmDoc The cash management document that the items in process will be associated with * @param trans The cashiering transaction the items in process are associated with. */ protected void saveExisingItemsInProcess(CashManagementDocument cmDoc, CashieringTransaction trans) { if (trans.getOpenItemsInProcess() != null) { CashDrawer drawer = cmDoc.getCashDrawer(); for (CashieringItemInProcess itemInProc: trans.getOpenItemsInProcess()) { if (itemInProc.getCurrentPayment() != null && !itemInProc.getCurrentPayment().equals(KualiDecimal.ZERO)) { itemInProc.setItemRemainingAmount(itemInProc.getItemRemainingAmount().subtract(itemInProc.getCurrentPayment())); itemInProc.setItemReducedAmount(itemInProc.getItemReducedAmount().add(itemInProc.getCurrentPayment())); if (drawer.getFinancialDocumentMiscellaneousAdvanceAmount() != null) { drawer.setFinancialDocumentMiscellaneousAdvanceAmount(drawer.getFinancialDocumentMiscellaneousAdvanceAmount().subtract(itemInProc.getCurrentPayment())); } itemInProc.setCurrentPayment(KualiDecimal.ZERO); if (itemInProc.getItemRemainingAmount().equals(KualiDecimal.ZERO)) { itemInProc.setItemClosedDate(new java.sql.Date(SpringContext.getBean(DateTimeService.class).getCurrentDate().getTime())); } businessObjectService.save(itemInProc); } } } } /** * * This method retrieves the amount of cash in the "money in" portion of the transaction and saves it to the * cash management document. * * @param cmDoc The cash management document that the cash will be saved to. * @param trans The cashiering transaction the cash is currently associated with. */ protected void saveMoneyInCash(CashManagementDocument cmDoc, CashieringTransaction trans) { // get the cumulative money in coin for this doc CoinDetail cumulativeMoneyInCoin = cashManagementDao.findCoinDetailByCashieringStatus(cmDoc.getDocumentNumber(), CashieringTransaction.DETAIL_DOCUMENT_TYPE, KFSConstants.CurrencyCoinSources.CASH_MANAGEMENT_IN); // add the new money in coin cumulativeMoneyInCoin.add(trans.getMoneyInCoin()); // save the cumulative businessObjectService.save(cumulativeMoneyInCoin); CurrencyDetail cumulativeMoneyInCurrency = cashManagementDao.findCurrencyDetailByCashieringStatus(cmDoc.getDocumentNumber(), CashieringTransaction.DETAIL_DOCUMENT_TYPE, KFSConstants.CurrencyCoinSources.CASH_MANAGEMENT_IN); cumulativeMoneyInCurrency.add(trans.getMoneyInCurrency()); businessObjectService.save(cumulativeMoneyInCurrency); } /** * * This method retrieves the amount of cash in the "money out" portion of the transaction and saves it to the * cash management document. * * @param cmDoc The cash management document that the cash will be saved to. * @param trans The cashiering transaction the cash is currently associated with. */ protected void saveMoneyOutCash(CashManagementDocument cmDoc, CashieringTransaction trans) { CoinDetail cumulativeMoneyOutCoin = cashManagementDao.findCoinDetailByCashieringStatus(cmDoc.getDocumentNumber(), CashieringTransaction.DETAIL_DOCUMENT_TYPE, KFSConstants.CurrencyCoinSources.CASH_MANAGEMENT_OUT); cumulativeMoneyOutCoin.add(trans.getMoneyOutCoin()); businessObjectService.save(cumulativeMoneyOutCoin); CurrencyDetail cumulativeMoneyOutCurrency = cashManagementDao.findCurrencyDetailByCashieringStatus(cmDoc.getDocumentNumber(), CashieringTransaction.DETAIL_DOCUMENT_TYPE, KFSConstants.CurrencyCoinSources.CASH_MANAGEMENT_OUT); cumulativeMoneyOutCurrency.add(trans.getMoneyOutCurrency()); businessObjectService.save(cumulativeMoneyOutCurrency); } /** * This method retrieves a collection of open CashieringItemInProcess objects from the cash management document given * and returns that collection. * * @param cmDoc The document the open items in process will be retrieved from. * @return The collection of open items. * * @see org.kuali.kfs.fp.document.service.CashManagementService#getOpenItemsInProcess(org.kuali.kfs.fp.document.CashManagementDocument) */ @Override public List<CashieringItemInProcess> getOpenItemsInProcess(CashManagementDocument cmDoc) { List<CashieringItemInProcess> itemsInProcess = cashManagementDao.findOpenItemsInProcessByCampusCode(cmDoc.getCampusCode()); return (itemsInProcess == null) ? new ArrayList<CashieringItemInProcess>() : itemsInProcess; } /** * This method retrieves a collection of recently closed CashieringItemInProcess objects from the cash management * document given and returns the collection. * * @param cmDoc The cash management document the recently closed items will be retrieved from. * @return The collection of recently closed items. * * @see org.kuali.kfs.fp.document.service.CashManagementService#getRecentlyClosedItemsInProcess(org.kuali.kfs.fp.document.CashManagementDocument) */ @Override public List<CashieringItemInProcess> getRecentlyClosedItemsInProcess(CashManagementDocument cmDoc) { return cashManagementDao.findRecentlyClosedItemsInProcess(cmDoc.getCampusCode()); } /** * This method generates a master coin detail for the cash management document given. A master coin detail is a CoinDetail * that represents the result of all the money in and out of the cash drawer via the given cash management document. The * following formula is used to perform this calculation: * <ul> * <li> * "coin detail for cash receipt - coin detail for deposits + coin detail for money in - coin detail for money out" * </li> * </ul> * * @param cmDoc The document the master coin detail will be generated from. * @return The resulting coin detail. * * @see org.kuali.kfs.fp.document.service.CashManagementService#generateMasterCoinDetail(org.kuali.kfs.fp.document.CashManagementDocument) */ @Override public CoinDetail generateMasterCoinDetail(CashManagementDocument cmDoc) { CoinDetail masterDetail = new CoinDetail(); masterDetail.setDocumentNumber(cmDoc.getDocumentNumber()); masterDetail.setFinancialDocumentTypeCode(CashieringTransaction.DETAIL_DOCUMENT_TYPE); masterDetail.setCashieringStatus(KFSConstants.CurrencyCoinSources.CASH_MANAGEMENT_MASTER); masterDetail.zeroOutAmounts(); CoinDetail cashReceiptDetail = cashManagementDao.findCoinDetailByCashieringStatus(cmDoc.getDocumentNumber(), CashieringTransaction.DETAIL_DOCUMENT_TYPE, KFSConstants.CurrencyCoinSources.CASH_RECEIPTS); if (cashReceiptDetail != null) { masterDetail.add(cashReceiptDetail); } CoinDetail depositDetail = cashManagementDao.findCoinDetailByCashieringStatus(cmDoc.getDocumentNumber(), CashieringTransaction.DETAIL_DOCUMENT_TYPE, KFSConstants.CurrencyCoinSources.DEPOSITS); if (depositDetail != null) { masterDetail.subtract(depositDetail); } CoinDetail moneyInDetail = cashManagementDao.findCoinDetailByCashieringStatus(cmDoc.getDocumentNumber(), CashieringTransaction.DETAIL_DOCUMENT_TYPE, KFSConstants.CurrencyCoinSources.CASH_MANAGEMENT_IN); if (moneyInDetail != null) { masterDetail.add(moneyInDetail); } CoinDetail moneyOutDetail = cashManagementDao.findCoinDetailByCashieringStatus(cmDoc.getDocumentNumber(), CashieringTransaction.DETAIL_DOCUMENT_TYPE, KFSConstants.CurrencyCoinSources.CASH_MANAGEMENT_OUT); if (moneyOutDetail != null) { masterDetail.subtract(moneyOutDetail); } return masterDetail; } /** * This method generates a master currency detail for the cash management document given. A master currency detail is a currencyDetail * that represents the result of all the money in and out of the cash drawer via the given cash management document. The * following formula is used to perform this calculation: * <ul> * <li> * "currency detail for cash receipt - currency detail for deposits + currency detail for money in - currency detail for money out" * </li> * </ul> * * @param cmDoc The document the master currency detail will be generated from. * @return The resulting currency detail. * * @see org.kuali.kfs.fp.document.service.CashManagementService#generateMasterCurrencyDetail(org.kuali.kfs.fp.document.CashManagementDocument) */ @Override public CurrencyDetail generateMasterCurrencyDetail(CashManagementDocument cmDoc) { CurrencyDetail masterDetail = new CurrencyDetail(); masterDetail.setDocumentNumber(cmDoc.getDocumentNumber()); masterDetail.setFinancialDocumentTypeCode(CashieringTransaction.DETAIL_DOCUMENT_TYPE); masterDetail.setCashieringStatus(KFSConstants.CurrencyCoinSources.CASH_MANAGEMENT_MASTER); masterDetail.zeroOutAmounts(); CurrencyDetail cashReceiptDetail = cashManagementDao.findCurrencyDetailByCashieringStatus(cmDoc.getDocumentNumber(), CashieringTransaction.DETAIL_DOCUMENT_TYPE, KFSConstants.CurrencyCoinSources.CASH_RECEIPTS); if (cashReceiptDetail != null) { masterDetail.add(cashReceiptDetail); } CurrencyDetail depositDetail = cashManagementDao.findCurrencyDetailByCashieringStatus(cmDoc.getDocumentNumber(), CashieringTransaction.DETAIL_DOCUMENT_TYPE, KFSConstants.CurrencyCoinSources.DEPOSITS); if (depositDetail != null) { masterDetail.subtract(depositDetail); } CurrencyDetail moneyInDetail = cashManagementDao.findCurrencyDetailByCashieringStatus(cmDoc.getDocumentNumber(), CashieringTransaction.DETAIL_DOCUMENT_TYPE, KFSConstants.CurrencyCoinSources.CASH_MANAGEMENT_IN); if (moneyInDetail != null) { masterDetail.add(moneyInDetail); } CurrencyDetail moneyOutDetail = cashManagementDao.findCurrencyDetailByCashieringStatus(cmDoc.getDocumentNumber(), CashieringTransaction.DETAIL_DOCUMENT_TYPE, KFSConstants.CurrencyCoinSources.CASH_MANAGEMENT_OUT); if (moneyOutDetail != null) { masterDetail.subtract(moneyOutDetail); } return masterDetail; } /** * Populates the currency and coin detail for final deposits by setting the deposited currency or coin amount equal to the * associated cashiering record currency or coin amount. * * @param cmDoc The cash management document which has deposits to populate. */ @Override public void populateCashDetailsForDeposit(CashManagementDocument cmDoc) { // if this ever gets changed so that each deposit has currency/coin lines, then // we can just do this with the ORM, which would be *much* easier for (Deposit d: cmDoc.getDeposits()) { if (d.getDepositTypeCode().equals(DepositConstants.DEPOSIT_TYPE_FINAL)) { if (d.getDepositedCurrency() == null) { d.setDepositedCurrency(cashManagementDao.findCurrencyDetailByCashieringStatus(cmDoc.getDocumentNumber(), CashieringTransaction.DETAIL_DOCUMENT_TYPE, CurrencyCoinSources.DEPOSITS)); } if (d.getDepositedCoin() == null) { d.setDepositedCoin(cashManagementDao.findCoinDetailByCashieringStatus(cmDoc.getDocumentNumber(), CashieringTransaction.DETAIL_DOCUMENT_TYPE, CurrencyCoinSources.DEPOSITS)); } } } } /** * This method retrieves the collection of cashiering checks associated with a given deposit. * * @param documentNumber The id of the document to search for the deposit within. * @param depositLineNumber The line number of the deposit to be found. * @return A collection of checks for the deposit and document given. * * @see org.kuali.kfs.fp.document.service.CashManagementService#selectCashieringChecksForDeposit(java.lang.String, java.lang.Integer) */ @Override public List<Check> selectCashieringChecksForDeposit(String documentNumber, Integer depositLineNumber) { return cashManagementDao.selectCashieringChecksForDeposit(documentNumber, depositLineNumber); } /** * This method retrieves the collection of undeposited cashiering checks associated with the document given. * * @param documentNumber The id of the document to search for the undeposited checks within. * @return A collection of any undeposited checks for the document given. * * @see org.kuali.kfs.fp.document.service.CashManagementService#selectUndepositedCashieringChecks(java.lang.String) */ @Override public List<Check> selectUndepositedCashieringChecks(String documentNumber) { return cashManagementDao.selectUndepositedCashieringChecks(documentNumber); } /** * This method retrieves a collection of all deposited checks associated with the given document. * * @param documentNumber The document to retrieve the deposited checks from. * @return A collection of all deposited checks for the document given. * * @see org.kuali.kfs.fp.document.service.CashManagementService#selectDepositedCashieringChecks(java.lang.String) */ @Override public List<Check> selectDepositedCashieringChecks(String documentNumber) { return cashManagementDao.selectDepositedCashieringChecks(documentNumber); } /** * Total up the amounts of all checks so far deposited as part of the given cash management document. * * @param documentNumber The id of a cash management document. * @return The total amount of cashiering checks deposited so far as part of that document. */ @Override public KualiDecimal calculateDepositedCheckTotal(String documentNumber) { KualiDecimal total = KualiDecimal.ZERO; for (Check check: cashManagementDao.selectDepositedCashieringChecks(documentNumber)) { if (check != null && check.getAmount() != null && check.getAmount().isGreaterThan(KualiDecimal.ZERO)) { total = total.add(check.getAmount()); } } return total; } /** * Calculates the total amount of all the undeposited checks for a cash management document. * * @param documentNumber The id of the cash management document to pull the undeposited checks from. * @return The total amount of all undeposited checks for the document given. * * @see org.kuali.kfs.fp.document.service.CashManagementService#calculateUndepositedCheckTotal(java.lang.String) */ @Override public KualiDecimal calculateUndepositedCheckTotal(String documentNumber) { KualiDecimal total = KualiDecimal.ZERO; for (Check check: cashManagementDao.selectUndepositedCashieringChecks(documentNumber)) { if (check != null && check.getAmount() != null && check.getAmount().isGreaterThan(KualiDecimal.ZERO)) { total = total.add(check.getAmount()); } } return total; } /** * This method determines if a document can be cancelled, by reviewing a set of criteria: * - do any cash receipts exist in this document? * - do any cashiering checks exist in this document? * - do any cash details exist in this document? * If any of these questions comes back as true, then the document cannot be canceled. * * @param cmDoc The document that would be canceled. * @return True if the document can be canceled, false otherwise. * * @see org.kuali.kfs.fp.document.service.CashManagementService#allowDocumentCancellation(org.kuali.kfs.fp.document.CashManagementDocument) */ @Override public boolean allowDocumentCancellation(CashManagementDocument cmDoc) { return !existCashReceipts(cmDoc) && !existCashieringChecks(cmDoc) && !existCashDetails(cmDoc); } /** * This method determines if any verified, interim, or final cash receipts currently exist. * * @param cmDoc The cash management document to find cash receipts associated with the campus of. * @return True if there's some cash receipts that verified, interim, or final in this campus; false if otherwise. */ protected boolean existCashReceipts(CashManagementDocument cmDoc) { List<CashReceiptDocument> cashReceipts = SpringContext.getBean(CashReceiptService.class).getCashReceipts(cmDoc.getCampusCode(), new String[] {CashReceipt.VERIFIED, CashReceipt.INTERIM, CashReceipt.FINAL} ); return cashReceipts != null && cashReceipts.size() > 0; } /** * This method determines if any populated currency or coin details exist for the given document. * * @param cmDoc A cash management document to find details. * @return True if it finds populated currency or coin details, false if otherwise. */ protected boolean existCashDetails(CashManagementDocument cmDoc) { boolean result = false; List<CurrencyDetail> currencyDetails = cashManagementDao.getAllCurrencyDetails(cmDoc.getDocumentNumber()); if (currencyDetails != null && currencyDetails.size() > 0) { for (CurrencyDetail detail: currencyDetails) { result |= !detail.isEmpty(); } } if (!result) { List<CoinDetail> coinDetails = cashManagementDao.getAllCoinDetails(cmDoc.getDocumentNumber()); if (coinDetails != null && coinDetails.size() > 0) { for (CoinDetail detail: coinDetails) { result |= !detail.isEmpty(); } } } return result; } /** * This method determines if cashiering checks exist for the cash management document. * * @param cmDoc The cash management document to test. * @return True if it finds some checks, false if otherwise. */ protected boolean existCashieringChecks(CashManagementDocument cmDoc) { List<Check> undepositedChecks = this.selectUndepositedCashieringChecks(cmDoc.getDocumentNumber()); List<Check> depositedChecks = cashManagementDao.selectDepositedCashieringChecks(cmDoc.getDocumentNumber()); return (undepositedChecks != null && undepositedChecks.size() > 0) || (depositedChecks != null && depositedChecks.size() > 0); } /** * This method retrieves the next available check line number from the document provided. * * @param documentNumber The document to get the next check line number from. * @return The next available check line number. * * @see org.kuali.kfs.fp.document.service.CashManagementService#selectNextAvailableCheckLineNumber(java.lang.String) */ @Override public Integer selectNextAvailableCheckLineNumber(String documentNumber) { return cashManagementDao.selectNextAvailableCheckLineNumber(documentNumber); } /** * This method retrieves the cash details for the final deposit object. The resulting map contains a CurrencyDetail and a * CoinDetail object, both keyed by the class of detail they represent (ie. CurrencyDetail.class is the map key for the * CurrencyDetail of the document). * * @param documentNumber The document the details will be generated from. * @return A map of the resulting cash details. This map is keyed by the detail class object. * * @see org.kuali.kfs.fp.document.service.CashManagementService#getCashDetailsForFinalDeposit(java.lang.String) */ @Override public Map<Class, Object> getCashDetailsForFinalDeposit(String documentNumber) { CurrencyDetail finalDepositCurrencyDetail = cashManagementDao.findCurrencyDetailByCashieringStatus(documentNumber, CashieringTransaction.DETAIL_DOCUMENT_TYPE, KFSConstants.CurrencyCoinSources.DEPOSITS); CoinDetail finalDepositCoinDetail = cashManagementDao.findCoinDetailByCashieringStatus(documentNumber, CashieringTransaction.DETAIL_DOCUMENT_TYPE, KFSConstants.CurrencyCoinSources.DEPOSITS); Map<Class, Object> result = new HashMap<Class, Object>(); if (finalDepositCurrencyDetail != null) { result.put(CurrencyDetail.class, finalDepositCurrencyDetail); } if (finalDepositCoinDetail != null) { result.put(CoinDetail.class, finalDepositCoinDetail); } return result; } /** * This method pulls out the master currency and coin details and sets them to the cashManagementDocument * @param cmDoc * @param masterCurrencyDetail * @param masterCoinDetail */ public void generateMasterRecord(CashManagementDocument cmDoc, CurrencyDetail masterCurrencyDetail, CoinDetail masterCoinDetail) { cmDoc.setFinancialDocumentHundredDollarAmount(masterCurrencyDetail.getFinancialDocumentHundredDollarAmount()); cmDoc.setFinancialDocumentFiftyDollarAmount(masterCurrencyDetail.getFinancialDocumentFiftyDollarAmount()); cmDoc.setFinancialDocumentTwentyDollarAmount(masterCurrencyDetail.getFinancialDocumentTwentyDollarAmount()); cmDoc.setFinancialDocumentTenDollarAmount(masterCurrencyDetail.getFinancialDocumentTenDollarAmount()); cmDoc.setFinancialDocumentFiveDollarAmount(masterCurrencyDetail.getFinancialDocumentFiveDollarAmount()); cmDoc.setFinancialDocumentTwoDollarAmount(masterCurrencyDetail.getFinancialDocumentTwoDollarAmount()); cmDoc.setFinancialDocumentOneDollarAmount(masterCurrencyDetail.getFinancialDocumentOneDollarAmount()); cmDoc.setFinancialDocumentOtherDollarAmount(masterCurrencyDetail.getFinancialDocumentOtherDollarAmount()); cmDoc.setFinancialDocumentHundredCentAmount(masterCoinDetail.getFinancialDocumentHundredCentAmount()); cmDoc.setFinancialDocumentFiftyCentAmount(masterCoinDetail.getFinancialDocumentFiftyCentAmount()); cmDoc.setFinancialDocumentTwentyFiveCentAmount(masterCoinDetail.getFinancialDocumentTwentyFiveCentAmount()); cmDoc.setFinancialDocumentTenCentAmount(masterCoinDetail.getFinancialDocumentTenCentAmount()); cmDoc.setFinancialDocumentFiveCentAmount(masterCoinDetail.getFinancialDocumentFiveCentAmount()); cmDoc.setFinancialDocumentOneCentAmount(masterCoinDetail.getFinancialDocumentOneCentAmount()); cmDoc.setFinancialDocumentOtherCentAmount(masterCoinDetail.getFinancialDocumentOtherCentAmount()); } // injected dependencies /** * Getter for retrieving an instance of the BusinessObjectService attribute. * * @return Current value of businessObjectService. */ public BusinessObjectService getBusinessObjectService() { return businessObjectService; } /** * Sets the businessObjectService attribute value. * * @param businessObjectService The businessObjectService to set. */ public void setBusinessObjectService(BusinessObjectService businessObjectService) { this.businessObjectService = businessObjectService; } /** * Getter for retrieving an instance of the CashDrawerService attribute. * * @return Current value of cashDrawerService. */ public CashDrawerService getCashDrawerService() { return cashDrawerService; } /** * Sets the cashDrawerService attribute value. * * @param cashDrawerService The cashDrawerService to set. */ public void setCashDrawerService(CashDrawerService cashDrawerService) { this.cashDrawerService = cashDrawerService; } /** * Gets the documentService attribute. * * @return Current value of documentService. */ public DocumentService getDocumentService() { return documentService; } /** * Sets the documentService attribute value. * * @param documentService */ public void setDocumentService(DocumentService documentService) { this.documentService = documentService; } /** * Gets the dateTimeService attribute. * * @return Current value of dateTimeService. */ public DateTimeService getDateTimeService() { return dateTimeService; } /** * Sets the dateTimeService attribute value. * * @param dateTimeService The dateTimeService to set. */ public void setDateTimeService(DateTimeService dateTimeService) { this.dateTimeService = dateTimeService; } /** * Gets the cashManagementDao attribute. * * @return Returns the cashManagementDao. */ public CashManagementDao getCashManagementDao() { return cashManagementDao; } /** * Sets the cashManagementDao attribute value. * * @param cashManagementDao The cashManagementDao to set. */ public void setCashManagementDao(CashManagementDao cashManagementDao) { this.cashManagementDao = cashManagementDao; } /** * @return an implementation of the DataDictionaryService */ public DataDictionaryService getDataDictionaryService() { return dataDictionaryService; } /** * Sets the data dictionary service implementation * @param dataDictionaryService the implementation of the data dictionary service to use */ public void setDataDictionaryService(DataDictionaryService dataDictionaryService) { this.dataDictionaryService = dataDictionaryService; } }