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.web.struts;
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    import java.util.Properties;
024    import java.util.Set;
025    
026    import java.sql.Date;
027    import java.sql.Timestamp;
028    import org.kuali.rice.kns.service.DateTimeService;
029    
030    import javax.servlet.http.HttpServletRequest;
031    import javax.servlet.http.HttpServletResponse;
032    
033    import org.apache.commons.lang.StringUtils;
034    import org.apache.struts.action.ActionForm;
035    import org.apache.struts.action.ActionForward;
036    import org.apache.struts.action.ActionMapping;
037    import org.kuali.kfs.fp.businessobject.CashDrawer;
038    import org.kuali.kfs.fp.businessobject.CashieringTransaction;
039    import org.kuali.kfs.fp.businessobject.Check;
040    import org.kuali.kfs.fp.businessobject.CoinDetail;
041    import org.kuali.kfs.fp.businessobject.CurrencyDetail;
042    import org.kuali.kfs.fp.businessobject.Deposit;
043    import org.kuali.kfs.fp.businessobject.DepositWizardCashieringCheckHelper;
044    import org.kuali.kfs.fp.businessobject.DepositWizardHelper;
045    import org.kuali.kfs.fp.businessobject.format.CashDrawerStatusCodeFormatter;
046    import org.kuali.kfs.fp.document.CashManagementDocument;
047    import org.kuali.kfs.fp.document.CashReceiptDocument;
048    import org.kuali.kfs.fp.document.service.CashManagementService;
049    import org.kuali.kfs.fp.document.service.CashReceiptService;
050    import org.kuali.kfs.fp.exception.CashDrawerStateException;
051    import org.kuali.kfs.fp.service.CashDrawerService;
052    import org.kuali.kfs.sys.KFSConstants;
053    import org.kuali.kfs.sys.KFSKeyConstants;
054    import org.kuali.kfs.sys.KFSPropertyConstants;
055    import org.kuali.kfs.sys.KFSConstants.CashDrawerConstants;
056    import org.kuali.kfs.sys.KFSConstants.DocumentStatusCodes.CashReceipt;
057    import org.kuali.kfs.sys.businessobject.Bank;
058    import org.kuali.kfs.sys.context.SpringContext;
059    import org.kuali.kfs.sys.document.datadictionary.FinancialSystemTransactionalDocumentEntry;
060    import org.kuali.rice.kew.exception.WorkflowException;
061    import org.kuali.rice.kim.bo.Person;
062    import org.kuali.rice.kns.document.authorization.DocumentAuthorizer;
063    import org.kuali.rice.kns.document.authorization.TransactionalDocumentAuthorizer;
064    import org.kuali.rice.kns.document.authorization.TransactionalDocumentPresentationController;
065    import org.kuali.rice.kns.exception.InfrastructureException;
066    import org.kuali.rice.kns.service.BusinessObjectService;
067    import org.kuali.rice.kns.service.DataDictionaryService;
068    import org.kuali.rice.kns.service.DocumentHelperService;
069    import org.kuali.rice.kns.service.DocumentService;
070    import org.kuali.rice.kns.util.GlobalVariables;
071    import org.kuali.rice.kns.util.KNSConstants;
072    import org.kuali.rice.kns.util.KualiDecimal;
073    import org.kuali.rice.kns.util.UrlFactory;
074    import org.kuali.rice.kns.web.format.CurrencyFormatter;
075    import org.kuali.rice.kns.web.struts.action.KualiAction;
076    
077    /**
078     * This class handles actions for the deposit wizard, which is used to create deposits that bundle groupings of Cash Receipt
079     * documents.
080     */
081    public class DepositWizardAction extends KualiAction {
082        private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(DepositWizardAction.class);
083        private static final String CASH_MANAGEMENT_STATUS_PAGE = "/cashManagementStatus.do";
084    
085        /**
086         * Overrides the parent to validate the document state of the cashManagementDocument which will be updated and redisplayed after
087         * the DepositWizard builds and attaches the new Deposit.
088         * 
089         * @see org.apache.struts.action.Action#execute(org.apache.struts.action.ActionMapping, org.apache.struts.action.ActionForm,
090         *      javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
091         */
092        @Override
093        public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
094            DepositWizardForm dwForm = (DepositWizardForm) form;
095    
096            ActionForward dest = super.execute(mapping, form, request, response);
097    
098            // check authorization manually, since the auth-check isn't inherited by this class
099            DocumentAuthorizer cmDocAuthorizer = SpringContext.getBean(DocumentHelperService.class).getDocumentAuthorizer(KFSConstants.FinancialDocumentTypeCodes.CASH_MANAGEMENT);
100            Person luser = GlobalVariables.getUserSession().getPerson();
101            cmDocAuthorizer.canInitiate(KFSConstants.FinancialDocumentTypeCodes.CASH_MANAGEMENT, luser);
102    
103            // populate the outgoing form used by the JSP if it seems empty
104            String cmDocId = dwForm.getCashManagementDocId();
105            if (StringUtils.isBlank(cmDocId)) {
106                cmDocId = request.getParameter("cmDocId");
107                String depositTypeCode = request.getParameter("depositTypeCode");
108    
109                CashManagementDocument cmDoc = (CashManagementDocument) SpringContext.getBean(DocumentService.class).getByDocumentHeaderId(cmDocId);
110    
111                try {
112                    initializeForm(dwForm, cmDoc, depositTypeCode);
113                }
114                catch (CashDrawerStateException cdse) {
115                    dest = new ActionForward(UrlFactory.parameterizeUrl(CASH_MANAGEMENT_STATUS_PAGE, cdse.toProperties()), true);
116                }
117            }
118    
119            return dest;
120        }
121    
122        /**
123         * Initializes the given form using the given values
124         * 
125         * @param dform
126         * @param cmDoc
127         * @param depositTypeCode
128         */
129        private void initializeForm(DepositWizardForm dform, CashManagementDocument cmDoc, String depositTypeCode) {
130            CashDrawer cd = SpringContext.getBean(CashDrawerService.class).getByCampusCode(cmDoc.getCampusCode());
131            if (cd == null) {
132                throw new RuntimeException("No cash drawer exists for campus code "+cmDoc.getCampusCode()+"; please create on via the Cash Drawer Maintenance Document before attemping to create a CashManagementDocument for campus "+cmDoc.getCampusCode());
133            }
134            if (!cd.isOpen()) {
135                CashDrawerStatusCodeFormatter f = new CashDrawerStatusCodeFormatter();
136    
137                String cmDocId = cmDoc.getDocumentNumber();
138                String currentState = cd.getStatusCode();
139    
140                throw new CashDrawerStateException(cmDoc.getCampusCode(), cmDocId, (String) f.format(CashDrawerConstants.STATUS_OPEN), (String) f.format(cd.getStatusCode()));
141            }
142    
143            dform.setCashManagementDocId(cmDoc.getDocumentNumber());
144            dform.setCashDrawerCampusCode(cmDoc.getCampusCode());
145    
146            dform.setDepositTypeCode(depositTypeCode);
147    
148            if (depositTypeCode.equals(KFSConstants.DocumentStatusCodes.CashReceipt.FINAL)) {
149                // hey, we're the magical final deposit. We get currency and coin details!
150                CurrencyDetail currencyDetail = new CurrencyDetail();
151                currencyDetail.setDocumentNumber(cmDoc.getDocumentNumber());
152                currencyDetail.setCashieringRecordSource(KFSConstants.CurrencyCoinSources.DEPOSITS);
153                currencyDetail.setFinancialDocumentTypeCode(CashieringTransaction.DETAIL_DOCUMENT_TYPE);
154                dform.setCurrencyDetail(currencyDetail);
155    
156                CoinDetail coinDetail = new CoinDetail();
157                coinDetail.setDocumentNumber(cmDoc.getDocumentNumber());
158                coinDetail.setCashieringRecordSource(KFSConstants.CurrencyCoinSources.DEPOSITS);
159                coinDetail.setFinancialDocumentTypeCode(CashieringTransaction.DETAIL_DOCUMENT_TYPE);
160                dform.setCoinDetail(coinDetail);
161            }
162    
163            loadCashReceipts(dform);
164            loadUndepositedCashieringChecks(dform);
165            loadEditModesAndDocumentActions(dform);
166        }
167    
168        /**
169         * Loads the CashReceipt information, re/setting the related form fields
170         * 
171         * @param dform
172         */
173        private void loadCashReceipts(DepositWizardForm dform) {
174            List verifiedReceipts = SpringContext.getBean(CashReceiptService.class).getCashReceipts(dform.getCashDrawerCampusCode(), KFSConstants.DocumentStatusCodes.CashReceipt.VERIFIED);
175            dform.setDepositableCashReceipts(new ArrayList());
176            dform.setCheckFreeCashReceipts(new ArrayList<CashReceiptDocument>());
177    
178            // prepopulate DepositWizardHelpers
179            int index = 0;
180            for (Iterator i = verifiedReceipts.iterator(); i.hasNext();) {
181                CashReceiptDocument receipt = (CashReceiptDocument) i.next();
182                if (receipt.getCheckCount() == 0 && receipt.getTotalCheckAmount().equals(KualiDecimal.ZERO)) {
183                    dform.getCheckFreeCashReceipts().add(receipt);
184                }
185                else {
186                    dform.getDepositableCashReceipts().add(receipt);
187                    DepositWizardHelper d = dform.getDepositWizardHelper(index++);
188                    //KFSMI-5232 Jira fix.  Convert the time stamp to SQL date format
189                    Timestamp ts = receipt.getDocumentHeader().getWorkflowDocument().getCreateDate();
190                    
191                    try {
192                        d.setCashReceiptCreateDate(SpringContext.getBean(DateTimeService.class).convertToSqlDate(ts));
193                    } catch (Exception e) {
194                        
195                    }
196                }
197            }
198        }
199    
200        /**
201         * This loads any cashiering checks which have not yet been deposited into the DepositWizardForm
202         * 
203         * @param dform a form to load undeposited checks into
204         */
205        private void loadUndepositedCashieringChecks(DepositWizardForm dform) {
206            List<Check> cashieringChecks = SpringContext.getBean(CashManagementService.class).selectUndepositedCashieringChecks(dform.getCashManagementDocId());
207            dform.setDepositableCashieringChecks(cashieringChecks);
208        }
209    
210        private void loadEditModesAndDocumentActions(DepositWizardForm dform) {
211            final FinancialSystemTransactionalDocumentEntry ddEntry = getCashManagementDataDictionaryEntry();
212            final TransactionalDocumentPresentationController presentationController = getCashManagementPresentationController(ddEntry);
213            final TransactionalDocumentAuthorizer docAuthorizer = getCashManagementDocumentAuthorizer(ddEntry);
214            
215            dform.setEditingMode(retrieveEditingModes(dform.getCashManagementDocId(), presentationController, docAuthorizer));
216            dform.setDocumentActions(retrieveDocumentActions(dform.getCashManagementDocId(), presentationController, docAuthorizer));
217        }
218        
219        /**
220         * @return the class of the cash management document
221         */
222        protected String getCashManagementDocumentTypeName() {
223            return "CMD";
224        }
225        
226        /**
227         * @return the data dictionary entry for the cash management class
228         */
229        private FinancialSystemTransactionalDocumentEntry getCashManagementDataDictionaryEntry() {
230            final DataDictionaryService ddService = SpringContext.getBean(DataDictionaryService.class);
231            return (FinancialSystemTransactionalDocumentEntry)ddService.getDataDictionary().getDocumentEntry(getCashManagementDocumentTypeName());
232        }
233        
234        /**
235         * Returns an instance of the document presentation controller for the cash management class
236         * @param cashManagementEntry the data dictionary entry for the cash management document
237         * @return an instance of the proper document presentation controller
238         */
239        private TransactionalDocumentPresentationController getCashManagementPresentationController(FinancialSystemTransactionalDocumentEntry cashManagementEntry) {
240            final Class presentationControllerClass = cashManagementEntry.getDocumentPresentationControllerClass();
241            TransactionalDocumentPresentationController presentationController = null;
242            try {
243                presentationController = (TransactionalDocumentPresentationController)presentationControllerClass.newInstance();
244            }
245            catch (InstantiationException ie) {
246                throw new RuntimeException("Could not instantiate cash management presentation controller of class "+presentationControllerClass.getName(), ie);
247            }
248            catch (IllegalAccessException iae) {
249                throw new RuntimeException("Could not instantiate cash management presentation controller of class "+presentationControllerClass.getName(), iae);
250            }
251            return presentationController;
252        }
253        
254        /**
255         * Returns an instance of the document authorizer for the cash management class
256         * @param cashManagementEntry the data dictionary entry for the cash management document
257         * @return an instance of the proper document authorizer
258         */
259        private TransactionalDocumentAuthorizer getCashManagementDocumentAuthorizer(FinancialSystemTransactionalDocumentEntry cashManagementEntry) {
260            final Class docAuthorizerClass = cashManagementEntry.getDocumentAuthorizerClass();
261            TransactionalDocumentAuthorizer docAuthorizer = null;
262            try {
263                docAuthorizer = (TransactionalDocumentAuthorizer)docAuthorizerClass.newInstance();
264            }
265            catch (InstantiationException ie) {
266                throw new RuntimeException("Could not instantiate cash management document authorizer of class "+docAuthorizerClass.getName(), ie);
267            }
268            catch (IllegalAccessException iae) {
269                throw new RuntimeException("Could not instantiate cash management document authorizer of class "+docAuthorizerClass.getName(), iae);
270            }
271            return docAuthorizer;
272        }
273        
274        /**
275         * Retrieves the edit modes for the given cash management document
276         * @param cashManagementDocId the id of the cash management document to check
277         * @param presentationController the presentation controller of the cash management document
278         * @param docAuthorizer the cash management document authorizer
279         * @return a Map of edit modes
280         */
281        private Map retrieveEditingModes(String cashManagementDocId, TransactionalDocumentPresentationController presentationController, TransactionalDocumentAuthorizer docAuthorizer) {
282            Map editModeMap = null;
283            try {
284                final CashManagementDocument cmDoc = (CashManagementDocument)SpringContext.getBean(DocumentService.class).getByDocumentHeaderId(cashManagementDocId);
285                Set<String> editModes = presentationController.getEditModes(cmDoc);
286                editModes = docAuthorizer.getEditModes(cmDoc, GlobalVariables.getUserSession().getPerson(), editModes);
287                editModeMap = convertSetToMap(editModes);
288            }
289            catch (WorkflowException we) {
290                throw new RuntimeException("Workflow exception while retrieving document "+cashManagementDocId, we);
291            }
292            return editModeMap;
293        }
294        
295        /**
296         * Retrieves the document actions for the given cash management document
297         * @param cashManagementDocId the id of the cash management document to check
298         * @param presentationController the presentation controller of the cash management document
299         * @param docAuthorizer the cash management document authorizer
300         * @return a Map of document actions
301         */
302        private Map retrieveDocumentActions(String cashManagementDocId, TransactionalDocumentPresentationController presentationController, TransactionalDocumentAuthorizer docAuthorizer) {
303            Map documentActionsMap = null;
304            try {
305                final CashManagementDocument cmDoc = (CashManagementDocument)SpringContext.getBean(DocumentService.class).getByDocumentHeaderId(cashManagementDocId);
306                Set<String> documentActions = presentationController.getDocumentActions(cmDoc);
307                documentActions = docAuthorizer.getEditModes(cmDoc, GlobalVariables.getUserSession().getPerson(), documentActions);
308                documentActionsMap = convertSetToMap(documentActions);
309            }
310            catch (WorkflowException we) {
311                throw new RuntimeException("Workflow exception while retrieving document "+cashManagementDocId, we);
312            }
313            return documentActionsMap;
314        }
315        
316        /**
317         * Converts a set into a map, where each value in the set becomes a key and each value becomes KNSConstants.KUALI_DEFAULT_TRUE_VALUE
318         * @param s a set
319         * @return a map
320         */
321        protected Map convertSetToMap(Set s){
322            Map map = new HashMap();
323            Iterator i = s.iterator();
324            while(i.hasNext()) {
325                Object key = i.next();
326               map.put(key,KNSConstants.KUALI_DEFAULT_TRUE_VALUE);
327            }
328            return map;
329        }
330    
331        /**
332         * Reloads the CashReceipts, leaving everything else unchanged
333         * 
334         * @see org.kuali.rice.kns.web.struts.action.KualiAction#refresh(org.apache.struts.action.ActionMapping,
335         *      org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
336         */
337        @Override
338        public ActionForward refresh(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
339            loadCashReceipts((DepositWizardForm) form);
340            loadUndepositedCashieringChecks((DepositWizardForm) form);
341            loadEditModesAndDocumentActions((DepositWizardForm) form);
342    
343            return super.refresh(mapping, form, request, response);
344        }
345    
346        /**
347         * This method is the starting point for the deposit document wizard.
348         * 
349         * @param mapping
350         * @param form
351         * @param request
352         * @param response
353         * @return ActionForward
354         * @throws Exception
355         */
356        public ActionForward startWizard(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
357            return mapping.findForward(KFSConstants.MAPPING_BASIC);
358        }
359    
360        /**
361         * This method is the action method for creating the new deposit document from the information chosen by the user in the UI.
362         * 
363         * @param mapping
364         * @param form
365         * @param request
366         * @param response
367         * @return ActionForward
368         */
369        public ActionForward createDeposit(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) {
370            ActionForward dest = mapping.findForward(KFSConstants.MAPPING_BASIC);
371    
372            DepositWizardForm dform = (DepositWizardForm) form;
373            final BusinessObjectService boService = SpringContext.getBean(BusinessObjectService.class);
374            final CashReceiptService cashReceiptService = SpringContext.getBean(CashReceiptService.class);
375            final DocumentService documentService = SpringContext.getBean(DocumentService.class);
376            final CashManagementService cashManagementService = SpringContext.getBean(CashManagementService.class);
377            
378            CurrencyFormatter formatter = new CurrencyFormatter();
379            
380            // reload edit modes - just in case we have to return to the deposit wizard page
381            loadEditModesAndDocumentActions(dform);
382    
383            // validate Bank
384            String bankCode = dform.getBankCode();
385            if (StringUtils.isBlank(bankCode)) {
386                GlobalVariables.getMessageMap().putError(KFSConstants.DepositConstants.DEPOSIT_WIZARD_DEPOSITHEADER_ERROR, KFSKeyConstants.Deposit.ERROR_MISSING_BANK);
387            }
388            else {
389                Map keyMap = new HashMap();
390                keyMap.put(KFSPropertyConstants.BANK_CODE, bankCode);
391    
392                Bank bank = (Bank) boService.findByPrimaryKey(Bank.class, keyMap);
393                if (bank == null) {
394                    GlobalVariables.getMessageMap().putError(KFSConstants.DepositConstants.DEPOSIT_WIZARD_DEPOSITHEADER_ERROR, KFSKeyConstants.Deposit.ERROR_UNKNOWN_BANK, bankCode);
395                }
396                else {
397                    dform.setBank(bank);
398                }
399            }
400    
401            boolean depositIsFinal = (StringUtils.equals(dform.getDepositTypeCode(), KFSConstants.DepositConstants.DEPOSIT_TYPE_FINAL));
402    
403            // validate cashReceipt selection
404            List selectedIds = new ArrayList();
405            for (Iterator i = dform.getDepositWizardHelpers().iterator(); i.hasNext();) {
406                String checkValue = ((DepositWizardHelper) i.next()).getSelectedValue();
407    
408                if (StringUtils.isNotBlank(checkValue) && !checkValue.equals(KFSConstants.ParameterValues.NO)) {
409                    // removed apparently-unnecessary test for !checkValue.equals(KFSConstants.ParameterValues.YES)
410                    selectedIds.add(checkValue);
411                }
412            }
413    
414            if (depositIsFinal) {
415                // add check free cash receipts to the selected receipts so they are automatically deposited
416                dform.setCheckFreeCashReceipts(new ArrayList<CashReceiptDocument>());
417                for (Object crDocObj : cashReceiptService.getCashReceipts(dform.getCashDrawerCampusCode(), KFSConstants.DocumentStatusCodes.CashReceipt.VERIFIED)) {
418                    CashReceiptDocument crDoc = (CashReceiptDocument) crDocObj;
419                    if (crDoc.getCheckCount() == 0) {
420                        // it's check free; it is automatically deposited as part of the final deposit
421                        selectedIds.add(crDoc.getDocumentNumber());
422                        dform.getCheckFreeCashReceipts().add(crDoc);
423                    }
424                }
425            }
426    
427            // make a list of cashiering checks to deposit
428            List<Integer> selectedCashieringChecks = new ArrayList<Integer>();
429            for (DepositWizardCashieringCheckHelper helper : dform.getDepositWizardCashieringCheckHelpers()) {
430                if (helper.getSequenceId() != null && !helper.getSequenceId().equals(new Integer(-1))) {
431                    selectedCashieringChecks.add(helper.getSequenceId());
432                }
433            }
434    
435            if (selectedIds.isEmpty() && selectedCashieringChecks.isEmpty()) {
436                GlobalVariables.getMessageMap().putError(KFSConstants.DepositConstants.DEPOSIT_WIZARD_CASHRECEIPT_ERROR, KFSKeyConstants.Deposit.ERROR_NO_CASH_RECEIPTS_SELECTED);
437            }
438    
439            //
440            // proceed, if possible
441            if (GlobalVariables.getMessageMap().hasNoErrors()) {
442                try {
443                    // retrieve selected receipts
444                    List selectedReceipts = new ArrayList();
445                    if (selectedIds != null && !selectedIds.isEmpty()) {
446                        selectedReceipts = documentService.getDocumentsByListOfDocumentHeaderIds(CashReceiptDocument.class, selectedIds);
447                    }
448    
449                    if (depositIsFinal) {
450                        // have all verified CRs been deposited? If not, that's an error
451                        List verifiedReceipts = cashReceiptService.getCashReceipts(dform.getCashDrawerCampusCode(), KFSConstants.DocumentStatusCodes.CashReceipt.VERIFIED);
452                        for (Object o : verifiedReceipts) {
453                            CashReceiptDocument crDoc = (CashReceiptDocument) o;
454                            if (!selectedReceipts.contains(crDoc)) {
455                                GlobalVariables.getMessageMap().putError(KFSConstants.DepositConstants.DEPOSIT_WIZARD_DEPOSITHEADER_ERROR, KFSKeyConstants.Deposit.ERROR_NON_DEPOSITED_VERIFIED_CASH_RECEIPT, new String[] { crDoc.getDocumentNumber() });
456                            }
457                        }
458                        KualiDecimal toBeDepositedChecksTotal = KualiDecimal.ZERO;
459                        // have we selected the rest of the undeposited checks?
460                        for (Check check : cashManagementService.selectUndepositedCashieringChecks(dform.getCashManagementDocId())) {
461                            if (!selectedCashieringChecks.contains(check.getSequenceId())) {
462                                GlobalVariables.getMessageMap().putError(KFSConstants.DepositConstants.DEPOSIT_WIZARD_DEPOSITHEADER_ERROR, KFSKeyConstants.Deposit.ERROR_CASHIERING_CHECK_MUST_BE_DEPOSITED, new String[] { check.getCheckNumber() });
463                            }
464                            else {
465                                toBeDepositedChecksTotal = toBeDepositedChecksTotal.add(check.getAmount());
466                            }
467                        }
468    
469                        // does the cash drawer have enough currency and coin to fulfill the requested deposit?
470                        checkEnoughCurrencyForDeposit(dform);
471                        checkEnoughCoinForDeposit(dform);
472    
473                        // does this deposit have currency and coin to match all currency and coin from CRs?
474                        List<CashReceiptDocument> interestingReceipts = cashReceiptService.getCashReceipts(dform.getCashDrawerCampusCode(), new String[] { CashReceipt.VERIFIED, CashReceipt.INTERIM, CashReceipt.FINAL });
475                        CurrencyDetail currencyTotal = new CurrencyDetail();
476                        CoinDetail coinTotal = new CoinDetail();
477                        for (CashReceiptDocument receipt : interestingReceipts) {
478                            receipt.refreshCashDetails();
479                            if (receipt.getCurrencyDetail() != null) {
480                                currencyTotal.add(receipt.getCurrencyDetail());
481                            }
482                            if (receipt.getCoinDetail() != null) {
483                                coinTotal.add(receipt.getCoinDetail());
484                            }
485                        }
486    
487                        KualiDecimal cashReceiptCashTotal = currencyTotal.getTotalAmount().add(coinTotal.getTotalAmount());
488                        KualiDecimal depositedCashieringChecksTotal = cashManagementService.calculateDepositedCheckTotal(dform.getCashManagementDocId());
489                        // remove the cashiering checks amounts from the cash receipts total; cashiering checks act as if they were CR
490                        // currency/coin that gets deposited
491                        cashReceiptCashTotal = cashReceiptCashTotal.subtract(depositedCashieringChecksTotal).subtract(toBeDepositedChecksTotal);
492                        KualiDecimal depositedCashTotal = dform.getCurrencyDetail().getTotalAmount().add(dform.getCoinDetail().getTotalAmount());
493                        if (!cashReceiptCashTotal.equals(depositedCashTotal)) {
494                            GlobalVariables.getMessageMap().putError(KFSConstants.DepositConstants.DEPOSIT_WIZARD_DEPOSITHEADER_ERROR, KFSKeyConstants.Deposit.ERROR_CASH_DEPOSIT_DID_NOT_BALANCE, new String[] { formatter.format(depositedCashTotal).toString(), formatter.format(cashReceiptCashTotal).toString() });
495                        }
496                    }
497    
498                    // proceed again...if possible
499                    if (GlobalVariables.getMessageMap().hasNoErrors()) {
500                        // retrieve CashManagementDocument
501                        String cashManagementDocId = dform.getCashManagementDocId();
502                        CashManagementDocument cashManagementDoc = null;
503                        try {
504                            cashManagementDoc = (CashManagementDocument) documentService.getByDocumentHeaderId(cashManagementDocId);
505                            if (cashManagementDoc == null) {
506                                throw new IllegalStateException("unable to find cashManagementDocument with id " + cashManagementDocId);
507                            }
508                        }
509                        catch (WorkflowException e) {
510                            throw new IllegalStateException("unable to retrieve cashManagementDocument with id " + cashManagementDocId, e);
511                        }
512    
513                        // create deposit
514                        String cmDocId = dform.getCashManagementDocId();
515    
516                        cashManagementService.addDeposit(cashManagementDoc, dform.getDepositTicketNumber(), dform.getBank(), selectedReceipts, selectedCashieringChecks, depositIsFinal);
517    
518                        if (depositIsFinal) {
519                            // find the final deposit
520                            Deposit finalDeposit = findFinalDeposit(cashManagementDoc);
521                            // if the currency and coin details aren't empty, save them and remove them from the cash drawer
522                            if (dform.getCurrencyDetail() != null) {
523                                // do we have enough currency to allow the deposit to leave the drawer?
524                                boService.save(dform.getCurrencyDetail());
525                                cashManagementDoc.getCashDrawer().removeCurrency(dform.getCurrencyDetail());
526                                finalDeposit.setDepositAmount(finalDeposit.getDepositAmount().add(dform.getCurrencyDetail().getTotalAmount()));
527                            }
528                            if (dform.getCoinDetail() != null) {
529                                // do we have enough coin to allow the deposit to leave the drawer?
530                                boService.save(dform.getCoinDetail());
531                                cashManagementDoc.getCashDrawer().removeCoin(dform.getCoinDetail());
532                                finalDeposit.setDepositAmount(finalDeposit.getDepositAmount().add(dform.getCoinDetail().getTotalAmount()));
533                            }
534                            boService.save(cashManagementDoc.getCashDrawer());
535                            boService.save(finalDeposit);
536                        }
537    
538                        // redirect to controlling CashManagementDocument
539                        dest = returnToSender(cashManagementDocId);
540                    }
541                }
542                catch (WorkflowException e) {
543                    throw new InfrastructureException("unable to retrieve cashReceipts by documentId", e);
544                }
545            }
546    
547            return dest;
548        }
549    
550        private Deposit findFinalDeposit(CashManagementDocument cmDoc) {
551            Deposit finalDeposit = null;
552            for (Deposit deposit : cmDoc.getDeposits()) {
553                if (deposit.getDepositTypeCode().equals(KFSConstants.DepositConstants.DEPOSIT_TYPE_FINAL)) {
554                    finalDeposit = deposit;
555                    break;
556                }
557            }
558            return finalDeposit;
559        }
560    
561        /**
562         * Checks that the currency amount requested to be part of a deposit can be fulfilled by the amount of currency in the cash
563         * drawer
564         * 
565         * @param depositForm the deposit form we are checking against
566         * @param detail the currency detail to check against the drawer
567         * @return true if enough currency, false if otherwise
568         */
569        private boolean checkEnoughCurrencyForDeposit(DepositWizardForm depositForm) {
570            boolean success = true;
571            CurrencyDetail detail = depositForm.getCurrencyDetail();
572            if (detail != null) {
573                // 1. get the cash drawer
574                CashDrawer drawer = SpringContext.getBean(CashDrawerService.class).getByCampusCode(depositForm.getCashDrawerCampusCode());
575                // assumptions at this point:
576                // 1. a cash drawer does exist for the unit
577                // 2. we can ignore negative amounts, because if we have negative amounts, we're actually gaining money (and that will
578                // happen with cashiering checks)
579                CurrencyFormatter formatter = new CurrencyFormatter();
580                if (detail.getFinancialDocumentHundredDollarAmount() != null && detail.getFinancialDocumentHundredDollarAmount().isGreaterThan(KualiDecimal.ZERO)) {
581                    if (drawer.getFinancialDocumentHundredDollarAmount() == null || drawer.getFinancialDocumentHundredDollarAmount().isLessThan(detail.getFinancialDocumentHundredDollarAmount())) {
582                        GlobalVariables.getMessageMap().putError(KFSConstants.DepositConstants.DEPOSIT_WIZARD_DEPOSITHEADER_ERROR, KFSKeyConstants.Deposit.ERROR_NOT_ENOUGH_CASH_TO_COMPLETE_DEPOSIT, new String[] { "hundred dollar amount", formatter.format(detail.getFinancialDocumentHundredDollarAmount()).toString(), formatter.format(drawer.getFinancialDocumentHundredDollarAmount()).toString() });
583                        success = false;
584                    }
585                }
586                if (detail.getFinancialDocumentFiftyDollarAmount() != null && detail.getFinancialDocumentFiftyDollarAmount().isGreaterThan(KualiDecimal.ZERO)) {
587                    if (drawer.getFinancialDocumentFiftyDollarAmount() == null || drawer.getFinancialDocumentFiftyDollarAmount().isLessThan(detail.getFinancialDocumentFiftyDollarAmount())) {
588                        GlobalVariables.getMessageMap().putError(KFSConstants.DepositConstants.DEPOSIT_WIZARD_DEPOSITHEADER_ERROR, KFSKeyConstants.Deposit.ERROR_NOT_ENOUGH_CASH_TO_COMPLETE_DEPOSIT, new String[] { "fifty dollar amount", formatter.format(detail.getFinancialDocumentFiftyDollarAmount()).toString(), formatter.format(drawer.getFinancialDocumentFiftyDollarAmount()).toString() });
589                        success = false;
590                    }
591                }
592                if (detail.getFinancialDocumentTwentyDollarAmount() != null && detail.getFinancialDocumentTwentyDollarAmount().isGreaterThan(KualiDecimal.ZERO)) {
593                    if (drawer.getFinancialDocumentTwentyDollarAmount() == null || drawer.getFinancialDocumentTwentyDollarAmount().isLessThan(detail.getFinancialDocumentTwentyDollarAmount())) {
594                        GlobalVariables.getMessageMap().putError(KFSConstants.DepositConstants.DEPOSIT_WIZARD_DEPOSITHEADER_ERROR, KFSKeyConstants.Deposit.ERROR_NOT_ENOUGH_CASH_TO_COMPLETE_DEPOSIT, new String[] { "twenty dollar amount", formatter.format(detail.getFinancialDocumentTwentyDollarAmount()).toString(), formatter.format(drawer.getFinancialDocumentTwentyDollarAmount()).toString() });
595                        success = false;
596                    }
597                }
598                if (detail.getFinancialDocumentTenDollarAmount() != null && detail.getFinancialDocumentTenDollarAmount().isGreaterThan(KualiDecimal.ZERO)) {
599                    if (drawer.getFinancialDocumentTenDollarAmount() == null || drawer.getFinancialDocumentTenDollarAmount().isLessThan(detail.getFinancialDocumentTenDollarAmount())) {
600                        GlobalVariables.getMessageMap().putError(KFSConstants.DepositConstants.DEPOSIT_WIZARD_DEPOSITHEADER_ERROR, KFSKeyConstants.Deposit.ERROR_NOT_ENOUGH_CASH_TO_COMPLETE_DEPOSIT, new String[] { "ten dollar amount", formatter.format(detail.getFinancialDocumentTenDollarAmount()).toString(), formatter.format(drawer.getFinancialDocumentTenDollarAmount()).toString() });
601                        success = false;
602                    }
603                }
604                if (detail.getFinancialDocumentFiveDollarAmount() != null && detail.getFinancialDocumentFiveDollarAmount().isGreaterThan(KualiDecimal.ZERO)) {
605                    if (drawer.getFinancialDocumentFiveDollarAmount() == null || drawer.getFinancialDocumentFiveDollarAmount().isLessThan(detail.getFinancialDocumentFiveDollarAmount())) {
606                        GlobalVariables.getMessageMap().putError(KFSConstants.DepositConstants.DEPOSIT_WIZARD_DEPOSITHEADER_ERROR, KFSKeyConstants.Deposit.ERROR_NOT_ENOUGH_CASH_TO_COMPLETE_DEPOSIT, new String[] { "five dollar amount", formatter.format(detail.getFinancialDocumentFiveDollarAmount()).toString(), formatter.format(drawer.getFinancialDocumentFiveDollarAmount()).toString() });
607                        success = false;
608                    }
609                }
610                if (detail.getFinancialDocumentTwoDollarAmount() != null && detail.getFinancialDocumentTwoDollarAmount().isGreaterThan(KualiDecimal.ZERO)) {
611                    if (drawer.getFinancialDocumentTwoDollarAmount() == null || drawer.getFinancialDocumentTwoDollarAmount().isLessThan(detail.getFinancialDocumentTwoDollarAmount())) {
612                        GlobalVariables.getMessageMap().putError(KFSConstants.DepositConstants.DEPOSIT_WIZARD_DEPOSITHEADER_ERROR, KFSKeyConstants.Deposit.ERROR_NOT_ENOUGH_CASH_TO_COMPLETE_DEPOSIT, new String[] { "two dollar amount", formatter.format(detail.getFinancialDocumentTwoDollarAmount()).toString(), formatter.format(drawer.getFinancialDocumentTwoDollarAmount()).toString() });
613                        success = false;
614                    }
615                }
616                if (detail.getFinancialDocumentOneDollarAmount() != null && detail.getFinancialDocumentOneDollarAmount().isGreaterThan(KualiDecimal.ZERO)) {
617                    if (drawer.getFinancialDocumentOneDollarAmount() == null || drawer.getFinancialDocumentOneDollarAmount().isLessThan(detail.getFinancialDocumentOneDollarAmount())) {
618                        GlobalVariables.getMessageMap().putError(KFSConstants.DepositConstants.DEPOSIT_WIZARD_DEPOSITHEADER_ERROR, KFSKeyConstants.Deposit.ERROR_NOT_ENOUGH_CASH_TO_COMPLETE_DEPOSIT, new String[] { "one dollar amount", formatter.format(detail.getFinancialDocumentOneDollarAmount()).toString(), formatter.format(drawer.getFinancialDocumentOneDollarAmount()).toString() });
619                        success = false;
620                    }
621                }
622                if (detail.getFinancialDocumentOtherDollarAmount() != null && detail.getFinancialDocumentOtherDollarAmount().isGreaterThan(KualiDecimal.ZERO)) {
623                    if (drawer.getFinancialDocumentOtherDollarAmount() == null || drawer.getFinancialDocumentOtherDollarAmount().isLessThan(detail.getFinancialDocumentOtherDollarAmount())) {
624                        GlobalVariables.getMessageMap().putError(KFSConstants.DepositConstants.DEPOSIT_WIZARD_DEPOSITHEADER_ERROR, KFSKeyConstants.Deposit.ERROR_NOT_ENOUGH_CASH_TO_COMPLETE_DEPOSIT, new String[] { "other dollar amount", formatter.format(detail.getFinancialDocumentOtherDollarAmount()).toString(), formatter.format(drawer.getFinancialDocumentOtherDollarAmount()).toString() });
625                        success = false;
626                    }
627                }
628            }
629            return success;
630        }
631    
632        /**
633         * Checks that the coin amount requested by the deposit does not exceed the amount actually in the drawer
634         * 
635         * @param depositForm the deposit form we are checking against
636         * @param detail the coin detail to check against the drawer
637         * @return true if there is enough coin, false if otherwise
638         */
639        public boolean checkEnoughCoinForDeposit(DepositWizardForm depositForm) {
640            boolean success = true;
641            CoinDetail detail = depositForm.getCoinDetail();
642            if (detail != null) {
643                // 1. get the cash drawer
644                CashDrawer drawer = SpringContext.getBean(CashDrawerService.class).getByCampusCode(depositForm.getCashDrawerCampusCode());
645                // assumptions at this point:
646                // 1. a cash drawer does exist for the unit
647                // 2. we can ignore negative amounts, because if we have negative amounts, we're actually gaining money (and that will
648                // happen with cashiering checks)
649                CurrencyFormatter formatter = new CurrencyFormatter();
650                if (detail.getFinancialDocumentHundredCentAmount() != null && detail.getFinancialDocumentHundredCentAmount().isGreaterThan(KualiDecimal.ZERO)) {
651                    if (drawer.getFinancialDocumentHundredCentAmount() == null || drawer.getFinancialDocumentHundredCentAmount().isLessThan(detail.getFinancialDocumentHundredCentAmount())) {
652                        GlobalVariables.getMessageMap().putError(KFSConstants.DepositConstants.DEPOSIT_WIZARD_DEPOSITHEADER_ERROR, KFSKeyConstants.Deposit.ERROR_NOT_ENOUGH_CASH_TO_COMPLETE_DEPOSIT, new String[] { "hundred cent amount", formatter.format(detail.getFinancialDocumentHundredCentAmount()).toString(), formatter.format(drawer.getFinancialDocumentHundredCentAmount()).toString() });
653                        success = false;
654                    }
655                }
656                if (detail.getFinancialDocumentFiftyCentAmount() != null && detail.getFinancialDocumentFiftyCentAmount().isGreaterThan(KualiDecimal.ZERO)) {
657                    if (drawer.getFinancialDocumentFiftyCentAmount() == null || drawer.getFinancialDocumentFiftyCentAmount().isLessThan(detail.getFinancialDocumentFiftyCentAmount())) {
658                        GlobalVariables.getMessageMap().putError(KFSConstants.DepositConstants.DEPOSIT_WIZARD_DEPOSITHEADER_ERROR, KFSKeyConstants.Deposit.ERROR_NOT_ENOUGH_CASH_TO_COMPLETE_DEPOSIT, new String[] { "fifty cent amount", formatter.format(detail.getFinancialDocumentFiftyCentAmount()).toString(), formatter.format(drawer.getFinancialDocumentFiftyCentAmount()).toString() });
659                        success = false;
660                    }
661                }
662                if (detail.getFinancialDocumentTwentyFiveCentAmount() != null && detail.getFinancialDocumentTwentyFiveCentAmount().isGreaterThan(KualiDecimal.ZERO)) {
663                    if (drawer.getFinancialDocumentTwentyFiveCentAmount() == null || drawer.getFinancialDocumentTwentyFiveCentAmount().isLessThan(detail.getFinancialDocumentTwentyFiveCentAmount())) {
664                        GlobalVariables.getMessageMap().putError(KFSConstants.DepositConstants.DEPOSIT_WIZARD_DEPOSITHEADER_ERROR, KFSKeyConstants.Deposit.ERROR_NOT_ENOUGH_CASH_TO_COMPLETE_DEPOSIT, new String[] { "twenty five cent amount", formatter.format(detail.getFinancialDocumentTwentyFiveCentAmount()).toString(), formatter.format(drawer.getFinancialDocumentTwentyFiveCentAmount()).toString() });
665                        success = false;
666                    }
667                }
668                if (detail.getFinancialDocumentTenCentAmount() != null && detail.getFinancialDocumentTenCentAmount().isGreaterThan(KualiDecimal.ZERO)) {
669                    if (drawer.getFinancialDocumentTenCentAmount() == null || drawer.getFinancialDocumentTenCentAmount().isLessThan(detail.getFinancialDocumentTenCentAmount())) {
670                        GlobalVariables.getMessageMap().putError(KFSConstants.DepositConstants.DEPOSIT_WIZARD_DEPOSITHEADER_ERROR, KFSKeyConstants.Deposit.ERROR_NOT_ENOUGH_CASH_TO_COMPLETE_DEPOSIT, new String[] { "ten cent amount", formatter.format(detail.getFinancialDocumentTenCentAmount()).toString(), formatter.format(drawer.getFinancialDocumentTenCentAmount()).toString() });
671                        success = false;
672                    }
673                }
674                if (detail.getFinancialDocumentFiveCentAmount() != null && detail.getFinancialDocumentFiveCentAmount().isGreaterThan(KualiDecimal.ZERO)) {
675                    if (drawer.getFinancialDocumentFiveCentAmount() == null || drawer.getFinancialDocumentFiveCentAmount().isLessThan(detail.getFinancialDocumentFiveCentAmount())) {
676                        GlobalVariables.getMessageMap().putError(KFSConstants.DepositConstants.DEPOSIT_WIZARD_DEPOSITHEADER_ERROR, KFSKeyConstants.Deposit.ERROR_NOT_ENOUGH_CASH_TO_COMPLETE_DEPOSIT, new String[] { "five cent amount", formatter.format(detail.getFinancialDocumentFiveCentAmount()).toString(), formatter.format(drawer.getFinancialDocumentFiveCentAmount()).toString() });
677                        success = false;
678                    }
679                }
680                if (detail.getFinancialDocumentOneCentAmount() != null && detail.getFinancialDocumentOneCentAmount().isGreaterThan(KualiDecimal.ZERO)) {
681                    if (drawer.getFinancialDocumentOneCentAmount() == null || drawer.getFinancialDocumentOneCentAmount().isLessThan(detail.getFinancialDocumentOneCentAmount())) {
682                        GlobalVariables.getMessageMap().putError(KFSConstants.DepositConstants.DEPOSIT_WIZARD_DEPOSITHEADER_ERROR, KFSKeyConstants.Deposit.ERROR_NOT_ENOUGH_CASH_TO_COMPLETE_DEPOSIT, new String[] { "one cent amount", formatter.format(detail.getFinancialDocumentOneCentAmount()).toString(), formatter.format(drawer.getFinancialDocumentOneCentAmount()).toString() });
683                        success = false;
684                    }
685                }
686                if (detail.getFinancialDocumentOtherCentAmount() != null && detail.getFinancialDocumentOtherCentAmount().isGreaterThan(KualiDecimal.ZERO)) {
687                    if (drawer.getFinancialDocumentOtherCentAmount() == null || drawer.getFinancialDocumentOtherCentAmount().isLessThan(detail.getFinancialDocumentOtherCentAmount())) {
688                        GlobalVariables.getMessageMap().putError(KFSConstants.DepositConstants.DEPOSIT_WIZARD_DEPOSITHEADER_ERROR, KFSKeyConstants.Deposit.ERROR_NOT_ENOUGH_CASH_TO_COMPLETE_DEPOSIT, new String[] { "other cent amount", formatter.format(detail.getFinancialDocumentOtherCentAmount()).toString(), formatter.format(drawer.getFinancialDocumentOtherCentAmount()).toString() });
689                        success = false;
690                    }
691                }
692            }
693            return success;
694        }
695    
696    
697        /**
698         * This method handles canceling (closing) the deposit wizard.
699         * 
700         * @param mapping
701         * @param form
702         * @param request
703         * @param response
704         * @return ActionForward
705         * @throws Exception
706         */
707        public ActionForward cancel(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
708            DepositWizardForm dform = (DepositWizardForm) form;
709    
710            ActionForward dest = returnToSender(dform.getCashManagementDocId());
711            return dest;
712        }
713    
714    
715        /**
716         * @param cmDocId
717         * @return ActionForward which will redirect the user to the docSearchDisplay for the CashManagementDocument with the given
718         *         documentId
719         */
720        private ActionForward returnToSender(String cmDocId) {
721            Properties params = new Properties();
722            params.setProperty("methodToCall", "docHandler");
723            params.setProperty("command", "displayDocSearchView");
724            params.setProperty("docId", cmDocId);
725    
726            String cmActionUrl = UrlFactory.parameterizeUrl(KFSConstants.CASH_MANAGEMENT_DOCUMENT_ACTION, params);
727    
728            return new ActionForward(cmActionUrl, true);
729        }
730    }
731