001 /* 002 * Copyright 2011 The Kuali Foundation. 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package org.kuali.kfs.fp.document.service.impl; 017 018 import java.util.ArrayList; 019 import java.util.HashMap; 020 import java.util.Iterator; 021 import java.util.List; 022 import java.util.Map; 023 024 import org.apache.commons.lang.StringUtils; 025 import org.kuali.kfs.fp.businessobject.CashDrawer; 026 import org.kuali.kfs.fp.businessobject.CashieringItemInProcess; 027 import org.kuali.kfs.fp.businessobject.CashieringTransaction; 028 import org.kuali.kfs.fp.businessobject.Check; 029 import org.kuali.kfs.fp.businessobject.CoinDetail; 030 import org.kuali.kfs.fp.businessobject.CurrencyDetail; 031 import org.kuali.kfs.fp.businessobject.Deposit; 032 import org.kuali.kfs.fp.businessobject.DepositCashReceiptControl; 033 import org.kuali.kfs.fp.businessobject.format.CashDrawerStatusCodeFormatter; 034 import org.kuali.kfs.fp.document.CashManagementDocument; 035 import org.kuali.kfs.fp.document.CashReceiptDocument; 036 import org.kuali.kfs.fp.document.dataaccess.CashManagementDao; 037 import org.kuali.kfs.fp.document.service.CashManagementService; 038 import org.kuali.kfs.fp.document.service.CashReceiptService; 039 import org.kuali.kfs.fp.exception.CashDrawerStateException; 040 import org.kuali.kfs.fp.exception.InvalidCashReceiptState; 041 import org.kuali.kfs.fp.service.CashDrawerService; 042 import org.kuali.kfs.sys.KFSConstants; 043 import org.kuali.kfs.sys.KFSKeyConstants; 044 import org.kuali.kfs.sys.KFSPropertyConstants; 045 import org.kuali.kfs.sys.KFSConstants.CashDrawerConstants; 046 import org.kuali.kfs.sys.KFSConstants.CurrencyCoinSources; 047 import org.kuali.kfs.sys.KFSConstants.DepositConstants; 048 import org.kuali.kfs.sys.KFSConstants.DocumentStatusCodes; 049 import org.kuali.kfs.sys.KFSConstants.DocumentStatusCodes.CashReceipt; 050 import org.kuali.kfs.sys.businessobject.Bank; 051 import org.kuali.kfs.sys.businessobject.FinancialSystemDocumentHeader; 052 import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntry; 053 import org.kuali.kfs.sys.context.SpringContext; 054 import org.kuali.rice.kew.exception.WorkflowException; 055 import org.kuali.rice.kim.bo.Person; 056 import org.kuali.rice.kns.document.authorization.DocumentAuthorizer; 057 import org.kuali.rice.kns.exception.InfrastructureException; 058 import org.kuali.rice.kns.service.BusinessObjectService; 059 import org.kuali.rice.kns.service.DataDictionaryService; 060 import org.kuali.rice.kns.service.DateTimeService; 061 import org.kuali.rice.kns.service.DocumentHelperService; 062 import org.kuali.rice.kns.service.DocumentService; 063 import org.kuali.rice.kns.util.GlobalVariables; 064 import org.kuali.rice.kns.util.KualiDecimal; 065 import org.kuali.rice.kns.util.ObjectUtils; 066 import org.kuali.rice.kns.workflow.service.KualiWorkflowDocument; 067 import org.springframework.transaction.annotation.Transactional; 068 069 /** 070 * This is the default implementation of the CashManagementService interface. 071 */ 072 @Transactional 073 public class CashManagementServiceImpl implements CashManagementService { 074 private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(CashManagementServiceImpl.class); 075 076 private BusinessObjectService businessObjectService; 077 private CashDrawerService cashDrawerService; 078 private DateTimeService dateTimeService; 079 private DocumentService documentService; 080 private CashManagementDao cashManagementDao; 081 private DataDictionaryService dataDictionaryService; 082 083 /** 084 * If a CMD is found that is associated with the CR document, then that CMD is returned; otherwise null is returned. 085 * Currently the relationships are: 086 * <ul> 087 * <li>(CashReceipt to CashReceiptHeader) is (1 to 1)</li> 088 * <li>(CashReceiptHeader to DepositCashReceiptControl) is (1 to 1)</li> 089 * <li>(DepositCashReceiptControl to Deposit) is (many to 1)</li> 090 * <li>(Deposit to CashManagementDocument) is (many to 1)</li> 091 * </ul> 092 * 093 * @param documentId The id of the cash receipt document linked to the cash management document. 094 * @return An instance of a CashManagementDocument matching the provided search criteria, or null if no value is found. 095 * 096 * @see org.kuali.kfs.fp.document.service.CashManagementService#getCashManagementDocumentForCashReceiptId(java.lang.String) 097 */ 098 public CashManagementDocument getCashManagementDocumentForCashReceiptId(String documentId) { 099 CashManagementDocument cmdoc = null; 100 101 // get CashReceiptHeader for the CashReceipt, if any 102 HashMap primaryKeys = new HashMap(); 103 primaryKeys.put(KFSPropertyConstants.DOCUMENT_NUMBER, documentId); 104 CashReceiptDocument crDoc = (CashReceiptDocument) businessObjectService.findByPrimaryKey(getDataDictionaryService().getDocumentClassByTypeName(KFSConstants.FinancialDocumentTypeCodes.CASH_RECEIPT), primaryKeys); 105 106 // get the DepositCashReceiptControl for the CashReceiptHeader 107 if (crDoc != null) { 108 List crcList = crDoc.getDepositCashReceiptControl(); 109 if (!crcList.isEmpty()) { 110 DepositCashReceiptControl dpcrc = (DepositCashReceiptControl) crcList.get(0); 111 112 // get the Deposit and follow it to the CashManagementDocument 113 Deposit d = (Deposit) dpcrc.getDeposit(); 114 cmdoc = d.getCashManagementDocument(); 115 } 116 } 117 118 return cmdoc; 119 } 120 121 122 /** 123 * This method creates a new cash management document and sets the provided values as attributes to the document. 124 * The steps followed to create a new cash management document are as follows: 125 * <ul> 126 * <li>Find the drawer for the campus code given.</li> 127 * <li>Make sure the drawer is closed, force the drawer closed if it is not already closed.</li> 128 * <li>Create the cash management document, set the provided values to the document and link it to the cash drawer</li> 129 * </ul> 130 * 131 * If the campusCode or docDescription values are null, an IllegalArgumentException will be thrown. 132 * 133 * TODO - annotation is not used or set at all in this method, remove it if appropriate. 134 * 135 * @param campusCode The campus code of the cash drawer. 136 * @param docDescription The document description to be set on the new cash management document. 137 * @param annotation 138 * @return A new instance of a CashManagementDocument (not persisted). 139 * 140 * @see org.kuali.kfs.fp.document.service.CashManagementService#createCashManagementDocument(java.lang.String, 141 * java.lang.String, java.lang.String) 142 */ 143 public CashManagementDocument createCashManagementDocument(String campusCode, String docDescription, String annotation) { 144 if (StringUtils.isBlank(campusCode)) { 145 throw new IllegalArgumentException("invalid (blank) campus code"); 146 } 147 if (StringUtils.isBlank(docDescription)) { 148 throw new IllegalArgumentException("invalid (blank) docDescription"); 149 } 150 151 // check user authorization 152 Person user = GlobalVariables.getUserSession().getPerson(); 153 DocumentAuthorizer documentAuthorizer = SpringContext.getBean(DocumentHelperService.class).getDocumentAuthorizer(KFSConstants.FinancialDocumentTypeCodes.CASH_MANAGEMENT); 154 documentAuthorizer.canInitiate(KFSConstants.FinancialDocumentTypeCodes.CASH_MANAGEMENT, user); 155 156 // check cash drawer 157 CashDrawer cd = cashDrawerService.getByCampusCode(campusCode); 158 if (cd == null) { 159 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); 160 } 161 String controllingDocId = cd.getReferenceFinancialDocumentNumber(); 162 163 // KULEDOCS-1475: adding handling for two things which should never happen: 164 // 1. CashDrawer is open or locked by document 'null' 165 // 2. CashDrawer is open or locked by a document which doesn't exist 166 if (!cd.isClosed() || cd.getStatusCode() == null) { 167 boolean forceDrawerClosed = false; 168 169 if (cd.getStatusCode() == null) { 170 forceDrawerClosed = true; 171 } 172 173 if (StringUtils.isBlank(controllingDocId)) { 174 forceDrawerClosed = true; 175 } 176 else if (!documentService.documentExists(controllingDocId)) { 177 forceDrawerClosed = true; 178 } 179 180 if (forceDrawerClosed) { 181 cashDrawerService.closeCashDrawer(cd); 182 cd = cashDrawerService.getByCampusCode(campusCode); 183 if (cd == null) { 184 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); 185 } 186 } 187 } 188 189 190 CashManagementDocument cmDoc = null; 191 if (cd.isClosed()) { 192 // create the document 193 try { 194 cmDoc = (CashManagementDocument) documentService.getNewDocument(CashManagementDocument.class); 195 cmDoc.getDocumentHeader().setDocumentDescription(docDescription); 196 cmDoc.setCampusCode(campusCode); 197 cmDoc.setCashDrawer(cd); 198 cmDoc.getCurrentTransaction().setCampusCode(cmDoc.getCampusCode()); 199 cmDoc.getCurrentTransaction().setReferenceFinancialDocumentNumber(cmDoc.getDocumentNumber()); 200 cmDoc.getCurrentTransaction().setOpenItemsInProcess(getOpenItemsInProcess(cmDoc)); 201 } 202 catch (WorkflowException e) { 203 throw new InfrastructureException("unable to create CashManagementDocument", e); 204 } 205 } 206 else { 207 CashDrawerStatusCodeFormatter f = new CashDrawerStatusCodeFormatter(); 208 209 throw new CashDrawerStateException(campusCode, controllingDocId, (String) f.format(CashDrawerConstants.STATUS_CLOSED), (String) f.format(cd.getStatusCode())); 210 } 211 212 return cmDoc; 213 } 214 215 /** 216 * This method creates new cumulative currency and coin details for the document given. 217 * 218 * @param cmDoc The cash management document the cumulative details will be associated with. 219 * @param cashieringSource The cashiering record source for the new details. 220 */ 221 public void createNewCashDetails(CashManagementDocument cmDoc, String cashieringSource) { 222 CoinDetail coinDetail = new CoinDetail(); 223 coinDetail.setDocumentNumber(cmDoc.getDocumentNumber()); 224 coinDetail.setFinancialDocumentTypeCode(CashieringTransaction.DETAIL_DOCUMENT_TYPE); 225 coinDetail.setCashieringRecordSource(cashieringSource); 226 businessObjectService.save(coinDetail); 227 228 CurrencyDetail currencyDetail = new CurrencyDetail(); 229 currencyDetail.setDocumentNumber(cmDoc.getDocumentNumber()); 230 currencyDetail.setFinancialDocumentTypeCode(CashieringTransaction.DETAIL_DOCUMENT_TYPE); 231 currencyDetail.setCashieringRecordSource(cashieringSource); 232 businessObjectService.save(currencyDetail); 233 } 234 235 /** 236 * This method adds a new deposit to a the given CashManagementDocument. 237 * <br/> 238 * The following steps go into adding a deposit to a cash management document. 239 * <ul> 240 * <li>The given deposit parameters are validated. 241 * <li>The corresponding cash drawer is locked 242 * <li>The given cashiering check records are turned into check records 243 * <li>The new deposit is created 244 * <li>The deposit is added to the cash management document and persisted 245 * <li>The list of cash receipts are associated with the new deposit 246 * <li>The deposit is saved again to ensure all links and attributes of the deposit are set appropriately and persisted 247 * <li>The drawer is unlocked 248 * <ul> 249 * 250 * @param cashManagementDoc The document to have the deposit added to. 251 * @param depositTicketNumber The ticket number of the deposit being added. 252 * @param bankAccount The bank account on the deposit. 253 * @param selectedCashReceipts The collection of cash receipts associated with the new deposit. 254 * @param selectedCashieringChecks The collection of checks associated with the new deposit. 255 * @param isFinalDeposit A flag used to identify if a deposit is the final deposit to be added to a cash management document. 256 * 257 * @see org.kuali.kfs.fp.document.service.CashManagementService#addInterimDeposit(org.kuali.kfs.fp.document.CashManagementDocument, 258 * java.lang.String, org.kuali.kfs.fp.businessobject.BankAccount, java.util.List) 259 */ 260 @SuppressWarnings("deprecation") 261 public void addDeposit(CashManagementDocument cashManagementDoc, String depositTicketNumber, Bank bank, List selectedCashReceipts, List selectedCashieringChecks, boolean isFinalDeposit) { 262 validateDepositParams(cashManagementDoc, bank, selectedCashReceipts); 263 264 String depositTypeCode = DepositConstants.DEPOSIT_TYPE_INTERIM; 265 if (isFinalDeposit) { 266 depositTypeCode = DepositConstants.DEPOSIT_TYPE_FINAL; 267 } 268 269 // lock the cashDrawer 270 cashDrawerService.lockCashDrawer(cashManagementDoc.getCashDrawer(), cashManagementDoc.getDocumentNumber()); 271 272 // turn the list of selected check sequence ids into a list of actual check records 273 Map<Integer, Check> checks = getUndepositedChecksAsMap(cashManagementDoc); 274 List<Check> checksToSave = new ArrayList<Check>(); 275 if (selectedCashieringChecks != null) { 276 for (Object o: selectedCashieringChecks) { 277 Integer sequenceId = (Integer)o; 278 Check check = checks.get(sequenceId); 279 checksToSave.add(check); 280 } 281 } 282 283 // create the Deposit 284 Deposit deposit = buildDeposit(cashManagementDoc, depositTypeCode, depositTicketNumber, bank, selectedCashReceipts, checksToSave); 285 286 // attach the deposit to the document 287 List deposits = cashManagementDoc.getDeposits(); 288 deposits.add(deposit); 289 documentService.updateDocument(cashManagementDoc); 290 291 // associate the CashReceipts with the deposit 292 List dccList = new ArrayList(); 293 for (Iterator i = selectedCashReceipts.iterator(); i.hasNext();) { 294 CashReceiptDocument crDoc = (CashReceiptDocument) i.next(); 295 FinancialSystemDocumentHeader dh = crDoc.getDocumentHeader(); 296 297 String statusCode = isFinalDeposit ? DocumentStatusCodes.CashReceipt.FINAL : DocumentStatusCodes.CashReceipt.INTERIM; 298 dh.setFinancialDocumentStatusCode(statusCode); 299 documentService.updateDocument(crDoc); 300 301 DepositCashReceiptControl dcc = new DepositCashReceiptControl(); 302 dcc.setFinancialDocumentCashReceiptNumber(crDoc.getDocumentNumber()); 303 dcc.setFinancialDocumentDepositNumber(deposit.getDocumentNumber()); 304 dcc.setFinancialDocumentDepositLineNumber(deposit.getFinancialDocumentDepositLineNumber()); 305 306 dcc.setCashReceiptDocument(crDoc); 307 dcc.setDeposit(deposit); 308 309 dccList.add(dcc); 310 } 311 // crHeaders get saved as side-effect of saving dccs 312 businessObjectService.save(dccList); 313 314 // make sure all checks have the right deposit line number 315 for (Check check: checksToSave) { 316 check.setFinancialDocumentDepositLineNumber(deposit.getFinancialDocumentDepositLineNumber()); 317 } 318 businessObjectService.save(checksToSave); 319 320 // unlock the cashDrawer, if needed 321 if (!isFinalDeposit) { 322 cashDrawerService.unlockCashDrawer(cashManagementDoc.getCashDrawer(), cashManagementDoc.getDocumentNumber()); 323 } 324 } 325 326 /** 327 * Validates the given Deposit parameters, throwing various (runtime) exceptions if errors exist. 328 * 329 * @param cashManagementDoc The document the deposit will be added to. 330 * @param bank The bank account of the deposit being added. 331 * @param selectedCashReceipts The collection of cash receipts associated with the new deposit. 332 */ 333 protected void validateDepositParams(CashManagementDocument cashManagementDoc, Bank bank, List<CashReceiptDocument> selectedCashReceipts) { 334 if (cashManagementDoc == null) { 335 throw new IllegalArgumentException("invalid (null) cashManagementDoc"); 336 } 337 else if (!cashManagementDoc.getDocumentHeader().getWorkflowDocument().stateIsSaved()) { 338 throw new IllegalStateException("cashManagementDoc '" + cashManagementDoc.getDocumentNumber() + "' is not in 'saved' state"); 339 } 340 else if (cashManagementDoc.hasFinalDeposit()) { 341 throw new IllegalStateException("cashManagementDoc '" + cashManagementDoc.getDocumentNumber() + "' hasFinalDeposit"); 342 } 343 if (bank == null) { 344 throw new IllegalArgumentException("invalid (null) bank"); 345 } 346 347 if (selectedCashReceipts == null) { 348 throw new IllegalArgumentException("invalid (null) cashReceipts list"); 349 } 350 else { 351 for (CashReceiptDocument cashReceipt : selectedCashReceipts) { 352 String statusCode = cashReceipt.getDocumentHeader().getFinancialDocumentStatusCode(); 353 if (!StringUtils.equals(statusCode, DocumentStatusCodes.CashReceipt.VERIFIED)) { 354 throw new InvalidCashReceiptState("cash receipt document " + cashReceipt.getDocumentNumber() + " has a status other than 'verified' "); 355 } 356 } 357 } 358 } 359 360 /** 361 * 362 * This method builds a new deposit object from the parameters provided. 363 * 364 * @param cashManagementDoc The cash management document the deposit will be added to. 365 * @param depositTypeCode The type code associated with the deposit. 366 * @param depositTicketNumber The deposit ticket number to be set on the deposit object. 367 * @param bank The bank account of the deposit. 368 * @param selectedCashReceipts The cash receipts that make up the deposit. 369 * @param selectedCashieringChecks The cashiering checks that make up the deposit. 370 * @return A new instance of a deposit generated from all the parameters provided. 371 */ 372 protected Deposit buildDeposit(CashManagementDocument cashManagementDoc, String depositTypeCode, String depositTicketNumber, Bank bank, List<CashReceiptDocument> selectedCashReceipts, List selectedCashieringChecks) { 373 Deposit deposit = new Deposit(); 374 deposit.setDocumentNumber(cashManagementDoc.getDocumentNumber()); 375 deposit.setCashManagementDocument(cashManagementDoc); 376 377 deposit.setDepositTypeCode(depositTypeCode); 378 379 deposit.setDepositDate(dateTimeService.getCurrentSqlDate()); 380 381 deposit.setBank(bank); 382 deposit.setDepositBankCode(bank.getBankCode()); 383 384 // derive the line number 385 int lineNumber = cashManagementDoc.getNextDepositLineNumber(); 386 deposit.setFinancialDocumentDepositLineNumber(new Integer(lineNumber)); 387 388 // trim depositTicketNumber to empty, because the field is optional 389 deposit.setDepositTicketNumber(StringUtils.trimToEmpty(depositTicketNumber)); 390 391 // total up the cash receipts 392 KualiDecimal total = KualiDecimal.ZERO; 393 for (Iterator i = selectedCashReceipts.iterator(); i.hasNext();) { 394 CashReceiptDocument crDoc = (CashReceiptDocument) i.next(); 395 total = total.add(crDoc.getTotalCheckAmount()); 396 } 397 Check currCheck; 398 for (Object checkObj: selectedCashieringChecks) { 399 currCheck = (Check)checkObj; 400 total = total.add(currCheck.getAmount()); 401 } 402 deposit.setDepositAmount(total); 403 404 return deposit; 405 } 406 407 /** 408 * This method returns all undeposited checks as a map with the key of each value in the map equal to the sequence id 409 * of the corresponding check. 410 * 411 * @param cmDoc The cash management doc to find undeposited checks for. 412 * @return A map of checks keyed on sequence id. 413 */ 414 protected Map<Integer, Check> getUndepositedChecksAsMap(CashManagementDocument cmDoc) { 415 Map<Integer, Check> checks = new HashMap<Integer, Check>(); 416 List<Check> checkList = cashManagementDao.selectUndepositedCashieringChecks(cmDoc.getDocumentNumber()); 417 if (checkList != null && checkList.size() > 0) { 418 for (Check check: checkList) { 419 checks.put(check.getSequenceId(), check); 420 } 421 } 422 return checks; 423 } 424 425 /** 426 * This method cancels a cash management document, effectively nullifying all values and attributes associated with 427 * the document. Canceling a CashManagementDocument results in the following: 428 * <ul> 429 * <li>Cancels (deletes) all deposits associated with the document.</li> 430 * <li>Recloses the drawer</li> 431 * <li>Remove all currency and coin records generated by the document.</li> 432 * </ul> 433 * <br> 434 * NOTE: Method should only be called after the appropriate CashManagementDocumentRule has been successfully passed. 435 * 436 * @param cmDoc The CashManagementDocument to be canceled. 437 * 438 * @see org.kuali.kfs.fp.document.service.CashManagementService#cancelCashManagementDocument(org.kuali.kfs.fp.document.CashManagementDocument) 439 */ 440 public void cancelCashManagementDocument(CashManagementDocument cmDoc) { 441 if (cmDoc == null) { 442 throw new IllegalArgumentException("invalid (null) CashManagementDocument"); 443 } 444 445 // cancel each deposit (which also deletes the records connecting the Deposit to a CashManagementDoc 446 List deposits = cmDoc.getDeposits(); 447 for (Iterator i = deposits.iterator(); i.hasNext();) { 448 Deposit deposit = (Deposit) i.next(); 449 450 cancelDeposit(deposit); 451 } 452 453 // reclose the cashDrawer 454 String unitName = cmDoc.getCampusCode(); 455 cashDrawerService.closeCashDrawer(cmDoc.getCashDrawer()); 456 457 // cleanup the CMDoc, but let the postprocessor itself save it 458 cmDoc.setDeposits(new ArrayList()); 459 cmDoc.getDocumentHeader().setFinancialDocumentStatusCode(DocumentStatusCodes.CANCELLED); 460 461 // kill off cumulative currency/coin detail records for this document (canceling the deposits kills the deposit records) 462 String[] cashieringSourcesToDelete = { KFSConstants.CurrencyCoinSources.CASH_RECEIPTS, CashieringTransaction.DETAIL_DOCUMENT_TYPE, KFSConstants.CurrencyCoinSources.CASH_MANAGEMENT_IN, KFSConstants.CurrencyCoinSources.CASH_MANAGEMENT_OUT }; 463 for (String cashieringSourceToDelete : cashieringSourcesToDelete) { 464 CurrencyDetail currencyDetail = cashManagementDao.findCurrencyDetailByCashieringRecordSource(cmDoc.getDocumentNumber(), CashieringTransaction.DETAIL_DOCUMENT_TYPE, cashieringSourceToDelete); 465 if (currencyDetail != null) { 466 businessObjectService.delete(currencyDetail); 467 } 468 CoinDetail coinDetail = cashManagementDao.findCoinDetailByCashieringRecordSource(cmDoc.getDocumentNumber(), CashieringTransaction.DETAIL_DOCUMENT_TYPE, cashieringSourceToDelete); 469 if (coinDetail != null) { 470 businessObjectService.delete(coinDetail); 471 } 472 } 473 } 474 475 476 /** 477 * This method cancels a given deposit. This equates to the following: 478 * <ul> 479 * <li>Resetting all associated CashReceipts to a state of VERIFIED.</li> 480 * <li>Update all associated cashiering checks to a be un-deposited.</li> 481 * <li>Unlock the cash drawer if needed.</li> 482 * <li>Delete the deposit.</li> 483 * </ul> 484 * 485 * @see org.kuali.kfs.fp.document.service.CashManagementService#cancelDeposit(org.kuali.kfs.fp.businessobject.Deposit) 486 */ 487 public void cancelDeposit(Deposit deposit) { 488 if (deposit == null) { 489 throw new IllegalArgumentException("invalid (null) deposit"); 490 } 491 492 // reload it, to forestall OptimisticLockExceptions 493 deposit.refresh(); 494 495 // save campus name, for possible later use 496 String depositCampus = deposit.getCashManagementDocument().getCampusCode(); 497 498 // update every CashReceipt associated with this Deposit 499 List depositCashReceiptControls = deposit.getDepositCashReceiptControl(); 500 for (Iterator j = depositCashReceiptControls.iterator(); j.hasNext();) { 501 DepositCashReceiptControl dcc = (DepositCashReceiptControl) j.next(); 502 if (!ObjectUtils.isNull(dcc)) { 503 dcc.refreshReferenceObject("cashReceiptDocument"); 504 CashReceiptDocument crDoc = dcc.getCashReceiptDocument(); 505 if (!ObjectUtils.isNull(crDoc)) { 506 crDoc.refreshReferenceObject("documentHeader"); 507 FinancialSystemDocumentHeader crdh = crDoc.getDocumentHeader(); 508 if (!ObjectUtils.isNull(crdh)) { 509 crdh.setFinancialDocumentStatusCode(DocumentStatusCodes.CashReceipt.VERIFIED); 510 documentService.updateDocument(crDoc); 511 } 512 } 513 } 514 } 515 516 // un-deposit all cashiering checks associated with the deposit 517 List<Check> depositedChecks = selectCashieringChecksForDeposit(deposit.getDocumentNumber(), deposit.getFinancialDocumentDepositLineNumber()); 518 for (Check check: depositedChecks) { 519 check.setFinancialDocumentDepositLineNumber(null); 520 } 521 businessObjectService.save(depositedChecks); 522 523 // unlock the cashDrawer, if needed 524 if (LOG.isDebugEnabled()) { 525 LOG.debug("deposit deposit type = "+deposit.getDepositTypeCode()+"; constant = "+KFSConstants.DepositConstants.DEPOSIT_TYPE_FINAL+"; are they equal? "+deposit.getDepositTypeCode().equals(KFSConstants.DepositConstants.DEPOSIT_TYPE_FINAL)); 526 } 527 if (deposit.getDepositTypeCode().equals(KFSConstants.DepositConstants.DEPOSIT_TYPE_FINAL)) { 528 CashDrawer drawer = cashDrawerService.getByCampusCode(deposit.getCashManagementDocument().getCampusCode()); 529 CurrencyDetail currencyDetail = cashManagementDao.findCurrencyDetailByCashieringRecordSource(deposit.getCashManagementDocument().getDocumentNumber(), CashieringTransaction.DETAIL_DOCUMENT_TYPE, KFSConstants.CurrencyCoinSources.DEPOSITS); 530 if (currencyDetail != null) { 531 drawer.addCurrency(currencyDetail); 532 businessObjectService.delete(currencyDetail); 533 } 534 CoinDetail coinDetail = cashManagementDao.findCoinDetailByCashieringRecordSource(deposit.getCashManagementDocument().getDocumentNumber(), CashieringTransaction.DETAIL_DOCUMENT_TYPE, KFSConstants.CurrencyCoinSources.DEPOSITS); 535 if (coinDetail != null) { 536 drawer.addCoin(coinDetail); 537 businessObjectService.delete(coinDetail); 538 } 539 businessObjectService.save(drawer); 540 cashDrawerService.unlockCashDrawer(drawer, deposit.getDocumentNumber()); 541 } 542 543 // delete the Deposit from the database 544 businessObjectService.delete(deposit); 545 } 546 547 /** 548 * This method performs the necessary steps to finalize a cash management document. These steps include: 549 * <ul> 550 * <li>Finalize all associated cash receipts. 551 * <li>Generate the master currency and coin details and persist them. 552 * <li>Update the CashManagementDocument status to APPROVED. 553 * </ul> 554 * 555 * <br> 556 * NOTE: Method should only be called after the appropriate CashManagementDocumentRule has been successfully passed 557 * 558 * @param cmDoc The CashManagementDocument to be finalized. 559 * 560 * @see org.kuali.kfs.fp.document.service.CashManagementService#finalizeCashManagementDocument(org.kuali.kfs.fp.document.CashManagementDocument) 561 */ 562 public void finalizeCashManagementDocument(CashManagementDocument cmDoc) { 563 if (cmDoc == null) { 564 throw new IllegalArgumentException("invalid (null) CashManagementDocument"); 565 } 566 if (!cmDoc.hasFinalDeposit()) { 567 throw new IllegalStateException("cmDoc " + cmDoc.getDocumentNumber() + " is missing a FinalDeposit"); 568 } 569 570 String campusCode = cmDoc.getCampusCode(); 571 cashDrawerService.closeCashDrawer(campusCode); 572 CashDrawer cd = cashDrawerService.getByCampusCode(campusCode); 573 574 575 // finalize the CashReceipts 576 List<Deposit> deposits = cmDoc.getDeposits(); 577 for (Deposit deposit : deposits) { 578 List<CashReceiptDocument> receipts = retrieveCashReceipts(deposit); 579 for (CashReceiptDocument receipt : receipts) { 580 // marks GLPEs of CRs as APPROVED 581 for (GeneralLedgerPendingEntry glpe : receipt.getGeneralLedgerPendingEntries()) { 582 glpe.setFinancialDocumentApprovedCode(DocumentStatusCodes.APPROVED); 583 } 584 585 // mark CRs themselves as APPROVED 586 receipt.getDocumentHeader().setFinancialDocumentStatusCode(DocumentStatusCodes.APPROVED); 587 588 // persist 589 documentService.updateDocument(receipt); 590 } 591 } 592 593 // generate the master currency and coin details; save those 594 CurrencyDetail masterCurrencyDetail = this.generateMasterCurrencyDetail(cmDoc); 595 businessObjectService.save(masterCurrencyDetail); 596 CoinDetail masterCoinDetail = this.generateMasterCoinDetail(cmDoc); 597 businessObjectService.save(masterCoinDetail); 598 599 // finalize the CMDoc, but let the postprocessor save it 600 cmDoc.getDocumentHeader().setFinancialDocumentStatusCode(DocumentStatusCodes.APPROVED); 601 } 602 603 /** 604 * This method verifies that all cash receipts for the document are deposited. 605 * 606 * @param cmDoc The cash management document to verify. 607 * @return True if all CashReceipts are deposited, false otherwise. 608 */ 609 public boolean allVerifiedCashReceiptsAreDeposited(CashManagementDocument cmDoc) { 610 boolean result = true; 611 List verifiedReceipts = SpringContext.getBean(CashReceiptService.class).getCashReceipts(cmDoc.getCampusCode(), KFSConstants.DocumentStatusCodes.CashReceipt.VERIFIED); 612 for (Object o: verifiedReceipts) { 613 if (!verifyCashReceiptIsDeposited(cmDoc, (CashReceiptDocument)o)) { 614 result = false; 615 break; 616 } 617 } 618 return result; 619 } 620 621 /** 622 * This method returns a collection of cash receipts associated with the deposit given. 623 * 624 * @param deposit The deposit to retrieve all the cash receipts for. 625 * @return A collection of cash receipts associated with the deposit given. 626 * 627 * @see org.kuali.kfs.fp.document.service.CashManagementService#retrieveCashReceipts(org.kuali.kfs.fp.businessobject.Deposit) 628 */ 629 public List<CashReceiptDocument> retrieveCashReceipts(Deposit deposit) { 630 List<CashReceiptDocument> cashReceiptDocuments = new ArrayList<CashReceiptDocument>(); 631 632 if (ObjectUtils.isNull(deposit.getDepositCashReceiptControl())) { 633 deposit.refreshReferenceObject("depositCashReceiptControl"); 634 } 635 636 Map pkMap = new HashMap(); 637 for (Object dcrcAsObject : deposit.getDepositCashReceiptControl()) { 638 final DepositCashReceiptControl dcrc = (DepositCashReceiptControl)dcrcAsObject; 639 try { 640 CashReceiptDocument crDoc = (CashReceiptDocument)documentService.getByDocumentHeaderId(dcrc.getFinancialDocumentCashReceiptNumber()); 641 final KualiWorkflowDocument headerWorkflowDoc = crDoc.getDocumentHeader().getWorkflowDocument(); 642 crDoc.refreshReferenceObject("documentHeader"); 643 crDoc.getDocumentHeader().setWorkflowDocument(headerWorkflowDoc); 644 cashReceiptDocuments.add(crDoc); 645 } 646 catch (WorkflowException we) { 647 throw new RuntimeException("Could not retrieve related Cash Receipts", we); 648 } 649 } 650 651 return cashReceiptDocuments; 652 } 653 654 /** 655 * Verifies if a given cash receipt is deposited as part of the given cash management document. 656 * 657 * @param cmDoc The cash management document to search through. 658 * @param crDoc The cash receipt to check the deposited status of. 659 * @return True if the given cash receipt document is deposited as part of the given cash management document, false otherwise. 660 */ 661 public boolean verifyCashReceiptIsDeposited(CashManagementDocument cmDoc, CashReceiptDocument crDoc) { 662 boolean thisCRDeposited = false; 663 for (Deposit deposit: cmDoc.getDeposits()) { 664 if (deposit.containsCashReceipt(crDoc)) { 665 thisCRDeposited = true; 666 break; 667 } 668 } 669 return thisCRDeposited; 670 } 671 672 /** 673 * This method turns the last interim deposit into the final deposit and locks the cash drawer. A deposit is turned into 674 * a final deposit by updating the deposit type code. 675 * <br> 676 * NOTE: This method throws an IllegalStateException if a final deposit already exists for this document or if there 677 * are any undeposited cash receipts. 678 * 679 * @param cmDoc The cash management document to take deposits from for finalization. 680 */ 681 public void finalizeLastInterimDeposit(CashManagementDocument cmDoc) { 682 // if there's already a final deposit, throw an IllegalStateException 683 if (cmDoc.hasFinalDeposit()) { 684 throw new IllegalStateException("CashManagementDocument #"+cmDoc.getDocumentNumber()+" already has a final deposit"); 685 } 686 // if there are still verified un-deposited cash receipts, throw an IllegalStateException 687 List verifiedReceipts = SpringContext.getBean(CashReceiptService.class).getCashReceipts(cmDoc.getCampusCode(), KFSConstants.DocumentStatusCodes.CashReceipt.VERIFIED); 688 for (Object o: verifiedReceipts) { 689 CashReceiptDocument crDoc = (CashReceiptDocument)o; 690 if (!verifyCashReceiptIsDeposited(cmDoc, crDoc)) { 691 throw new IllegalStateException("Verified Cash Receipt Document #"+crDoc.getDocumentNumber()+" must be deposited for this to be a final deposit"); 692 } 693 } 694 // lock the cash drawer 695 cashDrawerService.lockCashDrawer(cmDoc.getCashDrawer(), cmDoc.getDocumentNumber()); 696 697 // change the deposit type code for the last deposit 698 List<Deposit> allDeposits = cmDoc.getDeposits(); 699 Deposit lastInterim = allDeposits.get(allDeposits.size() - 1); 700 lastInterim.setDepositTypeCode(DepositConstants.DEPOSIT_TYPE_FINAL); 701 finalizeCashReceiptsForDeposit(lastInterim); 702 documentService.updateDocument(cmDoc); 703 } 704 705 /** 706 * This method switches cash receipts to "final" status as opposed to "interim" status. 707 * 708 * @param deposit The deposit the cash receipts are associated with. 709 */ 710 protected void finalizeCashReceiptsForDeposit(Deposit deposit) { 711 List cashReceipts = this.retrieveCashReceipts(deposit); 712 for (Object o: cashReceipts) { 713 CashReceiptDocument crDoc = (CashReceiptDocument)o; 714 crDoc.getDocumentHeader().setFinancialDocumentStatusCode(KFSConstants.DocumentStatusCodes.CashReceipt.FINAL); 715 documentService.updateDocument(crDoc); 716 } 717 } 718 719 /** 720 * This method applies the cashiering transaction to the given CashManagementDocument. This is accomplished by 721 * retrieving a CashieringTransactionRule object and running the appropriate methods to process the cashiering 722 * application rules. 723 * 724 * @see org.kuali.kfs.fp.document.service.CashManagementService#applyCashieringTransaction(org.kuali.kfs.fp.document.CashManagementDocument, org.kuali.kfs.fp.businessobject.CashieringTransaction) 725 * @see org.kuali.kfs.fp.document.validation.impl.CashieringTransactionRule#processCashieringTransactionApplicationRules(CashManagementDocument) 726 */ 727 public void applyCashieringTransaction(CashManagementDocument cmDoc) { 728 if (cmDoc.getCashDrawer() == null) { 729 cmDoc.setCashDrawer(cashDrawerService.getByCampusCode(cmDoc.getCampusCode())); 730 } 731 732 this.transferChecksToCashManagementDocument(cmDoc, cmDoc.getCurrentTransaction()); 733 this.saveChecks(cmDoc); 734 this.completeNewItemInProcess(cmDoc.getCurrentTransaction()); 735 if (cmDoc.getCurrentTransaction().getNewItemInProcess() != null) { 736 this.saveNewItemInProcess(cmDoc, cmDoc.getCurrentTransaction()); 737 } 738 this.saveExisingItemsInProcess(cmDoc, cmDoc.getCurrentTransaction()); 739 this.saveMoneyInCash(cmDoc, cmDoc.getCurrentTransaction()); 740 this.saveMoneyOutCash(cmDoc, cmDoc.getCurrentTransaction()); 741 this.updateCashDrawer(cmDoc.getCashDrawer(), cmDoc.getCurrentTransaction()); 742 cmDoc.resetCurrentTransaction(); 743 } 744 745 /** 746 * This method puts money from the "money in" portion of the transaction into the cash drawer, and takes money from the 747 * "money out" portion of the cash drawer out. 748 * 749 * @param drawer The cash drawer to operate on. 750 * @param trans The transaction that is the operation. 751 */ 752 protected void updateCashDrawer(CashDrawer drawer, CashieringTransaction trans) { 753 // add money in to cash drawer 754 if (!trans.getMoneyInCurrency().isEmpty()) { 755 drawer.addCurrency(trans.getMoneyInCurrency()); 756 } 757 if (!trans.getMoneyInCoin().isEmpty()) { 758 drawer.addCoin(trans.getMoneyInCoin()); 759 } 760 761 // subtract money out from cash drawer 762 if (!trans.getMoneyOutCurrency().isEmpty()) { 763 drawer.removeCurrency(trans.getMoneyOutCurrency()); 764 } 765 if (!trans.getMoneyOutCoin().isEmpty()) { 766 drawer.removeCoin(trans.getMoneyOutCoin()); 767 } 768 769 businessObjectService.save(drawer); 770 } 771 772 /** 773 * 774 * This method completes the new item in process by setting the item remaining amount equal to the item amount. 775 * 776 * @param trans The transaction being performed. 777 */ 778 protected void completeNewItemInProcess(CashieringTransaction trans) { 779 if (trans.getNewItemInProcess().isPopulated()) { 780 trans.getNewItemInProcess().setItemRemainingAmount(trans.getNewItemInProcess().getItemAmount()); 781 } 782 else { 783 trans.setNewItemInProcess(null); // we don't want to save it or deal with it 784 } 785 } 786 787 /** 788 * 789 * This method retrieves all the checks for the given document and persists them. 790 * 791 * @param cmDoc The cash management document the checks will be saved against. 792 */ 793 protected void saveChecks(CashManagementDocument cmDoc) { 794 if (cmDoc.getChecks() != null) { 795 for (Check check: cmDoc.getChecks()) { 796 check.setDocumentNumber(cmDoc.getDocumentNumber()); 797 check.setFinancialDocumentTypeCode(CashieringTransaction.DETAIL_DOCUMENT_TYPE); 798 check.setCashieringRecordSource(KFSConstants.CheckSources.CASH_MANAGEMENT); 799 businessObjectService.save(check); 800 } 801 } 802 } 803 804 /** 805 * 806 * This method retrieves the checks from the transaction and adds them to the cash management document. 807 * 808 * @param cmDoc The document the checks will be transferred to. 809 * @param trans The transaction the checks are associated with. 810 */ 811 protected void transferChecksToCashManagementDocument(CashManagementDocument cmDoc, CashieringTransaction trans) { 812 for (Check check: trans.getMoneyInChecks()) { 813 check.setFinancialDocumentTypeCode(CashieringTransaction.DETAIL_DOCUMENT_TYPE); 814 check.setCashieringRecordSource(KFSConstants.CheckSources.CASH_MANAGEMENT); 815 check.setDocumentNumber(cmDoc.getDocumentNumber()); 816 cmDoc.addCheck(check); 817 } 818 } 819 820 /** 821 * This methods checks if data was actually entered for the new item in process; if so, it saves that item in process. 822 * 823 * @param cmDoc The cash management doc that the new item in process will be associated with. 824 * @param trans The cashiering transaction that created the new item in process. 825 */ 826 protected void saveNewItemInProcess(CashManagementDocument cmDoc, CashieringTransaction trans) { 827 if (trans.getNewItemInProcess().isPopulated()) { 828 trans.getNewItemInProcess().setItemRemainingAmount(trans.getNewItemInProcess().getItemAmount()); 829 trans.getNewItemInProcess().setItemReducedAmount(KualiDecimal.ZERO); 830 trans.getNewItemInProcess().setCampusCode(cmDoc.getCampusCode()); 831 businessObjectService.save(trans.getNewItemInProcess()); 832 833 // put it in the list of open items in process 834 trans.getOpenItemsInProcess().add(trans.getNewItemInProcess()); 835 836 CashDrawer drawer = cmDoc.getCashDrawer(); 837 if (drawer.getFinancialDocumentMiscellaneousAdvanceAmount() == null) { 838 drawer.setFinancialDocumentMiscellaneousAdvanceAmount(trans.getNewItemInProcess().getItemAmount()); 839 } 840 else { 841 drawer.setFinancialDocumentMiscellaneousAdvanceAmount(drawer.getFinancialDocumentMiscellaneousAdvanceAmount().add(trans.getNewItemInProcess().getItemAmount())); 842 } 843 } 844 } 845 846 /** 847 * This method checks the cashiering transaction to see if any open items in process were at least partially paid back; 848 * it then saves the changes. 849 * 850 * @param cmDoc The cash management document that the items in process will be associated with 851 * @param trans The cashiering transaction the items in process are associated with. 852 */ 853 protected void saveExisingItemsInProcess(CashManagementDocument cmDoc, CashieringTransaction trans) { 854 if (trans.getOpenItemsInProcess() != null) { 855 CashDrawer drawer = cmDoc.getCashDrawer(); 856 857 for (CashieringItemInProcess itemInProc: trans.getOpenItemsInProcess()) { 858 if (itemInProc.getCurrentPayment() != null && !itemInProc.getCurrentPayment().equals(KualiDecimal.ZERO)) { 859 itemInProc.setItemRemainingAmount(itemInProc.getItemRemainingAmount().subtract(itemInProc.getCurrentPayment())); 860 itemInProc.setItemReducedAmount(itemInProc.getItemReducedAmount().add(itemInProc.getCurrentPayment())); 861 if (drawer.getFinancialDocumentMiscellaneousAdvanceAmount() != null) { 862 drawer.setFinancialDocumentMiscellaneousAdvanceAmount(drawer.getFinancialDocumentMiscellaneousAdvanceAmount().subtract(itemInProc.getCurrentPayment())); 863 } 864 itemInProc.setCurrentPayment(KualiDecimal.ZERO); 865 if (itemInProc.getItemRemainingAmount().equals(KualiDecimal.ZERO)) { 866 itemInProc.setItemClosedDate(new java.sql.Date(SpringContext.getBean(DateTimeService.class).getCurrentDate().getTime())); 867 } 868 businessObjectService.save(itemInProc); 869 } 870 } 871 } 872 } 873 874 /** 875 * 876 * This method retrieves the amount of cash in the "money in" portion of the transaction and saves it to the 877 * cash management document. 878 * 879 * @param cmDoc The cash management document that the cash will be saved to. 880 * @param trans The cashiering transaction the cash is currently associated with. 881 */ 882 protected void saveMoneyInCash(CashManagementDocument cmDoc, CashieringTransaction trans) { 883 // get the cumulative money in coin for this doc 884 CoinDetail cumulativeMoneyInCoin = cashManagementDao.findCoinDetailByCashieringRecordSource(cmDoc.getDocumentNumber(), CashieringTransaction.DETAIL_DOCUMENT_TYPE, KFSConstants.CurrencyCoinSources.CASH_MANAGEMENT_IN); 885 // add the new money in coin 886 cumulativeMoneyInCoin.add(trans.getMoneyInCoin()); 887 // save the cumulative 888 businessObjectService.save(cumulativeMoneyInCoin); 889 890 CurrencyDetail cumulativeMoneyInCurrency = cashManagementDao.findCurrencyDetailByCashieringRecordSource(cmDoc.getDocumentNumber(), CashieringTransaction.DETAIL_DOCUMENT_TYPE, KFSConstants.CurrencyCoinSources.CASH_MANAGEMENT_IN); 891 cumulativeMoneyInCurrency.add(trans.getMoneyInCurrency()); 892 businessObjectService.save(cumulativeMoneyInCurrency); 893 } 894 895 /** 896 * 897 * This method retrieves the amount of cash in the "money out" portion of the transaction and saves it to the 898 * cash management document. 899 * 900 * @param cmDoc The cash management document that the cash will be saved to. 901 * @param trans The cashiering transaction the cash is currently associated with. 902 */ 903 protected void saveMoneyOutCash(CashManagementDocument cmDoc, CashieringTransaction trans) { 904 CoinDetail cumulativeMoneyOutCoin = cashManagementDao.findCoinDetailByCashieringRecordSource(cmDoc.getDocumentNumber(), CashieringTransaction.DETAIL_DOCUMENT_TYPE, KFSConstants.CurrencyCoinSources.CASH_MANAGEMENT_OUT); 905 cumulativeMoneyOutCoin.add(trans.getMoneyOutCoin()); 906 businessObjectService.save(cumulativeMoneyOutCoin); 907 908 CurrencyDetail cumulativeMoneyOutCurrency = cashManagementDao.findCurrencyDetailByCashieringRecordSource(cmDoc.getDocumentNumber(), CashieringTransaction.DETAIL_DOCUMENT_TYPE, KFSConstants.CurrencyCoinSources.CASH_MANAGEMENT_OUT); 909 cumulativeMoneyOutCurrency.add(trans.getMoneyOutCurrency()); 910 businessObjectService.save(cumulativeMoneyOutCurrency); 911 } 912 913 /** 914 * This method retrieves a collection of open CashieringItemInProcess objects from the cash management document given 915 * and returns that collection. 916 * 917 * @param cmDoc The document the open items in process will be retrieved from. 918 * @return The collection of open items. 919 * 920 * @see org.kuali.kfs.fp.document.service.CashManagementService#getOpenItemsInProcess(org.kuali.kfs.fp.document.CashManagementDocument) 921 */ 922 public List<CashieringItemInProcess> getOpenItemsInProcess(CashManagementDocument cmDoc) { 923 List<CashieringItemInProcess> itemsInProcess = cashManagementDao.findOpenItemsInProcessByCampusCode(cmDoc.getCampusCode()); 924 return (itemsInProcess == null) ? new ArrayList<CashieringItemInProcess>() : itemsInProcess; 925 } 926 927 /** 928 * This method retrieves a collection of recently closed CashieringItemInProcess objects from the cash management 929 * document given and returns the collection. 930 * 931 * @param cmDoc The cash management document the recently closed items will be retrieved from. 932 * @return The collection of recently closed items. 933 * 934 * @see org.kuali.kfs.fp.document.service.CashManagementService#getRecentlyClosedItemsInProcess(org.kuali.kfs.fp.document.CashManagementDocument) 935 */ 936 public List<CashieringItemInProcess> getRecentlyClosedItemsInProcess(CashManagementDocument cmDoc) { 937 return cashManagementDao.findRecentlyClosedItemsInProcess(cmDoc.getCampusCode()); 938 } 939 940 /** 941 * This method generates a master coin detail for the cash management document given. A master coin detail is a CoinDetail 942 * that represents the result of all the money in and out of the cash drawer via the given cash management document. The 943 * following formula is used to perform this calculation: 944 * <ul> 945 * <li> 946 * "coin detail for cash receipt - coin detail for deposits + coin detail for money in - coin detail for money out" 947 * </li> 948 * </ul> 949 * 950 * @param cmDoc The document the master coin detail will be generated from. 951 * @return The resulting coin detail. 952 * 953 * @see org.kuali.kfs.fp.document.service.CashManagementService#generateMasterCoinDetail(org.kuali.kfs.fp.document.CashManagementDocument) 954 */ 955 public CoinDetail generateMasterCoinDetail(CashManagementDocument cmDoc) { 956 CoinDetail masterDetail = new CoinDetail(); 957 masterDetail.setDocumentNumber(cmDoc.getDocumentNumber()); 958 masterDetail.setFinancialDocumentTypeCode(CashieringTransaction.DETAIL_DOCUMENT_TYPE); 959 masterDetail.setCashieringRecordSource(KFSConstants.CurrencyCoinSources.CASH_MANAGEMENT_MASTER); 960 961 masterDetail.zeroOutAmounts(); 962 963 CoinDetail cashReceiptDetail = cashManagementDao.findCoinDetailByCashieringRecordSource(cmDoc.getDocumentNumber(), CashieringTransaction.DETAIL_DOCUMENT_TYPE, KFSConstants.CurrencyCoinSources.CASH_RECEIPTS); 964 if (cashReceiptDetail != null) { 965 masterDetail.add(cashReceiptDetail); 966 } 967 968 CoinDetail depositDetail = cashManagementDao.findCoinDetailByCashieringRecordSource(cmDoc.getDocumentNumber(), CashieringTransaction.DETAIL_DOCUMENT_TYPE, KFSConstants.CurrencyCoinSources.DEPOSITS); 969 if (depositDetail != null) { 970 masterDetail.subtract(depositDetail); 971 } 972 973 CoinDetail moneyInDetail = cashManagementDao.findCoinDetailByCashieringRecordSource(cmDoc.getDocumentNumber(), CashieringTransaction.DETAIL_DOCUMENT_TYPE, KFSConstants.CurrencyCoinSources.CASH_MANAGEMENT_IN); 974 if (moneyInDetail != null) { 975 masterDetail.add(moneyInDetail); 976 } 977 978 CoinDetail moneyOutDetail = cashManagementDao.findCoinDetailByCashieringRecordSource(cmDoc.getDocumentNumber(), CashieringTransaction.DETAIL_DOCUMENT_TYPE, KFSConstants.CurrencyCoinSources.CASH_MANAGEMENT_OUT); 979 if (moneyOutDetail != null) { 980 masterDetail.subtract(moneyOutDetail); 981 } 982 983 return masterDetail; 984 } 985 986 987 /** 988 * This method generates a master currency detail for the cash management document given. A master currency detail is a currencyDetail 989 * that represents the result of all the money in and out of the cash drawer via the given cash management document. The 990 * following formula is used to perform this calculation: 991 * <ul> 992 * <li> 993 * "currency detail for cash receipt - currency detail for deposits + currency detail for money in - currency detail for money out" 994 * </li> 995 * </ul> 996 * 997 * @param cmDoc The document the master currency detail will be generated from. 998 * @return The resulting currency detail. 999 * 1000 * @see org.kuali.kfs.fp.document.service.CashManagementService#generateMasterCurrencyDetail(org.kuali.kfs.fp.document.CashManagementDocument) 1001 */ 1002 public CurrencyDetail generateMasterCurrencyDetail(CashManagementDocument cmDoc) { 1003 CurrencyDetail masterDetail = new CurrencyDetail(); 1004 masterDetail.setDocumentNumber(cmDoc.getDocumentNumber()); 1005 masterDetail.setFinancialDocumentTypeCode(CashieringTransaction.DETAIL_DOCUMENT_TYPE); 1006 masterDetail.setCashieringRecordSource(KFSConstants.CurrencyCoinSources.CASH_MANAGEMENT_MASTER); 1007 1008 masterDetail.zeroOutAmounts(); 1009 1010 CurrencyDetail cashReceiptDetail = cashManagementDao.findCurrencyDetailByCashieringRecordSource(cmDoc.getDocumentNumber(), CashieringTransaction.DETAIL_DOCUMENT_TYPE, KFSConstants.CurrencyCoinSources.CASH_RECEIPTS); 1011 if (cashReceiptDetail != null) { 1012 masterDetail.add(cashReceiptDetail); 1013 } 1014 1015 CurrencyDetail depositDetail = cashManagementDao.findCurrencyDetailByCashieringRecordSource(cmDoc.getDocumentNumber(), CashieringTransaction.DETAIL_DOCUMENT_TYPE, KFSConstants.CurrencyCoinSources.DEPOSITS); 1016 if (depositDetail != null) { 1017 masterDetail.subtract(depositDetail); 1018 } 1019 1020 CurrencyDetail moneyInDetail = cashManagementDao.findCurrencyDetailByCashieringRecordSource(cmDoc.getDocumentNumber(), CashieringTransaction.DETAIL_DOCUMENT_TYPE, KFSConstants.CurrencyCoinSources.CASH_MANAGEMENT_IN); 1021 if (moneyInDetail != null) { 1022 masterDetail.add(moneyInDetail); 1023 } 1024 1025 CurrencyDetail moneyOutDetail = cashManagementDao.findCurrencyDetailByCashieringRecordSource(cmDoc.getDocumentNumber(), CashieringTransaction.DETAIL_DOCUMENT_TYPE, KFSConstants.CurrencyCoinSources.CASH_MANAGEMENT_OUT); 1026 if (moneyOutDetail != null) { 1027 masterDetail.subtract(moneyOutDetail); 1028 } 1029 1030 return masterDetail; 1031 } 1032 1033 /** 1034 * Populates the currency and coin detail for final deposits by setting the deposited currency or coin amount equal to the 1035 * associated cashiering record currency or coin amount. 1036 * 1037 * @param cmDoc The cash management document which has deposits to populate. 1038 */ 1039 public void populateCashDetailsForDeposit(CashManagementDocument cmDoc) { 1040 // if this ever gets changed so that each deposit has currency/coin lines, then 1041 // we can just do this with the ORM, which would be *much* easier 1042 for (Deposit d: cmDoc.getDeposits()) { 1043 if (d.getDepositTypeCode().equals(DepositConstants.DEPOSIT_TYPE_FINAL)) { 1044 if (d.getDepositedCurrency() == null) { 1045 d.setDepositedCurrency(cashManagementDao.findCurrencyDetailByCashieringRecordSource(cmDoc.getDocumentNumber(), CashieringTransaction.DETAIL_DOCUMENT_TYPE, CurrencyCoinSources.DEPOSITS)); 1046 } 1047 if (d.getDepositedCoin() == null) { 1048 d.setDepositedCoin(cashManagementDao.findCoinDetailByCashieringRecordSource(cmDoc.getDocumentNumber(), CashieringTransaction.DETAIL_DOCUMENT_TYPE, CurrencyCoinSources.DEPOSITS)); 1049 } 1050 } 1051 } 1052 } 1053 1054 /** 1055 * This method retrieves the collection of cashiering checks associated with a given deposit. 1056 * 1057 * @param documentNumber The id of the document to search for the deposit within. 1058 * @param depositLineNumber The line number of the deposit to be found. 1059 * @return A collection of checks for the deposit and document given. 1060 * 1061 * @see org.kuali.kfs.fp.document.service.CashManagementService#selectCashieringChecksForDeposit(java.lang.String, java.lang.Integer) 1062 */ 1063 public List<Check> selectCashieringChecksForDeposit(String documentNumber, Integer depositLineNumber) { 1064 return cashManagementDao.selectCashieringChecksForDeposit(documentNumber, depositLineNumber); 1065 } 1066 1067 /** 1068 * This method retrieves the collection of undeposited cashiering checks associated with the document given. 1069 * 1070 * @param documentNumber The id of the document to search for the undeposited checks within. 1071 * @return A collection of any undeposited checks for the document given. 1072 * 1073 * @see org.kuali.kfs.fp.document.service.CashManagementService#selectUndepositedCashieringChecks(java.lang.String) 1074 */ 1075 public List<Check> selectUndepositedCashieringChecks(String documentNumber) { 1076 return cashManagementDao.selectUndepositedCashieringChecks(documentNumber); 1077 } 1078 1079 /** 1080 * This method retrieves a collection of all deposited checks associated with the given document. 1081 * 1082 * @param documentNumber The document to retrieve the deposited checks from. 1083 * @return A collection of all deposited checks for the document given. 1084 * 1085 * @see org.kuali.kfs.fp.document.service.CashManagementService#selectDepositedCashieringChecks(java.lang.String) 1086 */ 1087 public List<Check> selectDepositedCashieringChecks(String documentNumber) { 1088 return cashManagementDao.selectDepositedCashieringChecks(documentNumber); 1089 } 1090 1091 1092 /** 1093 * Total up the amounts of all checks so far deposited as part of the given cash management document. 1094 * 1095 * @param documentNumber The id of a cash management document. 1096 * @return The total amount of cashiering checks deposited so far as part of that document. 1097 */ 1098 public KualiDecimal calculateDepositedCheckTotal(String documentNumber) { 1099 KualiDecimal total = KualiDecimal.ZERO; 1100 for (Check check: cashManagementDao.selectDepositedCashieringChecks(documentNumber)) { 1101 if (check != null && check.getAmount() != null && check.getAmount().isGreaterThan(KualiDecimal.ZERO)) { 1102 total = total.add(check.getAmount()); 1103 } 1104 } 1105 return total; 1106 } 1107 1108 /** 1109 * Calculates the total amount of all the undeposited checks for a cash management document. 1110 * 1111 * @param documentNumber The id of the cash management document to pull the undeposited checks from. 1112 * @return The total amount of all undeposited checks for the document given. 1113 * 1114 * @see org.kuali.kfs.fp.document.service.CashManagementService#calculateUndepositedCheckTotal(java.lang.String) 1115 */ 1116 public KualiDecimal calculateUndepositedCheckTotal(String documentNumber) { 1117 KualiDecimal total = KualiDecimal.ZERO; 1118 for (Check check: cashManagementDao.selectUndepositedCashieringChecks(documentNumber)) { 1119 if (check != null && check.getAmount() != null && check.getAmount().isGreaterThan(KualiDecimal.ZERO)) { 1120 total = total.add(check.getAmount()); 1121 } 1122 } 1123 return total; 1124 } 1125 1126 1127 /** 1128 * This method determines if a document can be cancelled, by reviewing a set of criteria: 1129 * - do any cash receipts exist in this document? 1130 * - do any cashiering checks exist in this document? 1131 * - do any cash details exist in this document? 1132 * If any of these questions comes back as true, then the document cannot be canceled. 1133 * 1134 * @param cmDoc The document that would be canceled. 1135 * @return True if the document can be canceled, false otherwise. 1136 * 1137 * @see org.kuali.kfs.fp.document.service.CashManagementService#allowDocumentCancellation(org.kuali.kfs.fp.document.CashManagementDocument) 1138 */ 1139 public boolean allowDocumentCancellation(CashManagementDocument cmDoc) { 1140 return !existCashReceipts(cmDoc) && !existCashieringChecks(cmDoc) && !existCashDetails(cmDoc); 1141 } 1142 1143 /** 1144 * This method determines if any verified, interim, or final cash receipts currently exist. 1145 * 1146 * @param cmDoc The cash management document to find cash receipts associated with the campus of. 1147 * @return True if there's some cash receipts that verified, interim, or final in this campus; false if otherwise. 1148 */ 1149 protected boolean existCashReceipts(CashManagementDocument cmDoc) { 1150 List<CashReceiptDocument> cashReceipts = SpringContext.getBean(CashReceiptService.class).getCashReceipts(cmDoc.getCampusCode(), new String[] {KFSConstants.DocumentStatusCodes.CashReceipt.VERIFIED, KFSConstants.DocumentStatusCodes.CashReceipt.INTERIM, KFSConstants.DocumentStatusCodes.CashReceipt.FINAL} ); 1151 return cashReceipts != null && cashReceipts.size() > 0; 1152 } 1153 1154 /** 1155 * This method determines if any populated currency or coin details exist for the given document. 1156 * 1157 * @param cmDoc A cash management document to find details. 1158 * @return True if it finds populated currency or coin details, false if otherwise. 1159 */ 1160 protected boolean existCashDetails(CashManagementDocument cmDoc) { 1161 boolean result = false; 1162 List<CurrencyDetail> currencyDetails = cashManagementDao.getAllCurrencyDetails(cmDoc.getDocumentNumber()); 1163 if (currencyDetails != null && currencyDetails.size() > 0) { 1164 for (CurrencyDetail detail: currencyDetails) { 1165 result |= !detail.isEmpty(); 1166 } 1167 } 1168 if (!result) { 1169 List<CoinDetail> coinDetails = cashManagementDao.getAllCoinDetails(cmDoc.getDocumentNumber()); 1170 if (coinDetails != null && coinDetails.size() > 0) { 1171 for (CoinDetail detail: coinDetails) { 1172 result |= !detail.isEmpty(); 1173 } 1174 } 1175 } 1176 return result; 1177 } 1178 1179 /** 1180 * This method determines if cashiering checks exist for the cash management document. 1181 * 1182 * @param cmDoc The cash management document to test. 1183 * @return True if it finds some checks, false if otherwise. 1184 */ 1185 protected boolean existCashieringChecks(CashManagementDocument cmDoc) { 1186 List<Check> undepositedChecks = this.selectUndepositedCashieringChecks(cmDoc.getDocumentNumber()); 1187 List<Check> depositedChecks = cashManagementDao.selectDepositedCashieringChecks(cmDoc.getDocumentNumber()); 1188 return (undepositedChecks != null && undepositedChecks.size() > 0) || (depositedChecks != null && depositedChecks.size() > 0); 1189 } 1190 1191 /** 1192 * This method retrieves the next available check line number from the document provided. 1193 * 1194 * @param documentNumber The document to get the next check line number from. 1195 * @return The next available check line number. 1196 * 1197 * @see org.kuali.kfs.fp.document.service.CashManagementService#selectNextAvailableCheckLineNumber(java.lang.String) 1198 */ 1199 public Integer selectNextAvailableCheckLineNumber(String documentNumber) { 1200 return cashManagementDao.selectNextAvailableCheckLineNumber(documentNumber); 1201 } 1202 1203 /** 1204 * This method retrieves the cash details for the final deposit object. The resulting map contains a CurrencyDetail and a 1205 * CoinDetail object, both keyed by the class of detail they represent (ie. CurrencyDetail.class is the map key for the 1206 * CurrencyDetail of the document). 1207 * 1208 * @param documentNumber The document the details will be generated from. 1209 * @return A map of the resulting cash details. This map is keyed by the detail class object. 1210 * 1211 * @see org.kuali.kfs.fp.document.service.CashManagementService#getCashDetailsForFinalDeposit(java.lang.String) 1212 */ 1213 public Map<Class, Object> getCashDetailsForFinalDeposit(String documentNumber) { 1214 CurrencyDetail finalDepositCurrencyDetail = cashManagementDao.findCurrencyDetailByCashieringRecordSource(documentNumber, CashieringTransaction.DETAIL_DOCUMENT_TYPE, KFSConstants.CurrencyCoinSources.DEPOSITS); 1215 CoinDetail finalDepositCoinDetail = cashManagementDao.findCoinDetailByCashieringRecordSource(documentNumber, CashieringTransaction.DETAIL_DOCUMENT_TYPE, KFSConstants.CurrencyCoinSources.DEPOSITS); 1216 Map<Class, Object> result = new HashMap<Class, Object>(); 1217 if (finalDepositCurrencyDetail != null) { 1218 result.put(CurrencyDetail.class, finalDepositCurrencyDetail); 1219 } 1220 if (finalDepositCoinDetail != null) { 1221 result.put(CoinDetail.class, finalDepositCoinDetail); 1222 } 1223 return result; 1224 } 1225 1226 // injected dependencies 1227 /** 1228 * Getter for retrieving an instance of the BusinessObjectService attribute. 1229 * 1230 * @return Current value of businessObjectService. 1231 */ 1232 public BusinessObjectService getBusinessObjectService() { 1233 return businessObjectService; 1234 } 1235 1236 /** 1237 * Sets the businessObjectService attribute value. 1238 * 1239 * @param businessObjectService The businessObjectService to set. 1240 */ 1241 public void setBusinessObjectService(BusinessObjectService businessObjectService) { 1242 this.businessObjectService = businessObjectService; 1243 } 1244 1245 /** 1246 * Getter for retrieving an instance of the CashDrawerService attribute. 1247 * 1248 * @return Current value of cashDrawerService. 1249 */ 1250 public CashDrawerService getCashDrawerService() { 1251 return cashDrawerService; 1252 } 1253 1254 /** 1255 * Sets the cashDrawerService attribute value. 1256 * 1257 * @param cashDrawerService The cashDrawerService to set. 1258 */ 1259 public void setCashDrawerService(CashDrawerService cashDrawerService) { 1260 this.cashDrawerService = cashDrawerService; 1261 } 1262 1263 /** 1264 * Gets the documentService attribute. 1265 * 1266 * @return Current value of documentService. 1267 */ 1268 public DocumentService getDocumentService() { 1269 return documentService; 1270 } 1271 1272 /** 1273 * Sets the documentService attribute value. 1274 * 1275 * @param documentService 1276 */ 1277 public void setDocumentService(DocumentService documentService) { 1278 this.documentService = documentService; 1279 } 1280 1281 /** 1282 * Gets the dateTimeService attribute. 1283 * 1284 * @return Current value of dateTimeService. 1285 */ 1286 public DateTimeService getDateTimeService() { 1287 return dateTimeService; 1288 } 1289 1290 /** 1291 * Sets the dateTimeService attribute value. 1292 * 1293 * @param dateTimeService The dateTimeService to set. 1294 */ 1295 public void setDateTimeService(DateTimeService dateTimeService) { 1296 this.dateTimeService = dateTimeService; 1297 } 1298 1299 /** 1300 * Gets the cashManagementDao attribute. 1301 * 1302 * @return Returns the cashManagementDao. 1303 */ 1304 public CashManagementDao getCashManagementDao() { 1305 return cashManagementDao; 1306 } 1307 1308 /** 1309 * Sets the cashManagementDao attribute value. 1310 * 1311 * @param cashManagementDao The cashManagementDao to set. 1312 */ 1313 public void setCashManagementDao(CashManagementDao cashManagementDao) { 1314 this.cashManagementDao = cashManagementDao; 1315 } 1316 1317 1318 /** 1319 * @return an implementation of the DataDictionaryService 1320 */ 1321 public DataDictionaryService getDataDictionaryService() { 1322 return dataDictionaryService; 1323 } 1324 1325 /** 1326 * Sets the data dictionary service implementation 1327 * @param dataDictionaryService the implementation of the data dictionary service to use 1328 */ 1329 public void setDataDictionaryService(DataDictionaryService dataDictionaryService) { 1330 this.dataDictionaryService = dataDictionaryService; 1331 } 1332 1333 } 1334