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