/*
* The Kuali Financial System, a comprehensive financial management system for higher education.
*
* Copyright 2005-2014 The Kuali Foundation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.kuali.kfs.fp.document.web.struts;
import java.util.Map;
import java.util.Properties;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.kuali.kfs.fp.businessobject.Check;
import org.kuali.kfs.fp.businessobject.Deposit;
import org.kuali.kfs.fp.document.CashManagementDocument;
import org.kuali.kfs.fp.document.service.CashManagementService;
import org.kuali.kfs.fp.document.service.CashReceiptService;
import org.kuali.kfs.fp.document.validation.event.AddCheckEvent;
import org.kuali.kfs.fp.document.validation.event.CashieringTransactionApplicationEventBase;
import org.kuali.kfs.fp.document.validation.event.DeleteCheckEvent;
import org.kuali.kfs.fp.document.web.struts.CashManagementForm.CashDrawerSummary;
import org.kuali.kfs.fp.exception.CashDrawerStateException;
import org.kuali.kfs.fp.service.CashDrawerService;
import org.kuali.kfs.sys.KFSConstants;
import org.kuali.kfs.sys.KFSConstants.CashDrawerConstants;
import org.kuali.kfs.sys.KFSConstants.DepositConstants;
import org.kuali.kfs.sys.KFSKeyConstants;
import org.kuali.kfs.sys.KFSKeyConstants.CashManagement;
import org.kuali.kfs.sys.KfsAuthorizationConstants;
import org.kuali.kfs.sys.context.SpringContext;
import org.kuali.rice.core.api.config.property.ConfigurationService;
import org.kuali.rice.kew.api.WorkflowDocument;
import org.kuali.rice.kew.api.exception.WorkflowException;
import org.kuali.rice.kim.api.identity.Person;
import org.kuali.rice.kns.util.KNSGlobalVariables;
import org.kuali.rice.kns.web.struts.action.KualiTransactionalDocumentActionBase;
import org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase;
import org.kuali.rice.krad.service.DocumentService;
import org.kuali.rice.krad.service.KualiRuleService;
import org.kuali.rice.krad.util.GlobalVariables;
import org.kuali.rice.krad.util.UrlFactory;
/**
* Action class for CashManagementForm
*/
public class CashManagementAction extends KualiTransactionalDocumentActionBase {
protected static Logger LOG = Logger.getLogger(CashManagementAction.class);
protected static final String CASH_MANAGEMENT_STATUS_PAGE = "/cashManagementStatus.do";
/**
* Default constructor
*/
public CashManagementAction() {
}
/**
* Overrides to call super, but also make sure the helpers are populated.
*
* @see org.kuali.rice.kns.web.struts.action.KualiDocumentActionBase#execute(org.apache.struts.action.ActionMapping,
* org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
@Override
public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
ActionForward dest = null;
try {
dest = super.execute(mapping, form, request, response);
CashManagementForm cmf = (CashManagementForm) form;
cmf.populateDepositHelpers();
WorkflowDocument kwd = cmf.getDocument().getDocumentHeader().getWorkflowDocument();
if (kwd.isEnroute() || kwd.isFinal()) {
cmf.setCashDrawerSummary(null);
}
else {
if (cmf.getCashDrawerSummary() == null) {
cmf.populateCashDrawerSummary();
}
}
// put any recently closed items in process in the form
cmf.setRecentlyClosedItemsInProcess(SpringContext.getBean(CashManagementService.class).getRecentlyClosedItemsInProcess(cmf.getCashManagementDocument()));
} catch (CashDrawerStateException cdse) {
dest = new ActionForward(UrlFactory.parameterizeUrl(CASH_MANAGEMENT_STATUS_PAGE, cdse.toProperties()), true);
}
return dest;
}
/**
* Overrides the default document-creation code to auto-save new documents upon creation: since creating a CMDoc changes the
* CashDrawer's state as a side-effect, we need all CMDocs to be docsearchable so that someone can relocate and use or cancel
* whatever the current CMDoc is.
*
* @param kualiDocumentFormBase
* @throws WorkflowException
*/
@Override
protected void createDocument(KualiDocumentFormBase kualiDocumentFormBase) throws WorkflowException {
Person user = GlobalVariables.getUserSession().getPerson();
String campusCode = SpringContext.getBean(CashReceiptService.class).getCashReceiptVerificationUnitForUser(user);
String defaultDescription = SpringContext.getBean(ConfigurationService.class).getPropertyValueAsString(CashManagement.DEFAULT_DOCUMENT_DESCRIPTION);
defaultDescription = StringUtils.replace(defaultDescription, "{0}", campusCode);
defaultDescription = StringUtils.substring(defaultDescription, 0, 39);
// create doc
CashManagementDocument cmDoc = SpringContext.getBean(CashManagementService.class).createCashManagementDocument(campusCode, defaultDescription, null);
// update form
kualiDocumentFormBase.setDocument(cmDoc);
kualiDocumentFormBase.setDocTypeName(cmDoc.getDocumentHeader().getWorkflowDocument().getDocumentTypeName());
}
/**
* @param mapping
* @param form
* @param request
* @param response
* @return ActionForward
* @throws Exception
*/
public ActionForward addInterimDeposit(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
CashManagementForm cmForm = (CashManagementForm) form;
CashManagementDocument cmDoc = cmForm.getCashManagementDocument();
checkDepositAuthorization(cmForm, cmDoc);
String wizardUrl = buildDepositWizardUrl(cmDoc, DepositConstants.DEPOSIT_TYPE_INTERIM);
return new ActionForward(wizardUrl, true);
}
/**
* @param mapping
* @param form
* @param request
* @param response
* @return ActionForward
* @throws Exception
*/
public ActionForward addFinalDeposit(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
CashManagementForm cmForm = (CashManagementForm) form;
CashManagementDocument cmDoc = cmForm.getCashManagementDocument();
checkDepositAuthorization(cmForm, cmDoc);
String wizardUrl = buildDepositWizardUrl(cmDoc, DepositConstants.DEPOSIT_TYPE_FINAL);
return new ActionForward(wizardUrl, true);
}
/**
* Throws a DocumentAuthorizationException if the current user is not authorized to add a deposit of the given type to the given
* document.
*
* @param cmDoc
* @param cmForm
*/
protected void checkDepositAuthorization(CashManagementForm cmForm, CashManagementDocument cmDoc) {
//deposits can only be added if the CashDrawer is open
if (!cmDoc.getCashDrawerStatus().equals(CashDrawerConstants.STATUS_OPEN)) {
throw new IllegalStateException("CashDrawer '" + cmDoc.getCampusCode() + "' must be open for deposits to be made");
}
//verify user's ability to add a deposit
Map<String, String> documentActions = cmForm.getEditingMode();
if (!documentActions.containsKey(KfsAuthorizationConstants.CashManagementEditMode.ALLOW_ADDITIONAL_DEPOSITS)) {
throw buildAuthorizationException("add a deposit", cmDoc);
}
}
/**
* @param cmDoc
* @param depositTypeCode
* @return URL for passing control to the DepositWizard
*/
protected String buildDepositWizardUrl(CashManagementDocument cmDoc, String depositTypeCode) {
Properties params = new Properties();
params.setProperty("methodToCall", "startWizard");
params.setProperty("cmDocId", cmDoc.getDocumentNumber());
params.setProperty("depositTypeCode", depositTypeCode);
String wizardActionUrl = UrlFactory.parameterizeUrl("depositWizard.do", params);
return wizardActionUrl;
}
/**
* @param mapping
* @param form
* @param request
* @param response
* @return ActionForward
* @throws Exception
*/
public ActionForward cancelDeposit(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
CashManagementForm cmForm = (CashManagementForm) form;
CashManagementDocument cmDoc = cmForm.getCashManagementDocument();
// validate cancelability
int depositIndex = getSelectedLine(request);
Deposit deposit = cmDoc.getDeposit(depositIndex);
if (StringUtils.equals(deposit.getDepositTypeCode(), DepositConstants.DEPOSIT_TYPE_INTERIM) && cmDoc.hasFinalDeposit()) {
throw new IllegalStateException("interim deposits cannot be canceled if the document already has a final deposit");
}
// cancel the deposit
deposit = cmDoc.removeDeposit(depositIndex);
SpringContext.getBean(CashManagementService.class).cancelDeposit(deposit);
// update the form
cmForm.removeDepositHelper(depositIndex);
// open the CashDrawer so that user can add new deposits
cmDoc.getCashDrawer().setStatusCode(KFSConstants.CashDrawerConstants.STATUS_OPEN);
// display status message
KNSGlobalVariables.getMessageList().add(CashManagement.STATUS_DEPOSIT_CANCELED);
((CashManagementForm) form).getCashDrawerSummary().resummarize(cmDoc);
return mapping.findForward(KFSConstants.MAPPING_BASIC);
}
/**
* @see org.kuali.rice.kns.web.struts.action.KualiDocumentActionBase#reload(org.apache.struts.action.ActionMapping,
* org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
@Override
public ActionForward reload(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
ActionForward dest = super.reload(mapping, form, request, response);
// refresh the CashDrawerSummary, just in case
CashManagementForm cmForm = (CashManagementForm) form;
CashManagementDocument cmDoc = cmForm.getCashManagementDocument();
CashDrawerSummary cms = cmForm.getCashDrawerSummary();
if (cms != null) {
cms.resummarize(cmDoc);
}
return dest;
}
/**
* @param mapping
* @param form
* @param request
* @param response
* @return ActionForward
* @throws Exception
*/
public ActionForward refreshSummary(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
CashManagementForm cmForm = (CashManagementForm) form;
CashManagementDocument cmDoc = cmForm.getCashManagementDocument();
if (cmForm.getCashDrawerSummary() != null) {
cmForm.getCashDrawerSummary().resummarize(cmDoc);
}
return mapping.findForward(KFSConstants.MAPPING_BASIC);
}
/**
* Saves the document, then opens the cash drawer
*
* @param mapping
* @param form
* @param request
* @param response
* @return
* @throws Exception
*/
public ActionForward openCashDrawer(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
CashManagementForm cmForm = (CashManagementForm) form;
CashManagementDocument cmDoc = cmForm.getCashManagementDocument();
if (!cmDoc.getDocumentHeader().getWorkflowDocument().isInitiated()) {
throw new IllegalStateException("openCashDrawer should only be called on documents which haven't yet been saved");
}
// open the CashDrawer
CashDrawerService cds = SpringContext.getBean(CashDrawerService.class);
cds.openCashDrawer(cmDoc.getCashDrawer(), cmDoc.getDocumentNumber());
// now that the cash drawer is open, let's create currency/coin detail records for this document
// create and save the cumulative cash receipt, deposit, money in and money out curr/coin details
SpringContext.getBean(CashManagementService.class).createNewCashDetails(cmDoc, KFSConstants.CurrencyCoinSources.CASH_RECEIPTS);
SpringContext.getBean(CashManagementService.class).createNewCashDetails(cmDoc, KFSConstants.CurrencyCoinSources.CASH_MANAGEMENT_IN);
SpringContext.getBean(CashManagementService.class).createNewCashDetails(cmDoc, KFSConstants.CurrencyCoinSources.CASH_MANAGEMENT_OUT);
try {
SpringContext.getBean(DocumentService.class).saveDocument(cmDoc);
}
catch (WorkflowException e) {
// force it closed if workflow proves recalcitrant
cds.closeCashDrawer(cmDoc.getCashDrawer());
throw e;
}
// update the CashDrawerSummary to reflect the change
cmForm.populateCashDrawerSummary();
return mapping.findForward(KFSConstants.MAPPING_BASIC);
}
/**
* This action makes the last interim deposit a final deposit
*
* @param mapping the mapping of the actions
* @param form the Struts form populated on the post
* @param request the servlet request
* @param response the servlet response
* @return a forward to the same page we were on
* @throws Exception because you never know when something just might go wrong
*/
public ActionForward finalizeLastInterimDeposit(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
CashManagementDocument cmDoc = ((CashManagementForm) form).getCashManagementDocument();
CashManagementService cms = SpringContext.getBean(CashManagementService.class);
if (cmDoc.hasFinalDeposit()) {
GlobalVariables.getMessageMap().putError(KFSConstants.CASH_MANAGEMENT_DEPOSIT_ERRORS, CashManagement.ERROR_DOCUMENT_ALREADY_HAS_FINAL_DEPOSIT, new String[] {});
}
else if (cmDoc.getDeposits().size() == 0) {
GlobalVariables.getMessageMap().putError(KFSConstants.CASH_MANAGEMENT_DEPOSIT_ERRORS, CashManagement.ERROR_DOCUMENT_NO_DEPOSITS_TO_MAKE_FINAL, new String[] {});
}
else if (!cms.allVerifiedCashReceiptsAreDeposited(cmDoc)) {
GlobalVariables.getMessageMap().putError(KFSConstants.CASH_MANAGEMENT_DEPOSIT_ERRORS, CashManagement.ERROR_NON_DEPOSITED_VERIFIED_CASH_RECEIPTS, new String[] {});
}
cms.finalizeLastInterimDeposit(cmDoc);
((CashManagementForm) form).getCashDrawerSummary().resummarize(cmDoc);
return mapping.findForward(KFSConstants.MAPPING_BASIC);
}
/**
* This action applies the current cashiering transaction to the cash drawer
*
* @param mapping
* @param form
* @param request
* @param response
* @return
* @throws Exception
*/
public ActionForward applyCashieringTransaction(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
CashManagementDocument cmDoc = ((CashManagementForm) form).getCashManagementDocument();
CashManagementService cmService = SpringContext.getBean(CashManagementService.class);
final boolean valid = SpringContext.getBean(KualiRuleService.class).applyRules(new CashieringTransactionApplicationEventBase("Cashiering Transaction Application Event", "", cmDoc, SpringContext.getBean(CashDrawerService.class).getByCampusCode(cmDoc.getCampusCode()), cmDoc.getCurrentTransaction()));
if (valid) {
cmService.applyCashieringTransaction(cmDoc);
((CashManagementForm) form).getCashDrawerSummary().resummarize(cmDoc);
}
return mapping.findForward(KFSConstants.MAPPING_BASIC);
}
/**
* This action allows the user to go to the cash drawer correction screen
*
* @param mapping
* @param form
* @param request
* @param response
* @return
* @throws Exception
*/
public ActionForward correctCashDrawer(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
return new ActionForward("CashDrawerCorrectionForm", buildCashDrawerCorrectionUrl(((CashManagementForm) form).getCashManagementDocument()), true);
}
/**
* @param cmDoc
* @param depositTypeCode
* @return URL for passing control to the DepositWizard
*/
protected String buildCashDrawerCorrectionUrl(CashManagementDocument cmDoc) {
Properties params = new Properties();
params.setProperty("methodToCall", "startCorrections");
params.setProperty("campusCode", cmDoc.getCampusCode());
return UrlFactory.parameterizeUrl("cashDrawerCorrection.do", params);
}
/**
* Adds Check instance created from the current "new check" line to the document
*
* @param mapping
* @param form
* @param request
* @param response
* @return ActionForward
* @throws Exception
*/
public ActionForward addCheck(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
CashManagementDocument cmDoc = ((CashManagementForm) form).getCashManagementDocument();
Check newCheck = cmDoc.getCurrentTransaction().getNewCheck();
newCheck.setDocumentNumber(cmDoc.getDocumentNumber());
// check business rules
boolean rulePassed = SpringContext.getBean(KualiRuleService.class).applyRules(new AddCheckEvent(KFSConstants.NEW_CHECK_PROPERTY_NAME, cmDoc, newCheck));
if (rulePassed) {
// add check
cmDoc.getCurrentTransaction().addCheck(newCheck);
// clear the used newCheck
cmDoc.getCurrentTransaction().setNewCheck(cmDoc.getCurrentTransaction().createNewCheck());
}
return mapping.findForward(KFSConstants.MAPPING_BASIC);
}
/**
* Deletes the selected check (line) from the document
*
* @param mapping
* @param form
* @param request
* @param response
* @return ActionForward
* @throws Exception
*/
public ActionForward deleteCheck(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
CashManagementDocument cmDoc = ((CashManagementForm) form).getCashManagementDocument();
int deleteIndex = getLineToDelete(request);
Check oldCheck = cmDoc.getCurrentTransaction().getCheck(deleteIndex);
boolean rulePassed = SpringContext.getBean(KualiRuleService.class).applyRules(new DeleteCheckEvent(KFSConstants.EXISTING_CHECK_PROPERTY_NAME, cmDoc, oldCheck));
if (rulePassed) {
// delete check
cmDoc.getCurrentTransaction().removeCheck(deleteIndex);
// delete baseline check, if any
if (cmDoc.getCurrentTransaction().hasBaselineCheck(deleteIndex)) {
cmDoc.getCurrentTransaction().getBaselineChecks().remove(deleteIndex);
}
}
else {
GlobalVariables.getMessageMap().putError("document.currentTransaction.check[" + deleteIndex + "]", KFSKeyConstants.Check.ERROR_CHECK_DELETERULE, Integer.toString(deleteIndex));
}
return mapping.findForward(KFSConstants.MAPPING_BASIC);
}
/**
* Overridden to clear the CashDrawerSummary info
*
* @see org.kuali.rice.kns.web.struts.action.KualiDocumentActionBase#route(org.apache.struts.action.ActionMapping,
* org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
@Override
public ActionForward route(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
CashManagementForm cmForm = (CashManagementForm) form;
CashManagementDocument cmDoc = cmForm.getCashManagementDocument();
ActionForward dest = super.route(mapping, form, request, response);
// clear the CashDrawerSummary
cmForm.setCashDrawerSummary(null);
return dest;
}
}