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.module.ar.document.web.struts; 017 018 import java.util.List; 019 020 import javax.servlet.http.HttpServletRequest; 021 import javax.servlet.http.HttpServletResponse; 022 023 import org.apache.commons.lang.StringUtils; 024 import org.apache.struts.action.ActionForm; 025 import org.apache.struts.action.ActionForward; 026 import org.apache.struts.action.ActionMapping; 027 import org.kuali.kfs.module.ar.ArConstants; 028 import org.kuali.kfs.module.ar.ArKeyConstants; 029 import org.kuali.kfs.module.ar.ArPropertyConstants; 030 import org.kuali.kfs.module.ar.businessobject.AccountsReceivableDocumentHeader; 031 import org.kuali.kfs.module.ar.businessobject.CashControlDetail; 032 import org.kuali.kfs.module.ar.document.CashControlDocument; 033 import org.kuali.kfs.module.ar.document.PaymentApplicationDocument; 034 import org.kuali.kfs.module.ar.document.service.AccountsReceivableDocumentHeaderService; 035 import org.kuali.kfs.module.ar.document.service.CashControlDocumentService; 036 import org.kuali.kfs.module.ar.document.validation.event.AddCashControlDetailEvent; 037 import org.kuali.kfs.sys.KFSConstants; 038 import org.kuali.kfs.sys.context.SpringContext; 039 import org.kuali.kfs.sys.document.web.struts.FinancialSystemTransactionalDocumentActionBase; 040 import org.kuali.kfs.sys.service.GeneralLedgerPendingEntryService; 041 import org.kuali.rice.kew.exception.WorkflowException; 042 import org.kuali.rice.kns.exception.UnknownDocumentIdException; 043 import org.kuali.rice.kns.rule.event.SaveDocumentEvent; 044 import org.kuali.rice.kns.service.BusinessObjectService; 045 import org.kuali.rice.kns.service.DocumentService; 046 import org.kuali.rice.kns.service.KualiConfigurationService; 047 import org.kuali.rice.kns.service.KualiRuleService; 048 import org.kuali.rice.kns.util.GlobalVariables; 049 import org.kuali.rice.kns.util.KualiDecimal; 050 import org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase; 051 import org.kuali.rice.kns.workflow.service.KualiWorkflowDocument; 052 053 public class CashControlDocumentAction extends FinancialSystemTransactionalDocumentActionBase { 054 055 /** 056 * @see org.kuali.rice.kns.web.struts.action.KualiDocumentActionBase#loadDocument(org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase) 057 */ 058 @Override 059 protected void loadDocument(KualiDocumentFormBase kualiDocumentFormBase) throws WorkflowException { 060 061 super.loadDocument(kualiDocumentFormBase); 062 CashControlDocumentForm ccForm = (CashControlDocumentForm) kualiDocumentFormBase; 063 CashControlDocument cashControlDocument = ccForm.getCashControlDocument(); 064 065 // now that the form has been originally loaded, we need to set a few Form variables that are used by 066 // JSP JSTL expressions because they are used directly and immediately upon initial form display 067 if (cashControlDocument != null && cashControlDocument.getCustomerPaymentMediumCode() != null) { 068 ccForm.setCashPaymentMediumSelected(ArConstants.PaymentMediumCode.CASH.equalsIgnoreCase(cashControlDocument.getCustomerPaymentMediumCode())); 069 } 070 071 // get the PaymentApplicationDocuments by reference number 072 for (CashControlDetail cashControlDetail : cashControlDocument.getCashControlDetails()) { 073 String docId = cashControlDetail.getReferenceFinancialDocumentNumber(); 074 PaymentApplicationDocument doc = null; 075 doc = (PaymentApplicationDocument) SpringContext.getBean(DocumentService.class).getByDocumentHeaderId(docId); 076 if (doc == null) { 077 throw new UnknownDocumentIdException("Document no longer exists. It may have been cancelled before being saved."); 078 } 079 080 cashControlDetail.setReferenceFinancialDocument(doc); 081 KualiWorkflowDocument workflowDoc = doc.getDocumentHeader().getWorkflowDocument(); 082 // KualiDocumentFormBase.populate() needs this updated in the session 083 GlobalVariables.getUserSession().setWorkflowDocument(workflowDoc); 084 } 085 086 } 087 088 /** 089 * Adds handling for cash control detail amount updates. 090 * 091 * @see org.apache.struts.action.Action#execute(org.apache.struts.action.ActionMapping, org.apache.struts.action.ActionForm, 092 * javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) 093 */ 094 @Override 095 public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 096 097 CashControlDocumentForm ccForm = (CashControlDocumentForm) form; 098 CashControlDocument ccDoc = ccForm.getCashControlDocument(); 099 100 if (ccDoc != null) { 101 ccForm.setCashPaymentMediumSelected(ArConstants.PaymentMediumCode.CASH.equalsIgnoreCase(ccDoc.getCustomerPaymentMediumCode())); 102 } 103 104 if (ccForm.hasDocumentId()) { 105 ccDoc = ccForm.getCashControlDocument(); 106 ccDoc.refreshReferenceObject("customerPaymentMedium"); 107 // recalc b/c changes to the amounts could have happened 108 ccDoc.recalculateTotals(); 109 } 110 111 return super.execute(mapping, form, request, response); 112 } 113 114 /** 115 * @see org.kuali.rice.kns.web.struts.action.KualiDocumentActionBase#createDocument(org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase) 116 */ 117 @Override 118 protected void createDocument(KualiDocumentFormBase kualiDocumentFormBase) throws WorkflowException { 119 super.createDocument(kualiDocumentFormBase); 120 CashControlDocumentForm form = (CashControlDocumentForm) kualiDocumentFormBase; 121 CashControlDocument document = form.getCashControlDocument(); 122 123 // set up the default values for the AR DOC Header (SHOULD PROBABLY MAKE THIS A SERVICE) 124 AccountsReceivableDocumentHeaderService accountsReceivableDocumentHeaderService = SpringContext.getBean(AccountsReceivableDocumentHeaderService.class); 125 AccountsReceivableDocumentHeader accountsReceivableDocumentHeader = accountsReceivableDocumentHeaderService.getNewAccountsReceivableDocumentHeaderForCurrentUser(); 126 accountsReceivableDocumentHeader.setDocumentNumber(document.getDocumentNumber()); 127 document.setAccountsReceivableDocumentHeader(accountsReceivableDocumentHeader); 128 129 } 130 131 /** 132 * @see org.kuali.rice.kns.web.struts.action.KualiDocumentActionBase#cancel(org.apache.struts.action.ActionMapping, 133 * org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) 134 */ 135 @Override 136 public ActionForward cancel(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 137 138 CashControlDocumentForm cashControlDocForm = (CashControlDocumentForm) form; 139 CashControlDocument cashControlDocument = cashControlDocForm.getCashControlDocument(); 140 141 // If the cancel works, proceed to canceling the cash control doc 142 if (cancelLinkedPaymentApplicationDocuments(cashControlDocument)) { 143 return super.cancel(mapping, form, request, response); 144 } 145 146 return mapping.findForward(KFSConstants.MAPPING_BASIC); 147 } 148 149 /** 150 * @see org.kuali.rice.kns.web.struts.action.KualiDocumentActionBase#disapprove(org.apache.struts.action.ActionMapping, 151 * org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) 152 */ 153 @Override 154 public ActionForward disapprove(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 155 boolean success = true; 156 CashControlDocumentForm cashControlDocForm = (CashControlDocumentForm) form; 157 CashControlDocument cashControlDocument = cashControlDocForm.getCashControlDocument(); 158 159 success = cancelLinkedPaymentApplicationDocuments(cashControlDocument); 160 161 if (!success) { 162 return mapping.findForward(KFSConstants.MAPPING_BASIC); 163 } 164 165 return super.disapprove(mapping, form, request, response); 166 } 167 168 /** 169 * This method cancels all linked Payment Application documents that are not already in approved status. 170 * 171 * @param cashControlDocument 172 * @throws WorkflowException 173 */ 174 protected boolean cancelLinkedPaymentApplicationDocuments(CashControlDocument cashControlDocument) throws WorkflowException { 175 boolean success = true; 176 List<CashControlDetail> details = cashControlDocument.getCashControlDetails(); 177 178 for (CashControlDetail cashControlDetail : details) { 179 DocumentService documentService = SpringContext.getBean(DocumentService.class); 180 181 PaymentApplicationDocument applicationDocument = (PaymentApplicationDocument) documentService.getByDocumentHeaderId(cashControlDetail.getReferenceFinancialDocumentNumber()); 182 if (KFSConstants.DocumentStatusCodes.CANCELLED.equals(applicationDocument.getDocumentHeader().getFinancialDocumentStatusCode())) { 183 // Ignore this case, as it should not impact the ability to cancel a cash control doc. 184 } 185 else if (!KFSConstants.DocumentStatusCodes.APPROVED.equals(applicationDocument.getDocumentHeader().getFinancialDocumentStatusCode())) { 186 documentService.cancelDocument(applicationDocument, ArKeyConstants.DOCUMENT_DELETED_FROM_CASH_CTRL_DOC); 187 } 188 else { 189 GlobalVariables.getMessageMap().putError(ArPropertyConstants.CashControlDetailFields.CASH_CONTROL_DETAILS_TAB, ArKeyConstants.ERROR_CANT_CANCEL_CASH_CONTROL_DOC_WITH_ASSOCIATED_APPROVED_PAYMENT_APPLICATION); 190 success = false; 191 ; 192 } 193 } 194 return success; 195 } 196 197 /** 198 * This method adds a new cash control detail 199 * 200 * @param mapping action mapping 201 * @param form action form 202 * @param request 203 * @param response 204 * @return forward action 205 * @throws Exception 206 */ 207 public ActionForward addCashControlDetail(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 208 209 CashControlDocumentForm cashControlDocForm = (CashControlDocumentForm) form; 210 CashControlDocument cashControlDocument = cashControlDocForm.getCashControlDocument(); 211 KualiConfigurationService kualiConfiguration = SpringContext.getBean(KualiConfigurationService.class); 212 213 CashControlDetail newCashControlDetail = cashControlDocForm.getNewCashControlDetail(); 214 newCashControlDetail.setDocumentNumber(cashControlDocument.getDocumentNumber()); 215 216 String customerNumber = newCashControlDetail.getCustomerNumber(); 217 if (StringUtils.isNotEmpty(customerNumber)) { 218 // force customer numbers to upper case, since its a primary key 219 customerNumber = customerNumber.toUpperCase(); 220 } 221 newCashControlDetail.setCustomerNumber(customerNumber); 222 223 // save the document, which will run business rules and make sure the doc is ready for lines 224 KualiRuleService ruleService = SpringContext.getBean(KualiRuleService.class); 225 boolean rulePassed = true; 226 227 // apply save rules for the doc 228 rulePassed &= ruleService.applyRules(new SaveDocumentEvent(KFSConstants.DOCUMENT_HEADER_ERRORS, cashControlDocument)); 229 230 // apply rules for the new cash control detail 231 rulePassed &= ruleService.applyRules(new AddCashControlDetailEvent(ArConstants.NEW_CASH_CONTROL_DETAIL_ERROR_PATH_PREFIX, cashControlDocument, newCashControlDetail)); 232 233 // add the new detail if rules passed 234 if (rulePassed) { 235 236 CashControlDocumentService cashControlDocumentService = SpringContext.getBean(CashControlDocumentService.class); 237 238 // add cash control detail. implicitly saves the cash control document 239 cashControlDocumentService.addNewCashControlDetail(kualiConfiguration.getPropertyString(ArKeyConstants.CREATED_BY_CASH_CTRL_DOC), cashControlDocument, newCashControlDetail); 240 241 // set a new blank cash control detail 242 cashControlDocForm.setNewCashControlDetail(new CashControlDetail()); 243 244 } 245 246 // recalc totals, including the docHeader total 247 cashControlDocument.recalculateTotals(); 248 249 return mapping.findForward(KFSConstants.MAPPING_BASIC); 250 251 } 252 253 /** 254 * This method deletes a cash control detail 255 * 256 * @param mapping action mapping 257 * @param form action form 258 * @param request 259 * @param response 260 * @return action forward 261 * @throws Exception 262 */ 263 public ActionForward deleteCashControlDetail(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 264 CashControlDocumentForm cashControlDocForm = (CashControlDocumentForm) form; 265 CashControlDocument cashControlDocument = cashControlDocForm.getCashControlDocument(); 266 267 int indexOfLineToDelete = getLineToDelete(request); 268 CashControlDetail cashControlDetail = cashControlDocument.getCashControlDetail(indexOfLineToDelete); 269 DocumentService documentService = SpringContext.getBean(DocumentService.class); 270 271 PaymentApplicationDocument payAppDoc = (PaymentApplicationDocument) documentService.getByDocumentHeaderId(cashControlDetail.getReferenceFinancialDocumentNumber()); 272 273 // this if statement is to catch the situation where a person deletes the line, but doesnt save 274 // and then reloads. This will bring the deleted line back on the screen. If they then click 275 // the delete line, and this test isnt here, it will try to cancel the already cancelled 276 // document, which will throw a workflow error and barf. 277 if (!payAppDoc.getDocumentHeader().getWorkflowDocument().stateIsCanceled()) { 278 documentService.cancelDocument(payAppDoc, ArKeyConstants.DOCUMENT_DELETED_FROM_CASH_CTRL_DOC); 279 } 280 cashControlDocument.deleteCashControlDetail(indexOfLineToDelete); 281 282 // recalc totals, including the docHeader total 283 cashControlDocument.recalculateTotals(); 284 285 return mapping.findForward(KFSConstants.MAPPING_BASIC); 286 } 287 288 /** 289 * This method generates the GLPEs. 290 * 291 * @param mapping action mapping 292 * @param form action form 293 * @param request 294 * @param response 295 * @return action forward 296 * @throws Exception 297 */ 298 public ActionForward generateGLPEs(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 299 300 CashControlDocumentForm cashControlDocForm = (CashControlDocumentForm) form; 301 CashControlDocument cashControlDocument = cashControlDocForm.getCashControlDocument(); 302 String paymentMediumCode = cashControlDocument.getCustomerPaymentMediumCode(); 303 304 // refresh reference objects 305 cashControlDocument.refreshReferenceObject("customerPaymentMedium"); 306 cashControlDocument.refreshReferenceObject("generalLedgerPendingEntries"); 307 308 // payment medium might have been changed meanwhile so we save first the document 309 BusinessObjectService businessObjectService = SpringContext.getBean(BusinessObjectService.class); 310 businessObjectService.save(cashControlDocument); 311 312 // generate the GLPEs 313 GeneralLedgerPendingEntryService glpeService = SpringContext.getBean(GeneralLedgerPendingEntryService.class); 314 boolean success = glpeService.generateGeneralLedgerPendingEntries(cashControlDocument); 315 316 if (!success) { 317 GlobalVariables.getMessageMap().putError(KFSConstants.GENERAL_LEDGER_PENDING_ENTRIES_TAB_ERRORS, ArKeyConstants.ERROR_GLPES_NOT_CREATED); 318 } 319 // approve the GLPEs 320 cashControlDocument.changeGeneralLedgerPendingEntriesApprovedStatusCode(); 321 322 // save the GLPEs in the database 323 CashControlDocumentService cashControlDocumentService = SpringContext.getBean(CashControlDocumentService.class); 324 cashControlDocumentService.saveGLPEs(cashControlDocument); 325 326 // approve the document when the GLPEs are generated 327 // DocumentService docService = SpringContext.getBean(DocumentService.class); 328 // docService.approveDocument(cashControlDocument, "Automatically approved document with GLPE generation.", null); 329 330 return mapping.findForward(KFSConstants.MAPPING_BASIC); 331 332 } 333 334 /** 335 * Recalculates the cash control total since user could have changed it during their update. 336 * 337 * @param cashControlDocument 338 */ 339 protected KualiDecimal calculateCashControlTotal(CashControlDocument cashControlDocument) { 340 KualiDecimal total = KualiDecimal.ZERO; 341 for (CashControlDetail cashControlDetail : cashControlDocument.getCashControlDetails()) { 342 343 total = total.add(cashControlDetail.getFinancialDocumentLineAmount()); 344 } 345 return total; 346 } 347 348 }