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