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.purap.document.web.struts; 017 018 import java.io.FileNotFoundException; 019 import java.io.IOException; 020 import java.util.Enumeration; 021 import java.util.HashMap; 022 import java.util.Iterator; 023 import java.util.List; 024 import java.util.Map; 025 import java.util.Map.Entry; 026 027 import javax.servlet.http.HttpServletRequest; 028 import javax.servlet.http.HttpServletResponse; 029 030 import org.apache.commons.lang.StringUtils; 031 import org.apache.struts.action.ActionForm; 032 import org.apache.struts.action.ActionForward; 033 import org.apache.struts.action.ActionMapping; 034 import org.apache.struts.upload.FormFile; 035 import org.kuali.kfs.coa.service.AccountService; 036 import org.kuali.kfs.module.purap.PurapConstants; 037 import org.kuali.kfs.module.purap.PurapPropertyConstants; 038 import org.kuali.kfs.module.purap.businessobject.PurApAccountingLine; 039 import org.kuali.kfs.module.purap.businessobject.PurApAccountingLineParser; 040 import org.kuali.kfs.module.purap.businessobject.PurApItem; 041 import org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument; 042 import org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocumentBase; 043 import org.kuali.kfs.module.purap.document.service.PurapService; 044 import org.kuali.kfs.module.purap.service.PurapAccountingService; 045 import org.kuali.kfs.sys.KFSConstants; 046 import org.kuali.kfs.sys.KFSPropertyConstants; 047 import org.kuali.kfs.sys.businessobject.AccountingLine; 048 import org.kuali.kfs.sys.businessobject.SourceAccountingLine; 049 import org.kuali.kfs.sys.context.SpringContext; 050 import org.kuali.kfs.sys.document.validation.event.AddAccountingLineEvent; 051 import org.kuali.kfs.sys.exception.AccountingLineParserException; 052 import org.kuali.kfs.sys.web.struts.KualiAccountingDocumentActionBase; 053 import org.kuali.kfs.sys.web.struts.KualiAccountingDocumentFormBase; 054 import org.kuali.rice.core.util.RiceConstants; 055 import org.kuali.rice.kew.exception.WorkflowException; 056 import org.kuali.rice.kns.service.DocumentService; 057 import org.kuali.rice.kns.service.KualiRuleService; 058 import org.kuali.rice.kns.service.PersistenceService; 059 import org.kuali.rice.kns.util.GlobalVariables; 060 import org.kuali.rice.kns.util.ObjectUtils; 061 import org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase; 062 import org.kuali.rice.kns.web.struts.form.KualiForm; 063 import org.kuali.rice.kns.workflow.service.KualiWorkflowDocument; 064 065 /** 066 * Struts Action for Purchasing and Accounts Payable documents 067 */ 068 public class PurchasingAccountsPayableActionBase extends KualiAccountingDocumentActionBase { 069 070 /** 071 * @see org.kuali.rice.kns.web.struts.action.KualiDocumentActionBase#loadDocument(org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase) 072 */ 073 @Override 074 protected void loadDocument(KualiDocumentFormBase kualiDocumentFormBase) throws WorkflowException { 075 super.loadDocument(kualiDocumentFormBase); 076 PurchasingAccountsPayableFormBase purapForm = (PurchasingAccountsPayableFormBase) kualiDocumentFormBase; 077 PurchasingAccountsPayableDocument document = (PurchasingAccountsPayableDocument) purapForm.getDocument(); 078 079 // refresh the account summary (note this also updates the account amounts) 080 purapForm.refreshAccountSummmary(); 081 082 for (org.kuali.rice.kns.bo.Note note : (java.util.List<org.kuali.rice.kns.bo.Note>) document.getDocumentBusinessObject().getBoNotes()) { 083 note.refreshReferenceObject("attachment"); 084 } 085 086 // sort the below the line 087 SpringContext.getBean(PurapService.class).sortBelowTheLine(document); 088 089 updateBaseline(document, (PurchasingAccountsPayableFormBase) kualiDocumentFormBase); 090 } 091 092 /** 093 * Updates the baseline accounts on form and doc. 094 * 095 * @param document A descendant of PurchasingAccountsPayableDocument 096 */ 097 protected <T extends PurchasingAccountsPayableDocument, V extends KualiAccountingDocumentFormBase> void updateBaseline(T document, V form) { 098 // clear out the old lines first 099 for (PurApItem item : document.getItems()) { 100 // clear out the old lines first 101 item.getBaselineSourceAccountingLines().clear(); 102 103 for (PurApAccountingLine sourceAccount : item.getSourceAccountingLines()) { 104 // JHK: KFSMI-287 - removed deep copy since this object will be thrown away after the page renders, we just need a 105 // different path to have them stored on the form 106 // ESPECIALLY since PURAP does not allow lines to be reverted (see calls to setRevertible) 107 item.getBaselineSourceAccountingLines().add(sourceAccount); 108 } 109 } 110 } 111 112 /** 113 * Invokes a service method to refresh the account summary. 114 * 115 * @param mapping An ActionMapping 116 * @param form An ActionForm 117 * @param request The HttpServletRequest 118 * @param response The HttpServletResponse 119 * @throws Exception 120 * @return An ActionForward 121 */ 122 public ActionForward refreshAccountSummary(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 123 PurchasingAccountsPayableFormBase purapForm = (PurchasingAccountsPayableFormBase) form; 124 PurchasingAccountsPayableDocument document = (PurchasingAccountsPayableDocument) purapForm.getDocument(); 125 SpringContext.getBean(PurapAccountingService.class).updateAccountAmounts(document); 126 purapForm.refreshAccountSummmary(); 127 return mapping.findForward(KFSConstants.MAPPING_BASIC); 128 } 129 130 /** 131 * @see org.kuali.kfs.sys.web.struts.KualiAccountingDocumentActionBase#uploadAccountingLines(boolean,org.apache.struts.action.ActionForm) 132 */ 133 @Override 134 protected void uploadAccountingLines(boolean isSource, ActionForm form) throws FileNotFoundException, IOException { 135 PurchasingAccountsPayableFormBase purapForm = (PurchasingAccountsPayableFormBase) form; 136 PurchasingAccountsPayableDocumentBase purapDocument = (PurchasingAccountsPayableDocumentBase)purapForm.getFinancialDocument(); 137 PurApAccountingLineParser accountingLineParser = (PurApAccountingLineParser)purapDocument.getAccountingLineParser(); 138 List importedLines = null; 139 String errorPathPrefix = PurapConstants.ACCOUNT_DISTRIBUTION_ERROR_KEY; 140 //String errorPathPrefix = "accountDistributionnewSourceLine"; 141 142 // import the lines 143 try { 144 FormFile sourceFile = purapForm.getSourceFile(); 145 checkUploadFile(sourceFile); 146 GlobalVariables.getMessageMap().clearErrorPath(); 147 GlobalVariables.getMessageMap().addToErrorPath(errorPathPrefix); 148 importedLines = accountingLineParser.importSourceAccountingLines(sourceFile.getFileName(), sourceFile.getInputStream(), purapDocument); 149 GlobalVariables.getMessageMap().removeFromErrorPath(errorPathPrefix); 150 } 151 catch (AccountingLineParserException e) { 152 GlobalVariables.getMessageMap().putError(errorPathPrefix, e.getErrorKey(), e.getErrorParameters()); 153 } 154 155 // add to list those lines successfully imported 156 if (importedLines != null) { 157 for (Iterator iter = importedLines.iterator(); iter.hasNext();) { 158 PurApAccountingLine importedLine = (PurApAccountingLine) iter.next(); 159 //boolean rulePassed = SpringContext.getBean(KualiRuleService.class).applyRules(new AddAccountingLineEvent(errorPathPrefix, purapForm.getDocument(), (AccountingLine) importedLine)); 160 //if (rulePassed) { 161 // add accountingLine 162 SpringContext.getBean(PersistenceService.class).retrieveNonKeyFields(importedLine); 163 ((PurchasingFormBase)purapForm).addAccountDistributionsourceAccountingLine(importedLine); 164 //} 165 } 166 } 167 } 168 169 /** 170 * @see org.kuali.kfs.sys.web.struts.KualiAccountingDocumentActionBase#insertSourceLine(org.apache.struts.action.ActionMapping, 171 * org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) 172 */ 173 @Override 174 public ActionForward insertSourceLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 175 // It would be preferable to find a way to genericize the KualiAccountingDocument methods but this will work for now 176 PurchasingAccountsPayableFormBase purapForm = (PurchasingAccountsPayableFormBase) form; 177 178 // index of item selected 179 int itemIndex = getSelectedLine(request); 180 PurApItem item = null; 181 182 // if custom processing of an accounting line is not done then insert a line generically. 183 if (processCustomInsertAccountingLine(purapForm, request) == false) { 184 String errorPrefix = null; 185 PurApAccountingLine line = null; 186 boolean rulePassed = false; 187 if (itemIndex >= 0) { 188 item = (PurApItem) ((PurchasingAccountsPayableDocument) purapForm.getDocument()).getItem((itemIndex)); 189 line = (PurApAccountingLine) ObjectUtils.deepCopy(item.getNewSourceLine()); 190 //SpringContext.getBean(AccountService.class).populateAccountingLineChartIfNeeded(line); 191 errorPrefix = KFSPropertyConstants.DOCUMENT + "." + PurapPropertyConstants.ITEM + "[" + Integer.toString(itemIndex) + "]." + KFSConstants.NEW_SOURCE_ACCT_LINE_PROPERTY_NAME; 192 rulePassed = SpringContext.getBean(KualiRuleService.class).applyRules(new AddAccountingLineEvent(errorPrefix, purapForm.getDocument(), (AccountingLine) line)); 193 } 194 else if (itemIndex == -2){ 195 //corrected: itemIndex == -2 is the only case for distribute account 196 //This is the case when we're inserting an accounting line for distribute account. 197 line = ((PurchasingFormBase)purapForm).getAccountDistributionnewSourceLine(); 198 //SpringContext.getBean(AccountService.class).populateAccountingLineChartIfNeeded(line); 199 errorPrefix = PurapPropertyConstants.ACCOUNT_DISTRIBUTION_NEW_SRC_LINE; 200 rulePassed = SpringContext.getBean(KualiRuleService.class).applyRules(new AddAccountingLineEvent(errorPrefix, purapForm.getDocument(), (AccountingLine) line)); 201 } 202 203 if (rulePassed) { 204 // add accountingLine 205 SpringContext.getBean(PersistenceService.class).retrieveNonKeyFields(line); 206 if (itemIndex >=0) { 207 insertAccountingLine(purapForm, item, line); 208 // clear the temp account 209 item.resetAccount(); 210 } 211 else if (itemIndex == -2) { 212 //this is the case for distribute account 213 ((PurchasingFormBase)purapForm).addAccountDistributionsourceAccountingLine(line); 214 } 215 } 216 } 217 218 return mapping.findForward(KFSConstants.MAPPING_BASIC); 219 } 220 221 /** 222 * Insert the given Accounting Line in several appropriate places in the given item and given form. 223 * 224 * @param financialDocumentForm A form that inherits from PurchasingAccountsPaybleFormBase 225 * @param item A PurApItem 226 * @param line A PurApAccountingLine 227 */ 228 protected void insertAccountingLine(PurchasingAccountsPayableFormBase financialDocumentForm, PurApItem item, PurApAccountingLine line) { 229 PurchasingAccountsPayableDocument preq = (PurchasingAccountsPayableDocument) financialDocumentForm.getDocument(); 230 231 // add it to the item 232 item.getSourceAccountingLines().add(line); 233 } 234 235 /** 236 * Allows the custom processing of an accounting line during a call to insert source line. If a custom method for inserting an 237 * accounting line was performed, then a value of true must be returned. 238 * 239 * @param purapForm 240 * @param request 241 * @return boolean indicating if validation succeeded 242 */ 243 public boolean processCustomInsertAccountingLine(PurchasingAccountsPayableFormBase purapForm, HttpServletRequest request) { 244 return false; 245 } 246 247 /** 248 * Insert the given Accounting Line in several appropriate places in the given item and given form. 249 * 250 * @param financialDocumentForm A form that inherits from KualiAccountingDocumentFormBase 251 * @param item A PurApItem 252 * @param line A PurApAccountingLine 253 */ 254 protected void insertAccountingLine(KualiAccountingDocumentFormBase financialDocumentForm, PurApItem item, PurApAccountingLine line) { 255 // add it to the item 256 item.getSourceAccountingLines().add(line); 257 } 258 259 /** 260 * @see org.kuali.kfs.sys.web.struts.KualiAccountingDocumentActionBase#deleteSourceLine(org.apache.struts.action.ActionMapping, 261 * org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) 262 */ 263 @Override 264 public ActionForward deleteSourceLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 265 PurchasingAccountsPayableFormBase purapForm = (PurchasingAccountsPayableFormBase) form; 266 267 String[] indexes = getSelectedLineForAccounts(request); 268 int itemIndex = Integer.parseInt(indexes[0]); 269 int accountIndex = Integer.parseInt(indexes[1]); 270 271 PurApItem item = (PurApItem) ((PurchasingAccountsPayableDocument) purapForm.getDocument()).getItem((itemIndex)); 272 item.getSourceAccountingLines().remove(accountIndex); 273 274 // remove the decorator 275 // financialDocumentForm.getSourceLineDecorators().remove(decorator); 276 277 return mapping.findForward(KFSConstants.MAPPING_BASIC); 278 } 279 280 /** 281 * @see org.kuali.kfs.sys.web.struts.KualiAccountingDocumentActionBase#getSourceAccountingLine(org.apache.struts.action.ActionForm, 282 * javax.servlet.http.HttpServletRequest) 283 */ 284 @Override 285 public SourceAccountingLine getSourceAccountingLine(ActionForm form, HttpServletRequest request) { 286 String[] indexes = getSelectedLineForAccounts(request); 287 int itemIndex = Integer.parseInt(indexes[0]); 288 int accountIndex = Integer.parseInt(indexes[1]); 289 PurchasingAccountsPayableFormBase purchasingAccountsPayableForm = (PurchasingAccountsPayableFormBase) form; 290 SourceAccountingLine line; 291 if (itemIndex == -2) { 292 line = customAccountRetrieval(accountIndex, purchasingAccountsPayableForm); 293 } 294 else { 295 PurApItem item = (PurApItem) ((PurchasingAccountsPayableDocument) purchasingAccountsPayableForm.getDocument()).getItem((itemIndex)); 296 line = (SourceAccountingLine) ObjectUtils.deepCopy(item.getSourceAccountingLines().get(accountIndex)); 297 } 298 return line; 299 } 300 301 /** 302 * Perform custom processing on accounting lines. See <code>getSelectedLineForAccounts</code>. 303 * 304 * @param accountIndex The index of the account into the request parameter 305 * @param purchasingAccountsPayableForm A form which inherits from PurchasingAccountsPayableFormBase 306 * @return A SourceAccountingLine 307 */ 308 protected SourceAccountingLine customAccountRetrieval(int accountIndex, PurchasingAccountsPayableFormBase purchasingAccountsPayableForm) { 309 // default impl returns null 310 return null; 311 } 312 313 /** 314 * Will return an array of Strings containing 2 indexes, the first String is the item index and the second String is the account 315 * index. These are obtained by parsing the method to call parameter from the request, between the word ".line" and "." The 316 * indexes are separated by a semicolon (:) 317 * 318 * @param request The HttpServletRequest 319 * @return An array of Strings containing pairs of two indices, an item index and a account index 320 */ 321 protected String[] getSelectedLineForAccounts(HttpServletRequest request) { 322 String accountString = new String(); 323 String parameterName = (String) request.getAttribute(KFSConstants.METHOD_TO_CALL_ATTRIBUTE); 324 if (StringUtils.isNotBlank(parameterName)) { 325 accountString = StringUtils.substringBetween(parameterName, ".line", "."); 326 } 327 String[] result = StringUtils.split(accountString, ":"); 328 329 return result; 330 } 331 332 /** 333 * @see org.kuali.rice.kns.web.struts.action.KualiDocumentActionBase#downloadBOAttachment(org.apache.struts.action.ActionMapping, 334 * org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) 335 */ 336 @Override 337 public ActionForward downloadBOAttachment(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 338 PurchasingAccountsPayableDocument document = (PurchasingAccountsPayableDocument) ((PurchasingAccountsPayableFormBase) form).getDocument(); 339 340 for (org.kuali.rice.kns.bo.Note note : (java.util.List<org.kuali.rice.kns.bo.Note>) document.getDocumentBusinessObject().getBoNotes()) { 341 note.refreshReferenceObject("attachment"); 342 } 343 344 return super.downloadBOAttachment(mapping, form, request, response); 345 } 346 347 @Override 348 protected void processAccountingLineOverrides(List accountingLines) { 349 //do nothing purap handles these differently 350 } 351 352 /** 353 * Perform calculation on item line. 354 * 355 * @param mapping An ActionMapping 356 * @param form An ActionForm 357 * @param request The HttpServletRequest 358 * @param response The HttpServletResponse 359 * @return An ActionForward 360 */ 361 public ActionForward calculate(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 362 return mapping.findForward(KFSConstants.MAPPING_BASIC); 363 } 364 365 public ActionForward clearAllTaxes(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 366 return mapping.findForward(KFSConstants.MAPPING_BASIC); 367 } 368 369 protected void customCalculate(PurchasingAccountsPayableDocument purapDoc) { 370 // do nothing by default 371 } 372 373 /** 374 * Toggles all specific tabs to open 375 * 376 * @param mapping 377 * @param form 378 * @param request 379 * @param response 380 * @return 381 * @throws Exception 382 */ 383 public ActionForward showAllAccounts(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 384 KualiForm kualiForm = (KualiForm) form; 385 String accountingLineTab = "AccountingLines"; 386 String value = null; 387 388 Map<String, String> tabStates = kualiForm.getTabStates(); 389 Map<String, String> newTabStates = new HashMap<String, String>(); 390 for (Entry<String, String> tabEntry: tabStates.entrySet()) { 391 if(tabEntry.getKey().startsWith(accountingLineTab)){ 392 newTabStates.put(tabEntry.getKey(), "OPEN"); 393 }else{ 394 if (tabEntry.getValue() instanceof String) { 395 value = tabEntry.getValue(); 396 } 397 else { 398 //This is the case where the value is an Array of String, 399 //so we'll have to get the first element 400 Object result = tabEntry.getValue(); 401 result.getClass(); 402 value = ((String[])result)[0]; 403 } 404 newTabStates.put(tabEntry.getKey(), value); 405 } 406 } 407 kualiForm.setTabStates(newTabStates); 408 return mapping.findForward(RiceConstants.MAPPING_BASIC); 409 } 410 411 /** 412 * Toggles all specific tabs to closed 413 * 414 * @param mapping 415 * @param form 416 * @param request 417 * @param response 418 * @return 419 * @throws Exception 420 */ 421 public ActionForward hideAllAccounts(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 422 KualiForm kualiForm = (KualiForm) form; 423 String accountingLineTab = "AccountingLines"; 424 String value = null; 425 426 Map<String, String> tabStates = kualiForm.getTabStates(); 427 Map<String, String> newTabStates = new HashMap<String, String>(); 428 for (Entry<String, String> tabEntry: tabStates.entrySet()) { 429 if(tabEntry.getKey().startsWith(accountingLineTab)){ 430 newTabStates.put(tabEntry.getKey(), "CLOSE"); 431 }else{ 432 if (tabEntry.getValue() instanceof String) { 433 value = tabEntry.getValue(); 434 } 435 else { 436 //This is the case where the value is an Array of String, 437 //so we'll have to get the first element 438 Object result = tabEntry.getValue(); 439 result.getClass(); 440 value = ((String[])result)[0]; 441 } 442 newTabStates.put(tabEntry.getKey(), value); 443 } 444 } 445 kualiForm.setTabStates(newTabStates); 446 return mapping.findForward(RiceConstants.MAPPING_BASIC); 447 } 448 449 /** 450 * Override to verify the document has been saved before the note is inserted. This will assure the correct parent object id is 451 * associated with the note. 452 * 453 * @see org.kuali.rice.kns.web.struts.action.KualiDocumentActionBase#insertBONote(org.apache.struts.action.ActionMapping, 454 * org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) 455 */ 456 @Override 457 public ActionForward insertBONote(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 458 PurchasingAccountsPayableDocument document = (PurchasingAccountsPayableDocument) ((PurchasingAccountsPayableFormBase) form).getDocument(); 459 KualiWorkflowDocument workflowDocument = document.getDocumentHeader().getWorkflowDocument(); 460 461 if (workflowDocument.stateIsInitiated()) { 462 SpringContext.getBean(DocumentService.class).saveDocument(document); 463 } 464 465 return super.insertBONote(mapping, form, request, response); 466 } 467 468 }