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.Map;
019    import java.util.Properties;
020    
021    import javax.servlet.http.HttpServletRequest;
022    import javax.servlet.http.HttpServletResponse;
023    
024    import org.apache.commons.lang.StringUtils;
025    import org.apache.log4j.Logger;
026    import org.apache.struts.action.ActionForm;
027    import org.apache.struts.action.ActionForward;
028    import org.apache.struts.action.ActionMapping;
029    import org.kuali.kfs.fp.businessobject.Check;
030    import org.kuali.kfs.fp.businessobject.Deposit;
031    import org.kuali.kfs.fp.document.CashManagementDocument;
032    import org.kuali.kfs.fp.document.service.CashManagementService;
033    import org.kuali.kfs.fp.document.service.CashReceiptService;
034    import org.kuali.kfs.fp.document.validation.event.AddCheckEvent;
035    import org.kuali.kfs.fp.document.validation.event.CashieringTransactionApplicationEventBase;
036    import org.kuali.kfs.fp.document.validation.event.DeleteCheckEvent;
037    import org.kuali.kfs.fp.document.web.struts.CashManagementForm.CashDrawerSummary;
038    import org.kuali.kfs.fp.exception.CashDrawerStateException;
039    import org.kuali.kfs.fp.service.CashDrawerService;
040    import org.kuali.kfs.sys.KFSConstants;
041    import org.kuali.kfs.sys.KFSKeyConstants;
042    import org.kuali.kfs.sys.KfsAuthorizationConstants;
043    import org.kuali.kfs.sys.KFSConstants.CashDrawerConstants;
044    import org.kuali.kfs.sys.KFSConstants.DepositConstants;
045    import org.kuali.kfs.sys.KFSKeyConstants.CashManagement;
046    import org.kuali.kfs.sys.context.SpringContext;
047    import org.kuali.rice.kew.exception.WorkflowException;
048    import org.kuali.rice.kim.bo.Person;
049    import org.kuali.rice.kns.service.DocumentService;
050    import org.kuali.rice.kns.service.KualiConfigurationService;
051    import org.kuali.rice.kns.service.KualiRuleService;
052    import org.kuali.rice.kns.util.GlobalVariables;
053    import org.kuali.rice.kns.util.UrlFactory;
054    import org.kuali.rice.kns.web.struts.action.KualiTransactionalDocumentActionBase;
055    import org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase;
056    import org.kuali.rice.kns.workflow.service.KualiWorkflowDocument;
057    
058    /**
059     * Action class for CashManagementForm
060     */
061    public class CashManagementAction extends KualiTransactionalDocumentActionBase {
062        protected static Logger LOG = Logger.getLogger(CashManagementAction.class);
063        protected static final String CASH_MANAGEMENT_STATUS_PAGE = "/cashManagementStatus.do";
064    
065        /**
066         * Default constructor
067         */
068        public CashManagementAction() {
069        }
070    
071    
072        /**
073         * Overrides to call super, but also make sure the helpers are populated.
074         * 
075         * @see org.kuali.rice.kns.web.struts.action.KualiDocumentActionBase#execute(org.apache.struts.action.ActionMapping,
076         *      org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
077         */
078        @Override
079        public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
080            ActionForward dest = null;
081            
082            try {
083                dest = super.execute(mapping, form, request, response);
084    
085                CashManagementForm cmf = (CashManagementForm) form;
086                cmf.populateDepositHelpers();
087                KualiWorkflowDocument kwd = cmf.getDocument().getDocumentHeader().getWorkflowDocument();
088                if (kwd.stateIsEnroute() || kwd.stateIsFinal()) {
089                    cmf.setCashDrawerSummary(null);
090                }
091                else {
092                    if (cmf.getCashDrawerSummary() == null) {
093                        cmf.populateCashDrawerSummary();
094                    }
095                }
096                // put any recently closed items in process in the form
097                cmf.setRecentlyClosedItemsInProcess(SpringContext.getBean(CashManagementService.class).getRecentlyClosedItemsInProcess(cmf.getCashManagementDocument()));
098            } catch (CashDrawerStateException cdse) {
099                dest = new ActionForward(UrlFactory.parameterizeUrl(CASH_MANAGEMENT_STATUS_PAGE, cdse.toProperties()), true);
100            }
101            
102            return dest;
103        }
104    
105        /**
106         * Overrides the default document-creation code to auto-save new documents upon creation: since creating a CMDoc changes the
107         * CashDrawer's state as a side-effect, we need all CMDocs to be docsearchable so that someone can relocate and use or cancel
108         * whatever the current CMDoc is.
109         * 
110         * @param kualiDocumentFormBase
111         * @throws WorkflowException
112         */
113        @Override
114        protected void createDocument(KualiDocumentFormBase kualiDocumentFormBase) throws WorkflowException {
115            Person user = GlobalVariables.getUserSession().getPerson();
116            String campusCode = SpringContext.getBean(CashReceiptService.class).getCashReceiptVerificationUnitForUser(user);
117    
118            String defaultDescription = SpringContext.getBean(KualiConfigurationService.class).getPropertyString(CashManagement.DEFAULT_DOCUMENT_DESCRIPTION);
119            defaultDescription = StringUtils.replace(defaultDescription, "{0}", campusCode);
120            defaultDescription = StringUtils.substring(defaultDescription, 0, 39);
121    
122            // create doc
123            CashManagementDocument cmDoc = SpringContext.getBean(CashManagementService.class).createCashManagementDocument(campusCode, defaultDescription, null);
124    
125            // update form
126            kualiDocumentFormBase.setDocument(cmDoc);
127            kualiDocumentFormBase.setDocTypeName(cmDoc.getDocumentHeader().getWorkflowDocument().getDocumentType());
128        }
129    
130        /**
131         * @param mapping
132         * @param form
133         * @param request
134         * @param response
135         * @return ActionForward
136         * @throws Exception
137         */
138        public ActionForward addInterimDeposit(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
139            CashManagementForm cmForm = (CashManagementForm) form;
140            CashManagementDocument cmDoc = cmForm.getCashManagementDocument();
141    
142            checkDepositAuthorization(cmForm, cmDoc);
143    
144            String wizardUrl = buildDepositWizardUrl(cmDoc, DepositConstants.DEPOSIT_TYPE_INTERIM);
145            return new ActionForward(wizardUrl, true);
146        }
147    
148        /**
149         * @param mapping
150         * @param form
151         * @param request
152         * @param response
153         * @return ActionForward
154         * @throws Exception
155         */
156        public ActionForward addFinalDeposit(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
157            CashManagementForm cmForm = (CashManagementForm) form;
158            CashManagementDocument cmDoc = cmForm.getCashManagementDocument();
159    
160            checkDepositAuthorization(cmForm, cmDoc);
161    
162            String wizardUrl = buildDepositWizardUrl(cmDoc, DepositConstants.DEPOSIT_TYPE_FINAL);
163            return new ActionForward(wizardUrl, true);
164        }
165    
166        /**
167         * Throws a DocumentAuthorizationException if the current user is not authorized to add a deposit of the given type to the given
168         * document.
169         * 
170         * @param cmDoc
171         * @param cmForm
172         */
173        protected void checkDepositAuthorization(CashManagementForm cmForm, CashManagementDocument cmDoc) {
174            //deposits can only be added if the CashDrawer is open
175            if (!cmDoc.getCashDrawerStatus().equals(CashDrawerConstants.STATUS_OPEN)) {
176                throw new IllegalStateException("CashDrawer '" + cmDoc.getCampusCode() + "' must be open for deposits to be made");
177            }
178            
179            //verify user's ability to add a deposit
180            Map<String, String> documentActions = cmForm.getEditingMode();
181            if (!documentActions.containsKey(KfsAuthorizationConstants.CashManagementEditMode.ALLOW_ADDITIONAL_DEPOSITS)) {
182                throw buildAuthorizationException("add a deposit", cmDoc);
183            }
184        }
185    
186        /**
187         * @param cmDoc
188         * @param depositTypeCode
189         * @return URL for passing control to the DepositWizard
190         */
191        protected String buildDepositWizardUrl(CashManagementDocument cmDoc, String depositTypeCode) {
192            Properties params = new Properties();
193            params.setProperty("methodToCall", "startWizard");
194            params.setProperty("cmDocId", cmDoc.getDocumentNumber());
195            params.setProperty("depositTypeCode", depositTypeCode);
196    
197            String wizardActionUrl = UrlFactory.parameterizeUrl("depositWizard.do", params);
198            return wizardActionUrl;
199        }
200    
201    
202        /**
203         * @param mapping
204         * @param form
205         * @param request
206         * @param response
207         * @return ActionForward
208         * @throws Exception
209         */
210        public ActionForward cancelDeposit(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
211            CashManagementForm cmForm = (CashManagementForm) form;
212            CashManagementDocument cmDoc = cmForm.getCashManagementDocument();
213    
214            // validate cancelability
215            int depositIndex = getSelectedLine(request);
216            Deposit deposit = cmDoc.getDeposit(depositIndex);
217            if (StringUtils.equals(deposit.getDepositTypeCode(), DepositConstants.DEPOSIT_TYPE_INTERIM) && cmDoc.hasFinalDeposit()) {
218                throw new IllegalStateException("interim deposits cannot be canceled if the document already has a final deposit");
219            }
220    
221            // cancel the deposit
222            deposit = cmDoc.removeDeposit(depositIndex);
223            SpringContext.getBean(CashManagementService.class).cancelDeposit(deposit);
224    
225            // update the form
226            cmForm.removeDepositHelper(depositIndex);
227    
228            // open the CashDrawer so that user can add new deposits
229            cmDoc.getCashDrawer().setStatusCode(KFSConstants.CashDrawerConstants.STATUS_OPEN);
230    
231            // display status message
232            GlobalVariables.getMessageList().add(CashManagement.STATUS_DEPOSIT_CANCELED);
233            
234            ((CashManagementForm) form).getCashDrawerSummary().resummarize(cmDoc);
235    
236            return mapping.findForward(KFSConstants.MAPPING_BASIC);
237        }
238    
239    
240        /**
241         * @see org.kuali.rice.kns.web.struts.action.KualiDocumentActionBase#reload(org.apache.struts.action.ActionMapping,
242         *      org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
243         */
244        @Override
245        public ActionForward reload(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
246            ActionForward dest = super.reload(mapping, form, request, response);
247    
248            // refresh the CashDrawerSummary, just in case
249            CashManagementForm cmForm = (CashManagementForm) form;
250            CashManagementDocument cmDoc = cmForm.getCashManagementDocument();
251    
252            CashDrawerSummary cms = cmForm.getCashDrawerSummary();
253            if (cms != null) {
254                cms.resummarize(cmDoc);
255            }
256    
257            return dest;
258        }
259    
260    
261        /**
262         * @param mapping
263         * @param form
264         * @param request
265         * @param response
266         * @return ActionForward
267         * @throws Exception
268         */
269        public ActionForward refreshSummary(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
270            CashManagementForm cmForm = (CashManagementForm) form;
271            CashManagementDocument cmDoc = cmForm.getCashManagementDocument();
272            
273            if (cmForm.getCashDrawerSummary() != null) {
274                cmForm.getCashDrawerSummary().resummarize(cmDoc);
275            }
276    
277            return mapping.findForward(KFSConstants.MAPPING_BASIC);
278        }
279    
280    
281        /**
282         * Saves the document, then opens the cash drawer
283         * 
284         * @param mapping
285         * @param form
286         * @param request
287         * @param response
288         * @return
289         * @throws Exception
290         */
291        public ActionForward openCashDrawer(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
292            CashManagementForm cmForm = (CashManagementForm) form;
293            CashManagementDocument cmDoc = cmForm.getCashManagementDocument();
294    
295            if (!cmDoc.getDocumentHeader().getWorkflowDocument().stateIsInitiated()) {
296                throw new IllegalStateException("openCashDrawer should only be called on documents which haven't yet been saved");
297            }
298    
299            // open the CashDrawer
300            CashDrawerService cds = SpringContext.getBean(CashDrawerService.class);
301            cds.openCashDrawer(cmDoc.getCashDrawer(), cmDoc.getDocumentNumber());
302            // now that the cash drawer is open, let's create currency/coin detail records for this document
303            // create and save the cumulative cash receipt, deposit, money in and money out curr/coin details
304            SpringContext.getBean(CashManagementService.class).createNewCashDetails(cmDoc, KFSConstants.CurrencyCoinSources.CASH_RECEIPTS);
305            SpringContext.getBean(CashManagementService.class).createNewCashDetails(cmDoc, KFSConstants.CurrencyCoinSources.CASH_MANAGEMENT_IN);
306            SpringContext.getBean(CashManagementService.class).createNewCashDetails(cmDoc, KFSConstants.CurrencyCoinSources.CASH_MANAGEMENT_OUT);
307            try {
308                SpringContext.getBean(DocumentService.class).saveDocument(cmDoc);
309            }
310            catch (WorkflowException e) {
311                // force it closed if workflow proves recalcitrant
312                cds.closeCashDrawer(cmDoc.getCashDrawer());
313                throw e;
314            }
315    
316            // update the CashDrawerSummary to reflect the change
317            cmForm.populateCashDrawerSummary();
318    
319            return mapping.findForward(KFSConstants.MAPPING_BASIC);
320        }
321    
322        /**
323         * This action makes the last interim deposit a final deposit
324         * 
325         * @param mapping the mapping of the actions
326         * @param form the Struts form populated on the post
327         * @param request the servlet request
328         * @param response the servlet response
329         * @return a forward to the same page we were on
330         * @throws Exception because you never know when something just might go wrong
331         */
332        public ActionForward finalizeLastInterimDeposit(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
333            CashManagementDocument cmDoc = ((CashManagementForm) form).getCashManagementDocument();
334            CashManagementService cms = SpringContext.getBean(CashManagementService.class);
335    
336            if (cmDoc.hasFinalDeposit()) {
337                GlobalVariables.getMessageMap().putError(KFSConstants.CASH_MANAGEMENT_DEPOSIT_ERRORS, CashManagement.ERROR_DOCUMENT_ALREADY_HAS_FINAL_DEPOSIT, new String[] {});
338            }
339            else if (cmDoc.getDeposits().size() == 0) {
340                GlobalVariables.getMessageMap().putError(KFSConstants.CASH_MANAGEMENT_DEPOSIT_ERRORS, CashManagement.ERROR_DOCUMENT_NO_DEPOSITS_TO_MAKE_FINAL, new String[] {});
341            }
342            else if (!cms.allVerifiedCashReceiptsAreDeposited(cmDoc)) {
343                GlobalVariables.getMessageMap().putError(KFSConstants.CASH_MANAGEMENT_DEPOSIT_ERRORS, CashManagement.ERROR_NON_DEPOSITED_VERIFIED_CASH_RECEIPTS, new String[] {});
344            }
345    
346            cms.finalizeLastInterimDeposit(cmDoc);
347    
348            ((CashManagementForm) form).getCashDrawerSummary().resummarize(cmDoc);
349    
350            return mapping.findForward(KFSConstants.MAPPING_BASIC);
351        }
352    
353        /**
354         * This action applies the current cashiering transaction to the cash drawer
355         * 
356         * @param mapping
357         * @param form
358         * @param request
359         * @param response
360         * @return
361         * @throws Exception
362         */
363        public ActionForward applyCashieringTransaction(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
364            CashManagementDocument cmDoc = ((CashManagementForm) form).getCashManagementDocument();
365            CashManagementService cmService = SpringContext.getBean(CashManagementService.class);
366            
367            final boolean valid = SpringContext.getBean(KualiRuleService.class).applyRules(new CashieringTransactionApplicationEventBase("Cashiering Transaction Application Event", "", cmDoc, SpringContext.getBean(CashDrawerService.class).getByCampusCode(cmDoc.getCampusCode()), cmDoc.getCurrentTransaction()));
368    
369            if (valid) {
370                cmService.applyCashieringTransaction(cmDoc);
371    
372                ((CashManagementForm) form).getCashDrawerSummary().resummarize(cmDoc);
373            }
374    
375            return mapping.findForward(KFSConstants.MAPPING_BASIC);
376        }
377    
378        /**
379         * This action allows the user to go to the cash drawer correction screen
380         * 
381         * @param mapping
382         * @param form
383         * @param request
384         * @param response
385         * @return
386         * @throws Exception
387         */
388        public ActionForward correctCashDrawer(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
389            return new ActionForward("CashDrawerCorrectionForm", buildCashDrawerCorrectionUrl(((CashManagementForm) form).getCashManagementDocument()), true);
390        }
391    
392        /**
393         * @param cmDoc
394         * @param depositTypeCode
395         * @return URL for passing control to the DepositWizard
396         */
397        protected String buildCashDrawerCorrectionUrl(CashManagementDocument cmDoc) {
398            Properties params = new Properties();
399            params.setProperty("methodToCall", "startCorrections");
400            params.setProperty("campusCode", cmDoc.getCampusCode());
401    
402            return UrlFactory.parameterizeUrl("cashDrawerCorrection.do", params);
403        }
404    
405        /**
406         * Adds Check instance created from the current "new check" line to the document
407         * 
408         * @param mapping
409         * @param form
410         * @param request
411         * @param response
412         * @return ActionForward
413         * @throws Exception
414         */
415        public ActionForward addCheck(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
416            CashManagementDocument cmDoc = ((CashManagementForm) form).getCashManagementDocument();
417    
418            Check newCheck = cmDoc.getCurrentTransaction().getNewCheck();
419            newCheck.setDocumentNumber(cmDoc.getDocumentNumber());
420    
421            // check business rules
422            boolean rulePassed = SpringContext.getBean(KualiRuleService.class).applyRules(new AddCheckEvent(KFSConstants.NEW_CHECK_PROPERTY_NAME, cmDoc, newCheck));
423            if (rulePassed) {
424                // add check
425                cmDoc.getCurrentTransaction().addCheck(newCheck);
426    
427                // clear the used newCheck
428                cmDoc.getCurrentTransaction().setNewCheck(cmDoc.getCurrentTransaction().createNewCheck());
429    
430            }
431    
432            return mapping.findForward(KFSConstants.MAPPING_BASIC);
433        }
434    
435        /**
436         * Deletes the selected check (line) from the document
437         * 
438         * @param mapping
439         * @param form
440         * @param request
441         * @param response
442         * @return ActionForward
443         * @throws Exception
444         */
445        public ActionForward deleteCheck(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
446            CashManagementDocument cmDoc = ((CashManagementForm) form).getCashManagementDocument();
447    
448            int deleteIndex = getLineToDelete(request);
449            Check oldCheck = cmDoc.getCurrentTransaction().getCheck(deleteIndex);
450    
451    
452            boolean rulePassed = SpringContext.getBean(KualiRuleService.class).applyRules(new DeleteCheckEvent(KFSConstants.EXISTING_CHECK_PROPERTY_NAME, cmDoc, oldCheck));
453    
454            if (rulePassed) {
455                // delete check
456                cmDoc.getCurrentTransaction().removeCheck(deleteIndex);
457    
458                // delete baseline check, if any
459                if (cmDoc.getCurrentTransaction().hasBaselineCheck(deleteIndex)) {
460                    cmDoc.getCurrentTransaction().getBaselineChecks().remove(deleteIndex);
461                }
462    
463            }
464            else {
465                GlobalVariables.getMessageMap().putError("document.currentTransaction.check[" + deleteIndex + "]", KFSKeyConstants.Check.ERROR_CHECK_DELETERULE, Integer.toString(deleteIndex));
466            }
467    
468            return mapping.findForward(KFSConstants.MAPPING_BASIC);
469        }
470    
471        /**
472         * Overridden to clear the CashDrawerSummary info
473         * 
474         * @see org.kuali.rice.kns.web.struts.action.KualiDocumentActionBase#route(org.apache.struts.action.ActionMapping,
475         *      org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
476         */
477        @Override
478        public ActionForward route(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
479            CashManagementForm cmForm = (CashManagementForm) form;
480            CashManagementDocument cmDoc = cmForm.getCashManagementDocument();
481    
482            ActionForward dest = super.route(mapping, form, request, response);
483    
484            // clear the CashDrawerSummary
485            cmForm.setCashDrawerSummary(null);
486    
487            return dest;
488        }
489    }
490