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