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.sys.web.struts; 017 018 import static org.kuali.kfs.sys.KFSKeyConstants.ERROR_DOCUMENT_ACCOUNTING_LINE_SALES_TAX_INVALID_ACCOUNT; 019 import static org.kuali.kfs.sys.KFSKeyConstants.ERROR_DOCUMENT_ACCOUNTING_LINE_SALES_TAX_REQUIRED; 020 import static org.kuali.kfs.sys.KFSKeyConstants.ERROR_REQUIRED; 021 022 import java.io.FileNotFoundException; 023 import java.io.IOException; 024 import java.util.ArrayList; 025 import java.util.Arrays; 026 import java.util.HashMap; 027 import java.util.HashSet; 028 import java.util.Iterator; 029 import java.util.List; 030 import java.util.Map; 031 import java.util.Properties; 032 import java.util.Set; 033 034 import javax.servlet.http.HttpServletRequest; 035 import javax.servlet.http.HttpServletResponse; 036 037 import org.apache.commons.lang.StringUtils; 038 import org.apache.struts.action.ActionForm; 039 import org.apache.struts.action.ActionForward; 040 import org.apache.struts.action.ActionMapping; 041 import org.apache.struts.upload.FormFile; 042 import org.kuali.kfs.coa.service.AccountService; 043 import org.kuali.kfs.fp.businessobject.CapitalAssetInformation; 044 import org.kuali.kfs.fp.businessobject.CapitalAssetInformationDetail; 045 import org.kuali.kfs.fp.businessobject.SalesTax; 046 import org.kuali.kfs.fp.document.CapitalAssetEditable; 047 import org.kuali.kfs.sys.KFSConstants; 048 import org.kuali.kfs.sys.KFSKeyConstants; 049 import org.kuali.kfs.sys.KFSPropertyConstants; 050 import org.kuali.kfs.sys.businessobject.AccountingLine; 051 import org.kuali.kfs.sys.businessobject.AccountingLineOverride; 052 import org.kuali.kfs.sys.businessobject.AccountingLineParser; 053 import org.kuali.kfs.sys.businessobject.FinancialSystemDocumentHeader; 054 import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntry; 055 import org.kuali.kfs.sys.businessobject.SourceAccountingLine; 056 import org.kuali.kfs.sys.businessobject.TargetAccountingLine; 057 import org.kuali.kfs.sys.context.SpringContext; 058 import org.kuali.kfs.sys.document.AccountingDocument; 059 import org.kuali.kfs.sys.document.AmountTotaling; 060 import org.kuali.kfs.sys.document.validation.event.AddAccountingLineEvent; 061 import org.kuali.kfs.sys.document.validation.event.DeleteAccountingLineEvent; 062 import org.kuali.kfs.sys.document.validation.impl.AccountingDocumentRuleBaseConstants.APPLICATION_PARAMETER; 063 import org.kuali.kfs.sys.document.web.struts.FinancialSystemTransactionalDocumentActionBase; 064 import org.kuali.kfs.sys.exception.AccountingLineParserException; 065 import org.kuali.kfs.sys.service.impl.KfsParameterConstants; 066 import org.kuali.rice.kew.exception.WorkflowException; 067 import org.kuali.rice.kns.service.BusinessObjectService; 068 import org.kuali.rice.kns.service.DataDictionaryService; 069 import org.kuali.rice.kns.service.DictionaryValidationService; 070 import org.kuali.rice.kns.service.KualiRuleService; 071 import org.kuali.rice.kns.service.ParameterEvaluator; 072 import org.kuali.rice.kns.service.ParameterService; 073 import org.kuali.rice.kns.service.PersistenceService; 074 import org.kuali.rice.kns.util.GlobalVariables; 075 import org.kuali.rice.kns.util.KNSConstants; 076 import org.kuali.rice.kns.util.ObjectUtils; 077 import org.kuali.rice.kns.util.Timer; 078 import org.kuali.rice.kns.util.UrlFactory; 079 import org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase; 080 081 /** 082 * This class handles UI actions for all shared methods of financial documents. 083 */ 084 public class KualiAccountingDocumentActionBase extends FinancialSystemTransactionalDocumentActionBase { 085 protected static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(KualiAccountingDocumentActionBase.class); 086 087 /** 088 * Adds check for accountingLine updates, generates and dispatches any events caused by such updates 089 * 090 * @see org.apache.struts.action.Action#execute(org.apache.struts.action.ActionMapping, org.apache.struts.action.ActionForm, 091 * javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) 092 */ 093 @Override 094 public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 095 Timer t0 = new Timer("KualiFinancialDocumentFormBase.execute"); 096 KualiAccountingDocumentFormBase transForm = (KualiAccountingDocumentFormBase) form; 097 098 // handle changes to accountingLines 099 if (transForm.hasDocumentId()) { 100 AccountingDocument financialDocument = (AccountingDocument) transForm.getDocument(); 101 102 processAccountingLines(financialDocument, transForm, KFSConstants.SOURCE); 103 processAccountingLines(financialDocument, transForm, KFSConstants.TARGET); 104 } 105 106 // This is after a potential handleUpdate(), to display automatically cleared overrides following a route or save. 107 processAccountingLineOverrides(transForm); 108 109 // proceed as usual 110 ActionForward result = super.execute(mapping, form, request, response); 111 t0.log(); 112 return result; 113 } 114 115 /** 116 * All document-load operations get routed through here 117 * 118 * @see org.kuali.rice.kns.web.struts.action.KualiDocumentActionBase#loadDocument(org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase) 119 */ 120 @Override 121 protected void loadDocument(KualiDocumentFormBase kualiDocumentFormBase) throws WorkflowException { 122 super.loadDocument(kualiDocumentFormBase); 123 124 125 KualiAccountingDocumentFormBase tform = (KualiAccountingDocumentFormBase) kualiDocumentFormBase; 126 127 // clear out the new accounting line holders 128 tform.setNewSourceLine(null); 129 tform.setNewTargetLine(null); 130 131 processAccountingLineOverrides(tform); 132 } 133 134 /** 135 * Needed to override this to keep from losing Sales Tax information 136 * 137 * @see org.kuali.rice.kns.web.struts.action.KualiAction#refresh(org.apache.struts.action.ActionMapping, 138 * org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) 139 */ 140 @Override 141 public ActionForward refresh(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 142 super.refresh(mapping, form, request, response); 143 refreshSalesTaxInfo(form); 144 145 return mapping.findForward(KFSConstants.MAPPING_BASIC); 146 } 147 148 /** 149 * Needed to override this to keep from losing Sales Tax information 150 * 151 * @see org.kuali.rice.kns.web.struts.action.KualiAction#toggleTab(org.apache.struts.action.ActionMapping, 152 * org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) 153 */ 154 @Override 155 public ActionForward toggleTab(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 156 super.toggleTab(mapping, form, request, response); 157 refreshSalesTaxInfo(form); 158 159 return mapping.findForward(KFSConstants.MAPPING_BASIC); 160 } 161 162 163 // Set of actions for which updateEvents should be generated 164 protected static final Set UPDATE_EVENT_ACTIONS; 165 static { 166 String[] updateEventActions = { KFSConstants.SAVE_METHOD, KFSConstants.ROUTE_METHOD, KFSConstants.APPROVE_METHOD, KFSConstants.BLANKET_APPROVE_METHOD }; 167 UPDATE_EVENT_ACTIONS = new HashSet(); 168 for (int i = 0; i < updateEventActions.length; ++i) { 169 UPDATE_EVENT_ACTIONS.add(updateEventActions[i]); 170 } 171 } 172 173 /** 174 * @param transForm 175 */ 176 protected void processAccountingLineOverrides(KualiAccountingDocumentFormBase transForm) { 177 processAccountingLineOverrides(transForm.getNewSourceLine()); 178 processAccountingLineOverrides(transForm.getNewTargetLine()); 179 if (transForm.hasDocumentId()) { 180 AccountingDocument financialDocument = (AccountingDocument) transForm.getDocument(); 181 182 processAccountingLineOverrides(financialDocument.getSourceAccountingLines()); 183 processAccountingLineOverrides(financialDocument.getTargetAccountingLines()); 184 } 185 } 186 187 /** 188 * @param line 189 */ 190 protected void processAccountingLineOverrides(AccountingLine line) { 191 processAccountingLineOverrides(Arrays.asList(new AccountingLine[] { line })); 192 } 193 194 /** 195 * @param accountingLines 196 */ 197 protected void processAccountingLineOverrides(List accountingLines) { 198 if (!accountingLines.isEmpty()) { 199 SpringContext.getBean(PersistenceService.class).retrieveReferenceObjects(accountingLines, AccountingLineOverride.REFRESH_FIELDS); 200 201 for (Iterator i = accountingLines.iterator(); i.hasNext();) { 202 AccountingLine line = (AccountingLine) i.next(); 203 AccountingLineOverride.processForOutput(line); 204 } 205 } 206 } 207 208 /** 209 * @param transDoc 210 * @param transForm 211 * @param lineSet 212 */ 213 protected void processAccountingLines(AccountingDocument transDoc, KualiAccountingDocumentFormBase transForm, String lineSet) { 214 // figure out which set of lines we're looking at 215 List formLines; 216 String pathPrefix; 217 boolean source; 218 if (lineSet.equals(KFSConstants.SOURCE)) { 219 formLines = transDoc.getSourceAccountingLines(); 220 pathPrefix = KFSConstants.DOCUMENT_PROPERTY_NAME + "." + KFSConstants.EXISTING_SOURCE_ACCT_LINE_PROPERTY_NAME; 221 source = true; 222 } 223 else { 224 formLines = transDoc.getTargetAccountingLines(); 225 pathPrefix = KFSConstants.DOCUMENT_PROPERTY_NAME + "." + KFSConstants.EXISTING_TARGET_ACCT_LINE_PROPERTY_NAME; 226 source = false; 227 } 228 229 // find and process corresponding form and baselines 230 int index = 0; 231 for (Iterator i = formLines.iterator(); i.hasNext(); index++) { 232 AccountingLine formLine = (AccountingLine) i.next(); 233 234 // update sales tax required attribute for view 235 // handleSalesTaxRequired(transDoc, formLine, source, false, index); 236 checkSalesTax(transDoc, formLine, source, false, index); 237 } 238 } 239 240 /** 241 * Automatically clears any overrides that have become unneeded. This is for accounting lines that were changed right before 242 * final actions like route. Normally the unneeded overrides are cleared in accountingLineOverrideField.tag instead, but that 243 * requires another form submit. This method shouldn't be called on lines that haven't changed, to avoid automatically changing 244 * read-only lines. This cannot be done in the Rule because Rules cannot change the AccountingLines; they only get a deepCopy. 245 * 246 * @param formLine 247 */ 248 protected void clearOverridesThatBecameUnneeded(AccountingLine formLine) { 249 AccountingLineOverride currentlyNeeded = AccountingLineOverride.determineNeededOverrides(formLine); 250 AccountingLineOverride currentOverride = AccountingLineOverride.valueOf(formLine.getOverrideCode()); 251 if (!currentOverride.isValidMask(currentlyNeeded)) { 252 // todo: handle unsupported combinations of overrides (not a problem until we allow certain multiple overrides) 253 } 254 formLine.setOverrideCode(currentOverride.mask(currentlyNeeded).getCode()); 255 } 256 257 /** 258 * This method will remove a TargetAccountingLine from a FinancialDocument. This assumes that the user presses the delete button 259 * for a specific accounting line on the document and that the document is represented by a FinancialDocumentFormBase. 260 * 261 * @param mapping 262 * @param form 263 * @param request 264 * @param response 265 * @return ActionForward 266 * @throws Exception 267 */ 268 public ActionForward deleteTargetLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 269 KualiAccountingDocumentFormBase financialDocumentForm = (KualiAccountingDocumentFormBase) form; 270 271 int deleteIndex = getLineToDelete(request); 272 String errorPath = KFSConstants.DOCUMENT_PROPERTY_NAME + "." + KFSConstants.EXISTING_TARGET_ACCT_LINE_PROPERTY_NAME + "[" + deleteIndex + "]"; 273 boolean rulePassed = SpringContext.getBean(KualiRuleService.class).applyRules(new DeleteAccountingLineEvent(errorPath, financialDocumentForm.getDocument(), ((AccountingDocument) financialDocumentForm.getDocument()).getTargetAccountingLine(deleteIndex), false)); 274 275 // if the rule evaluation passed, let's delete it 276 if (rulePassed) { 277 deleteAccountingLine(false, financialDocumentForm, deleteIndex); 278 } 279 else { 280 String[] errorParams = new String[] { "target", Integer.toString(deleteIndex + 1) }; 281 GlobalVariables.getMessageMap().putError(errorPath, KFSKeyConstants.ERROR_ACCOUNTINGLINE_DELETERULE_INVALIDACCOUNT, errorParams); 282 } 283 284 return mapping.findForward(KFSConstants.MAPPING_BASIC); 285 } 286 287 /** 288 * This method will remove a SourceAccountingLine from a FinancialDocument. This assumes that the user presses the delete button 289 * for a specific accounting line on the document and that the document is represented by a FinancialDocumentFormBase. 290 * 291 * @param mapping 292 * @param form 293 * @param request 294 * @param response 295 * @return ActionForward 296 * @throws Exception 297 */ 298 public ActionForward deleteSourceLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 299 KualiAccountingDocumentFormBase financialDocumentForm = (KualiAccountingDocumentFormBase) form; 300 301 int deleteIndex = getLineToDelete(request); 302 String errorPath = KFSConstants.DOCUMENT_PROPERTY_NAME + "." + KFSConstants.EXISTING_SOURCE_ACCT_LINE_PROPERTY_NAME + "[" + deleteIndex + "]"; 303 boolean rulePassed = SpringContext.getBean(KualiRuleService.class).applyRules(new DeleteAccountingLineEvent(errorPath, financialDocumentForm.getDocument(), ((AccountingDocument) financialDocumentForm.getDocument()).getSourceAccountingLine(deleteIndex), false)); 304 305 // if the rule evaluation passed, let's delete it 306 if (rulePassed) { 307 deleteAccountingLine(true, financialDocumentForm, deleteIndex); 308 } 309 else { 310 String[] errorParams = new String[] { "source", Integer.toString(deleteIndex + 1) }; 311 GlobalVariables.getMessageMap().putError(errorPath, KFSKeyConstants.ERROR_ACCOUNTINGLINE_DELETERULE_INVALIDACCOUNT, errorParams); 312 } 313 314 return mapping.findForward(KFSConstants.MAPPING_BASIC); 315 } 316 317 318 /** 319 * Deletes the source or target accountingLine with the given index from the given form. Assumes that the rule- and form- 320 * validation have already occurred. 321 * 322 * @param isSource 323 * @param financialDocumentForm 324 * @param deleteIndex 325 */ 326 protected void deleteAccountingLine(boolean isSource, KualiAccountingDocumentFormBase financialDocumentForm, int deleteIndex) { 327 if (isSource) { 328 // remove from document 329 financialDocumentForm.getFinancialDocument().getSourceAccountingLines().remove(deleteIndex); 330 331 } 332 else { 333 // remove from document 334 financialDocumentForm.getFinancialDocument().getTargetAccountingLines().remove(deleteIndex); 335 } 336 // update the doc total 337 AccountingDocument tdoc = (AccountingDocument) financialDocumentForm.getDocument(); 338 if (tdoc instanceof AmountTotaling) { 339 ((FinancialSystemDocumentHeader) financialDocumentForm.getDocument().getDocumentHeader()).setFinancialDocumentTotalAmount(((AmountTotaling) tdoc).getTotalDollarAmount()); 340 } 341 342 } 343 344 345 /** 346 * This action executes a call to upload CSV accounting line values as TargetAccountingLines for a given transactional document. 347 * The "uploadAccountingLines()" method handles the multi-part request. 348 * 349 * @param mapping 350 * @param form 351 * @param request 352 * @param response 353 * @return ActionForward 354 * @throws Exception 355 */ 356 public ActionForward uploadTargetLines(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 357 358 // call method that sourceform and destination list 359 uploadAccountingLines(false, form); 360 361 return mapping.findForward(KFSConstants.MAPPING_BASIC); 362 } 363 364 365 /** 366 * This action executes a call to upload CSV accounting line values as SourceAccountingLines for a given transactional document. 367 * The "uploadAccountingLines()" method handles the multi-part request. 368 * 369 * @param mapping 370 * @param form 371 * @param request 372 * @param response 373 * @return ActionForward 374 * @throws FileNotFoundException 375 * @throws IOException 376 */ 377 public ActionForward uploadSourceLines(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws FileNotFoundException, IOException { 378 LOG.info("Uploading source accounting lines"); 379 // call method that sourceform and destination list 380 uploadAccountingLines(true, form); 381 382 return mapping.findForward(KFSConstants.MAPPING_BASIC); 383 } 384 385 /** 386 * This method determines whether we are uploading source or target lines, and then calls uploadAccountingLines directly on the 387 * document object. This method handles retrieving the actual upload file as an input stream into the document. 388 * 389 * @param isSource 390 * @param form 391 * @throws FileNotFoundException 392 * @throws IOException 393 */ 394 protected void uploadAccountingLines(boolean isSource, ActionForm form) throws FileNotFoundException, IOException { 395 KualiAccountingDocumentFormBase tmpForm = (KualiAccountingDocumentFormBase) form; 396 397 List importedLines = null; 398 399 AccountingDocument financialDocument = tmpForm.getFinancialDocument(); 400 AccountingLineParser accountingLineParser = financialDocument.getAccountingLineParser(); 401 402 // import the lines 403 String errorPathPrefix = null; 404 try { 405 if (isSource) { 406 errorPathPrefix = KFSConstants.DOCUMENT_PROPERTY_NAME + "." + KFSConstants.SOURCE_ACCOUNTING_LINE_ERRORS; 407 FormFile sourceFile = tmpForm.getSourceFile(); 408 checkUploadFile(sourceFile); 409 importedLines = accountingLineParser.importSourceAccountingLines(sourceFile.getFileName(), sourceFile.getInputStream(), financialDocument); 410 } 411 else { 412 errorPathPrefix = KFSConstants.DOCUMENT_PROPERTY_NAME + "." + KFSConstants.TARGET_ACCOUNTING_LINE_ERRORS; 413 FormFile targetFile = tmpForm.getTargetFile(); 414 checkUploadFile(targetFile); 415 importedLines = accountingLineParser.importTargetAccountingLines(targetFile.getFileName(), targetFile.getInputStream(), financialDocument); 416 } 417 } 418 catch (AccountingLineParserException e) { 419 GlobalVariables.getMessageMap().putError(errorPathPrefix, e.getErrorKey(), e.getErrorParameters()); 420 } 421 422 // add line to list for those lines which were successfully imported 423 if (importedLines != null) { 424 for (Iterator i = importedLines.iterator(); i.hasNext();) { 425 AccountingLine importedLine = (AccountingLine) i.next(); 426 insertAccountingLine(isSource, tmpForm, importedLine); 427 } 428 } 429 } 430 431 protected void checkUploadFile(FormFile file) { 432 if (file == null) { 433 throw new AccountingLineParserException("invalid (null) upload file", KFSKeyConstants.ERROR_UPLOADFILE_NULL); 434 } 435 } 436 437 /** 438 * This method will add a TargetAccountingLine to a FinancialDocument. This assumes that the user presses the add button for a 439 * specific accounting line on the document and that the document is represented by a FinancialDocumentFormBase. It first 440 * validates the line for data integrity and then checks appropriate business rules. 441 * 442 * @param mapping 443 * @param form 444 * @param request 445 * @param response 446 * @return ActionForward 447 * @throws Exception 448 */ 449 public ActionForward insertTargetLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 450 KualiAccountingDocumentFormBase financialDocumentForm = (KualiAccountingDocumentFormBase) form; 451 TargetAccountingLine line = financialDocumentForm.getNewTargetLine(); 452 453 // populate chartOfAccountsCode from account number if accounts cant cross chart and Javascript is turned off 454 //SpringContext.getBean(AccountService.class).populateAccountingLineChartIfNeeded(line); 455 456 boolean rulePassed = true; 457 // before we check the regular rules we need to check the sales tax rules 458 // TODO: Refactor rules so we no longer have to call this before a copy of the 459 // accountingLine 460 rulePassed &= checkSalesTax((AccountingDocument) financialDocumentForm.getDocument(), line, false, true, 0); 461 462 // check any business rules 463 rulePassed &= SpringContext.getBean(KualiRuleService.class).applyRules(new AddAccountingLineEvent(KFSConstants.NEW_TARGET_ACCT_LINE_PROPERTY_NAME, financialDocumentForm.getDocument(), line)); 464 465 // if the rule evaluation passed, let's add it 466 if (rulePassed) { 467 // add accountingLine 468 SpringContext.getBean(PersistenceService.class).refreshAllNonUpdatingReferences(line); 469 insertAccountingLine(false, financialDocumentForm, line); 470 471 // clear the used newTargetLine 472 financialDocumentForm.setNewTargetLine(null); 473 } 474 475 return mapping.findForward(KFSConstants.MAPPING_BASIC); 476 } 477 478 479 /** 480 * This action executes an insert of a SourceAccountingLine into a document only after validating the accounting line and 481 * checking any appropriate business rules. 482 * 483 * @param mapping 484 * @param form 485 * @param request 486 * @param response 487 * @return ActionForward 488 * @throws Exception 489 */ 490 public ActionForward insertSourceLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 491 KualiAccountingDocumentFormBase financialDocumentForm = (KualiAccountingDocumentFormBase) form; 492 SourceAccountingLine line = financialDocumentForm.getNewSourceLine(); 493 494 // populate chartOfAccountsCode from account number if accounts cant cross chart and Javascript is turned off 495 //SpringContext.getBean(AccountService.class).populateAccountingLineChartIfNeeded(line); 496 497 boolean rulePassed = true; 498 // before we check the regular rules we need to check the sales tax rules 499 // TODO: Refactor rules so we no longer have to call this before a copy of the 500 // accountingLine 501 rulePassed &= checkSalesTax((AccountingDocument) financialDocumentForm.getDocument(), line, true, true, 0); 502 // check any business rules 503 rulePassed &= SpringContext.getBean(KualiRuleService.class).applyRules(new AddAccountingLineEvent(KFSConstants.NEW_SOURCE_ACCT_LINE_PROPERTY_NAME, financialDocumentForm.getDocument(), line)); 504 505 if (rulePassed) { 506 // add accountingLine 507 SpringContext.getBean(PersistenceService.class).refreshAllNonUpdatingReferences(line); 508 insertAccountingLine(true, financialDocumentForm, line); 509 510 // clear the used newTargetLine 511 financialDocumentForm.setNewSourceLine(null); 512 } 513 514 return mapping.findForward(KFSConstants.MAPPING_BASIC); 515 } 516 517 /** 518 * Adds the given accountingLine to the appropriate form-related datastructures. 519 * 520 * @param isSource 521 * @param financialDocumentForm 522 * @param line 523 */ 524 protected void insertAccountingLine(boolean isSource, KualiAccountingDocumentFormBase financialDocumentForm, AccountingLine line) { 525 AccountingDocument tdoc = financialDocumentForm.getFinancialDocument(); 526 if (isSource) { 527 // add it to the document 528 tdoc.addSourceAccountingLine((SourceAccountingLine) line); 529 530 // add PK fields to sales tax if needed 531 if (line.isSalesTaxRequired()) { 532 populateSalesTax(line); 533 } 534 535 // Update the doc total 536 if (tdoc instanceof AmountTotaling) 537 ((FinancialSystemDocumentHeader) financialDocumentForm.getDocument().getDocumentHeader()).setFinancialDocumentTotalAmount(((AmountTotaling) tdoc).getTotalDollarAmount()); 538 } 539 else { 540 // add it to the document 541 tdoc.addTargetAccountingLine((TargetAccountingLine) line); 542 543 // add PK fields to sales tax if needed 544 if (line.isSalesTaxRequired()) { 545 populateSalesTax(line); 546 } 547 } 548 } 549 550 /** 551 * TODO: remove this method once baseline accounting lines has been removed 552 */ 553 protected List deepCopyAccountingLinesList(List originals) { 554 if (originals == null) { 555 return null; 556 } 557 List copiedLines = new ArrayList(); 558 for (int i = 0; i < originals.size(); i++) { 559 copiedLines.add(ObjectUtils.deepCopy((AccountingLine) originals.get(i))); 560 } 561 return copiedLines; 562 } 563 564 /** 565 * This action changes the value of the hide field in the user interface so that when the page is rendered, the UI knows to show 566 * all of the labels for each of the accounting line values. 567 * 568 * @param mapping 569 * @param form 570 * @param request 571 * @param response 572 * @return ActionForward 573 * @throws Exception 574 */ 575 public ActionForward showDetails(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 576 KualiAccountingDocumentFormBase tmpForm = (KualiAccountingDocumentFormBase) form; 577 tmpForm.setHideDetails(false); 578 return mapping.findForward(KFSConstants.MAPPING_BASIC); 579 } 580 581 /** 582 * This method is triggered when the user toggles the show/hide button to "hide" thus making the UI render without any of the 583 * accounting line labels/descriptions showing up underneath the values in the UI. 584 * 585 * @param mapping 586 * @param form 587 * @param request 588 * @param response 589 * @return ActionForward 590 * @throws Exception 591 */ 592 public ActionForward hideDetails(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 593 KualiAccountingDocumentFormBase tmpForm = (KualiAccountingDocumentFormBase) form; 594 tmpForm.setHideDetails(true); 595 return mapping.findForward(KFSConstants.MAPPING_BASIC); 596 } 597 598 /** 599 * Takes care of storing the action form in the User session and forwarding to the balance inquiry report menu action for a 600 * source accounting line. 601 * 602 * @param mapping 603 * @param form 604 * @param request 605 * @param response 606 * @return ActionForward 607 * @throws Exception 608 */ 609 public ActionForward performBalanceInquiryForSourceLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 610 SourceAccountingLine line = this.getSourceAccountingLine(form, request); 611 return performBalanceInquiryForAccountingLine(mapping, form, request, line); 612 } 613 614 /** 615 * Takes care of storing the action form in the User session and forwarding to the balance inquiry report menu action for a 616 * target accounting line. 617 * 618 * @param mapping 619 * @param form 620 * @param request 621 * @param response 622 * @return ActionForward 623 * @throws Exception 624 */ 625 public ActionForward performBalanceInquiryForTargetLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 626 int lineIndex = getSelectedLine(request); 627 628 TargetAccountingLine line = this.getTargetAccountingLine(form, request); 629 630 return performBalanceInquiryForAccountingLine(mapping, form, request, line); 631 } 632 633 /** 634 * This method is a helper method that will return a source accounting line. The reason we're making it protected in here is so 635 * that we can override this method in some of the modules. PurchasingActionBase is one of the subclasses that will be 636 * overriding this, because in PurchasingActionBase, we'll need to get the source accounting line using both an item index and 637 * an account index. 638 * 639 * @param form 640 * @param request 641 * @param isSource 642 * @return 643 */ 644 protected SourceAccountingLine getSourceAccountingLine(ActionForm form, HttpServletRequest request) { 645 int lineIndex = getSelectedLine(request); 646 SourceAccountingLine line = (SourceAccountingLine) ObjectUtils.deepCopy(((KualiAccountingDocumentFormBase) form).getFinancialDocument().getSourceAccountingLine(lineIndex)); 647 return line; 648 } 649 650 protected TargetAccountingLine getTargetAccountingLine(ActionForm form, HttpServletRequest request) { 651 int lineIndex = getSelectedLine(request); 652 TargetAccountingLine line = (TargetAccountingLine) ((KualiAccountingDocumentFormBase) form).getFinancialDocument().getTargetAccountingLine(lineIndex); 653 654 return line; 655 } 656 657 /** 658 * This method handles preparing all of the accounting line data so that it can be pushed up to the balance inquiries for 659 * populating the search criteria of each. 660 * 661 * @param mapping 662 * @param form 663 * @param request 664 * @param line 665 * @return ActionForward 666 */ 667 protected ActionForward performBalanceInquiryForAccountingLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, AccountingLine line) { 668 // build out base path for return location 669 String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath(); 670 671 // build out the actual form key that will be used to retrieve the form on refresh 672 String callerDocFormKey = GlobalVariables.getUserSession().addObject(form); 673 674 // now add required parameters 675 Properties parameters = new Properties(); 676 parameters.put(KFSConstants.DISPATCH_REQUEST_PARAMETER, KFSConstants.START_METHOD); 677 // need this next param b/c the lookup's return back will overwrite 678 // the original doc form key 679 parameters.put(KFSConstants.BALANCE_INQUIRY_REPORT_MENU_CALLER_DOC_FORM_KEY, callerDocFormKey); 680 parameters.put(KFSConstants.DOC_FORM_KEY, callerDocFormKey); 681 parameters.put(KFSConstants.BACK_LOCATION, basePath + mapping.getPath() + ".do"); 682 683 if (line.getPostingYear() != null) { 684 parameters.put(KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR, line.getPostingYear().toString()); 685 } 686 if (StringUtils.isNotBlank(line.getReferenceOriginCode())) { 687 parameters.put("referenceOriginCode", line.getReferenceOriginCode()); 688 } 689 if (StringUtils.isNotBlank(line.getReferenceNumber())) { 690 parameters.put("referenceNumber", line.getReferenceNumber()); 691 } 692 if (StringUtils.isNotBlank(line.getReferenceTypeCode())) { 693 parameters.put("referenceTypeCode", line.getReferenceTypeCode()); 694 } 695 if (StringUtils.isNotBlank(line.getDebitCreditCode())) { 696 parameters.put("debitCreditCode", line.getDebitCreditCode()); 697 } 698 if (StringUtils.isNotBlank(line.getChartOfAccountsCode())) { 699 parameters.put("chartOfAccountsCode", line.getChartOfAccountsCode()); 700 } 701 if (StringUtils.isNotBlank(line.getAccountNumber())) { 702 parameters.put("accountNumber", line.getAccountNumber()); 703 } 704 if (StringUtils.isNotBlank(line.getFinancialObjectCode())) { 705 parameters.put("financialObjectCode", line.getFinancialObjectCode()); 706 } 707 if (StringUtils.isNotBlank(line.getSubAccountNumber())) { 708 parameters.put("subAccountNumber", line.getSubAccountNumber()); 709 } 710 if (StringUtils.isNotBlank(line.getFinancialSubObjectCode())) { 711 parameters.put("financialSubObjectCode", line.getFinancialSubObjectCode()); 712 } 713 if (StringUtils.isNotBlank(line.getProjectCode())) { 714 parameters.put("projectCode", line.getProjectCode()); 715 } 716 if (StringUtils.isNotBlank(getObjectTypeCodeFromLine(line))) { 717 if (!StringUtils.isBlank(line.getObjectTypeCode())) { 718 parameters.put("objectTypeCode", line.getObjectTypeCode()); 719 } 720 else { 721 line.refreshReferenceObject("objectCode"); 722 parameters.put("objectTypeCode", line.getObjectCode().getFinancialObjectTypeCode()); 723 } 724 } 725 726 String lookupUrl = UrlFactory.parameterizeUrl(basePath + "/" + KFSConstants.BALANCE_INQUIRY_REPORT_MENU_ACTION, parameters); 727 728 // register that we're going to come back w/ to this form w/ a refresh methodToCall 729 ((KualiAccountingDocumentFormBase) form).registerEditableProperty(KNSConstants.DISPATCH_REQUEST_PARAMETER); 730 731 return new ActionForward(lookupUrl, true); 732 } 733 734 /** 735 * A hook so that most accounting lines - which don't have object types - can have their object type codes used in balance 736 * inquiries 737 * 738 * @param line the line to get the object type code from 739 * @return the object type code the line would use 740 */ 741 protected String getObjectTypeCodeFromLine(AccountingLine line) { 742 line.refreshReferenceObject("objectCode"); 743 return line.getObjectCode().getFinancialObjectTypeCode(); 744 } 745 746 @Override 747 public ActionForward save(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 748 KualiAccountingDocumentFormBase tmpForm = (KualiAccountingDocumentFormBase) form; 749 this.applyCapitalAssetInformation(tmpForm); 750 751 ActionForward forward = super.save(mapping, form, request, response); 752 753 // need to check on sales tax for all the accounting lines 754 checkSalesTaxRequiredAllLines(tmpForm, tmpForm.getFinancialDocument().getSourceAccountingLines()); 755 checkSalesTaxRequiredAllLines(tmpForm, tmpForm.getFinancialDocument().getTargetAccountingLines()); 756 return forward; 757 } 758 759 @Override 760 public ActionForward approve(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 761 KualiAccountingDocumentFormBase tmpForm = (KualiAccountingDocumentFormBase) form; 762 this.applyCapitalAssetInformation(tmpForm); 763 764 ActionForward forward = super.approve(mapping, form, request, response); 765 766 // need to check on sales tax for all the accounting lines 767 checkSalesTaxRequiredAllLines(tmpForm, tmpForm.getFinancialDocument().getSourceAccountingLines()); 768 checkSalesTaxRequiredAllLines(tmpForm, tmpForm.getFinancialDocument().getTargetAccountingLines()); 769 return forward; 770 } 771 772 @Override 773 public ActionForward route(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 774 KualiAccountingDocumentFormBase tmpForm = (KualiAccountingDocumentFormBase) form; 775 this.applyCapitalAssetInformation(tmpForm); 776 777 ActionForward forward = super.route(mapping, form, request, response); 778 779 checkSalesTaxRequiredAllLines(tmpForm, tmpForm.getFinancialDocument().getSourceAccountingLines()); 780 checkSalesTaxRequiredAllLines(tmpForm, tmpForm.getFinancialDocument().getTargetAccountingLines()); 781 782 return forward; 783 } 784 785 @Override 786 public ActionForward blanketApprove(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 787 KualiAccountingDocumentFormBase tmpForm = (KualiAccountingDocumentFormBase) form; 788 this.applyCapitalAssetInformation(tmpForm); 789 790 ActionForward forward = super.blanketApprove(mapping, form, request, response); 791 792 return forward; 793 } 794 795 /** 796 * Encapsulate the rule check so we can call it from multiple places 797 * 798 * @param document 799 * @param line 800 * @return true if sales is either not required or it contains sales tax 801 */ 802 protected boolean checkSalesTax(AccountingDocument document, AccountingLine line, boolean source, boolean newLine, int index) { 803 boolean passed = true; 804 if (isSalesTaxRequired(document, line)) { 805 // then set the salesTaxRequired on the accountingLine 806 line.setSalesTaxRequired(true); 807 populateSalesTax(line); 808 // check to see if the sales tax info has been put in 809 passed &= isValidSalesTaxEntered(line, source, newLine, index); 810 } 811 return passed; 812 } 813 814 /** 815 * This method checks to see if this doctype needs sales tax If it does then it checks to see if the account and object code 816 * require sales tax If it does then it returns true. Note - this is hackish as we shouldn't have to call rules directly from 817 * the action class But we need to in this instance because we are copying the lines before calling rules and need a way to 818 * modify them before they go on 819 * 820 * @param accountingLine 821 * @return true if sales tax check is needed, false otherwise 822 */ 823 protected boolean isSalesTaxRequired(AccountingDocument financialDocument, AccountingLine accountingLine) { 824 boolean required = false; 825 String docType = SpringContext.getBean(DataDictionaryService.class).getDocumentTypeNameByClass(financialDocument.getClass()); 826 // first we need to check just the doctype to see if it needs the sales tax check 827 ParameterService parameterService = SpringContext.getBean(ParameterService.class); 828 // apply the rule, see if it fails 829 ParameterEvaluator docTypeSalesTaxCheckEvaluator = SpringContext.getBean(ParameterService.class).getParameterEvaluator(KfsParameterConstants.FINANCIAL_PROCESSING_DOCUMENT.class, APPLICATION_PARAMETER.DOCTYPE_SALES_TAX_CHECK, docType); 830 if (docTypeSalesTaxCheckEvaluator.evaluationSucceeds()) { 831 required = true; 832 } 833 834 // second we need to check the account and object code combination to see if it needs sales tax 835 if (required) { 836 // get the object code and account 837 String objCd = accountingLine.getFinancialObjectCode(); 838 String account = accountingLine.getAccountNumber(); 839 if (!StringUtils.isEmpty(objCd) && !StringUtils.isEmpty(account)) { 840 String compare = account + ":" + objCd; 841 ParameterEvaluator salesTaxApplicableAcctAndObjectEvaluator = SpringContext.getBean(ParameterService.class).getParameterEvaluator(KfsParameterConstants.FINANCIAL_PROCESSING_DOCUMENT.class, APPLICATION_PARAMETER.SALES_TAX_APPLICABLE_ACCOUNTS_AND_OBJECT_CODES, compare); 842 if (!salesTaxApplicableAcctAndObjectEvaluator.evaluationSucceeds()) { 843 required = false; 844 } 845 } 846 else { 847 // the two fields are currently empty and we don't need to check yet 848 required = false; 849 } 850 } 851 return required; 852 } 853 854 /** 855 * This method checks to see if the sales tax information was put into the accounting line 856 * 857 * @param accountingLine 858 * @return true if entered correctly, false otherwise 859 */ 860 protected boolean isValidSalesTaxEntered(AccountingLine accountingLine, boolean source, boolean newLine, int index) { 861 boolean valid = true; 862 DictionaryValidationService dictionaryValidationService = SpringContext.getBean(DictionaryValidationService.class); 863 BusinessObjectService boService = SpringContext.getBean(BusinessObjectService.class); 864 String objCd = accountingLine.getFinancialObjectCode(); 865 String account = accountingLine.getAccountNumber(); 866 SalesTax salesTax = accountingLine.getSalesTax(); 867 String pathPrefix = ""; 868 if (source && !newLine) { 869 pathPrefix = "document." + KFSConstants.EXISTING_SOURCE_ACCT_LINE_PROPERTY_NAME + "[" + index + "]"; 870 } 871 else if (!source && !newLine) { 872 pathPrefix = "document." + KFSConstants.EXISTING_TARGET_ACCT_LINE_PROPERTY_NAME + "[" + index + "]"; 873 } 874 else if (source && newLine) { 875 pathPrefix = KFSConstants.NEW_SOURCE_ACCT_LINE_PROPERTY_NAME; 876 } 877 else if (!source && newLine) { 878 pathPrefix = KFSConstants.NEW_TARGET_ACCT_LINE_PROPERTY_NAME; 879 } 880 GlobalVariables.getMessageMap().addToErrorPath(pathPrefix); 881 if (ObjectUtils.isNull(salesTax)) { 882 valid &= false; 883 GlobalVariables.getMessageMap().putError("salesTax.chartOfAccountsCode", ERROR_DOCUMENT_ACCOUNTING_LINE_SALES_TAX_REQUIRED, account, objCd); 884 } 885 else { 886 887 if (StringUtils.isBlank(salesTax.getChartOfAccountsCode())) { 888 valid &= false; 889 GlobalVariables.getMessageMap().putError("salesTax.chartOfAccountsCode", ERROR_REQUIRED, "Chart of Accounts"); 890 } 891 if (StringUtils.isBlank(salesTax.getAccountNumber())) { 892 valid &= false; 893 GlobalVariables.getMessageMap().putError("salesTax.accountNumber", ERROR_REQUIRED, "Account Number"); 894 } 895 if (salesTax.getFinancialDocumentGrossSalesAmount() == null) { 896 valid &= false; 897 GlobalVariables.getMessageMap().putError("salesTax.financialDocumentGrossSalesAmount", ERROR_REQUIRED, "Gross Sales Amount"); 898 } 899 if (salesTax.getFinancialDocumentTaxableSalesAmount() == null) { 900 valid &= false; 901 GlobalVariables.getMessageMap().putError("salesTax.financialDocumentTaxableSalesAmount", ERROR_REQUIRED, "Taxable Sales Amount"); 902 } 903 if (salesTax.getFinancialDocumentSaleDate() == null) { 904 valid &= false; 905 GlobalVariables.getMessageMap().putError("salesTax.financialDocumentSaleDate", ERROR_REQUIRED, "Sale Date"); 906 } 907 if (StringUtils.isNotBlank(salesTax.getChartOfAccountsCode()) && StringUtils.isNotBlank(salesTax.getAccountNumber())) { 908 909 if (boService.getReferenceIfExists(salesTax, "account") == null) { 910 valid &= false; 911 GlobalVariables.getMessageMap().putError("salesTax.accountNumber", ERROR_DOCUMENT_ACCOUNTING_LINE_SALES_TAX_INVALID_ACCOUNT, salesTax.getChartOfAccountsCode(), salesTax.getAccountNumber()); 912 913 } 914 } 915 if (!valid) { 916 GlobalVariables.getMessageMap().putError("salesTax.chartOfAccountsCode", ERROR_DOCUMENT_ACCOUNTING_LINE_SALES_TAX_REQUIRED, account, objCd); 917 } 918 } 919 GlobalVariables.getMessageMap().removeFromErrorPath(pathPrefix); 920 return valid; 921 } 922 923 /** 924 * This method removes the sales tax information from a line that no longer requires it 925 * 926 * @param accountingLine 927 */ 928 protected void removeSalesTax(AccountingLine accountingLine) { 929 SalesTax salesTax = accountingLine.getSalesTax(); 930 if (ObjectUtils.isNotNull(salesTax)) { 931 accountingLine.setSalesTax(null); 932 } 933 } 934 935 936 /** 937 * This method checks to see if the given accounting needs sales tax and if it does it sets the salesTaxRequired variable on the 938 * line If it doesn't and it has it then it removes the sales tax information from the line This method is called from the 939 * execute() on all accounting lines that have been edited or lines that have already been added to the document, not on new 940 * lines 941 * 942 * @param transDoc 943 * @param formLine 944 * @param baseLine 945 */ 946 protected void handleSalesTaxRequired(AccountingDocument transDoc, AccountingLine formLine, boolean source, boolean newLine, int index) { 947 boolean salesTaxRequired = isSalesTaxRequired(transDoc, formLine); 948 if (salesTaxRequired) { 949 formLine.setSalesTaxRequired(true); 950 populateSalesTax(formLine); 951 } 952 else if (!salesTaxRequired && hasSalesTaxBeenEntered(formLine, source, newLine, index)) { 953 // remove it if it has been added but is no longer required 954 removeSalesTax(formLine); 955 } 956 } 957 958 protected boolean hasSalesTaxBeenEntered(AccountingLine accountingLine, boolean source, boolean newLine, int index) { 959 boolean entered = true; 960 String objCd = accountingLine.getFinancialObjectCode(); 961 String account = accountingLine.getAccountNumber(); 962 SalesTax salesTax = accountingLine.getSalesTax(); 963 if (ObjectUtils.isNull(salesTax)) { 964 return false; 965 } 966 if (StringUtils.isBlank(salesTax.getChartOfAccountsCode())) { 967 entered &= false; 968 } 969 if (StringUtils.isBlank(salesTax.getAccountNumber())) { 970 entered &= false; 971 } 972 if (salesTax.getFinancialDocumentGrossSalesAmount() == null) { 973 entered &= false; 974 } 975 if (salesTax.getFinancialDocumentTaxableSalesAmount() == null) { 976 entered &= false; 977 } 978 if (salesTax.getFinancialDocumentSaleDate() == null) { 979 entered &= false; 980 } 981 return entered; 982 } 983 984 /** 985 * This method is called from the createDocument and processes through all the accouting lines and checks to see if they need 986 * sales tax fields 987 * 988 * @param kualiDocumentFormBase 989 * @param baselineSourceLines 990 */ 991 protected void handleSalesTaxRequiredAllLines(KualiDocumentFormBase kualiDocumentFormBase, List<AccountingLine> baselineAcctingLines) { 992 AccountingDocument accoutingDocument = (AccountingDocument) kualiDocumentFormBase.getDocument(); 993 int index = 0; 994 for (AccountingLine accountingLine : baselineAcctingLines) { 995 boolean source = false; 996 if (accountingLine.isSourceAccountingLine()) { 997 source = true; 998 } 999 handleSalesTaxRequired(accoutingDocument, accountingLine, source, false, index); 1000 index++; 1001 } 1002 1003 } 1004 1005 protected boolean checkSalesTaxRequiredAllLines(KualiDocumentFormBase kualiDocumentFormBase, List<AccountingLine> baselineAcctingLines) { 1006 AccountingDocument accoutingDocument = (AccountingDocument) kualiDocumentFormBase.getDocument(); 1007 boolean passed = true; 1008 int index = 0; 1009 for (AccountingLine accountingLine : baselineAcctingLines) { 1010 boolean source = false; 1011 if (accountingLine.isSourceAccountingLine()) { 1012 source = true; 1013 } 1014 passed &= checkSalesTax(accoutingDocument, accountingLine, source, false, index); 1015 index++; 1016 } 1017 return passed; 1018 } 1019 1020 /** 1021 * This method refreshes the sales tax fields on a refresh or tab toggle so that all the information that was there before is 1022 * still there after a state change 1023 * 1024 * @param form 1025 */ 1026 protected void refreshSalesTaxInfo(ActionForm form) { 1027 KualiAccountingDocumentFormBase accountingForm = (KualiAccountingDocumentFormBase) form; 1028 AccountingDocument document = (AccountingDocument) accountingForm.getDocument(); 1029 List sourceLines = document.getSourceAccountingLines(); 1030 List targetLines = document.getTargetAccountingLines(); 1031 handleSalesTaxRequiredAllLines(accountingForm, sourceLines); 1032 handleSalesTaxRequiredAllLines(accountingForm, targetLines); 1033 1034 AccountingLine newTargetLine = accountingForm.getNewTargetLine(); 1035 AccountingLine newSourceLine = accountingForm.getNewSourceLine(); 1036 if (newTargetLine != null) { 1037 handleSalesTaxRequired(document, newTargetLine, false, true, 0); 1038 } 1039 if (newSourceLine != null) { 1040 handleSalesTaxRequired(document, newSourceLine, true, true, 0); 1041 } 1042 } 1043 1044 /** 1045 * This method populates the sales tax for a given accounting line with the appropriate primary key fields from the accounting 1046 * line since OJB won't do it automatically for us 1047 * 1048 * @param line 1049 */ 1050 protected void populateSalesTax(AccountingLine line) { 1051 SalesTax salesTax = line.getSalesTax(); 1052 1053 if (ObjectUtils.isNotNull(salesTax)) { 1054 salesTax.setDocumentNumber(line.getDocumentNumber()); 1055 salesTax.setFinancialDocumentLineTypeCode(line.getFinancialDocumentLineTypeCode()); 1056 salesTax.setFinancialDocumentLineNumber(line.getSequenceNumber()); 1057 } 1058 } 1059 1060 @Override 1061 public ActionForward performLookup(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 1062 // parse out the business object name from our methodToCall parameter 1063 String fullParameter = (String) request.getAttribute(KFSConstants.METHOD_TO_CALL_ATTRIBUTE); 1064 String boClassName = StringUtils.substringBetween(fullParameter, KFSConstants.METHOD_TO_CALL_BOPARM_LEFT_DEL, KFSConstants.METHOD_TO_CALL_BOPARM_RIGHT_DEL); 1065 1066 if (!StringUtils.equals(boClassName, GeneralLedgerPendingEntry.class.getName())) { 1067 return super.performLookup(mapping, form, request, response); 1068 } 1069 1070 String path = super.performLookup(mapping, form, request, response).getPath(); 1071 path = path.replaceFirst(KFSConstants.LOOKUP_ACTION, KFSConstants.GL_MODIFIED_INQUIRY_ACTION); 1072 1073 return new ActionForward(path, true); 1074 } 1075 1076 /** 1077 * clear up the capital asset information 1078 */ 1079 public ActionForward clearCapitalAssetInfo(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 1080 LOG.debug("clearCapitalAssetInfo() - start"); 1081 1082 KualiAccountingDocumentFormBase kualiAccountingDocumentFormBase = (KualiAccountingDocumentFormBase) form; 1083 CapitalAssetInformation capitalAssetInformation = this.getCurrentCapitalAssetInformationObject(kualiAccountingDocumentFormBase); 1084 if (capitalAssetInformation == null) { 1085 return mapping.findForward(KFSConstants.MAPPING_BASIC); 1086 } 1087 1088 this.resetCapitalAssetInfo(capitalAssetInformation); 1089 1090 return mapping.findForward(KFSConstants.MAPPING_BASIC); 1091 } 1092 1093 /** 1094 * add the capital asset information 1095 */ 1096 public ActionForward addCapitalAssetInfo(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 1097 LOG.debug("addCapitalAssetInfoDetail() - start"); 1098 1099 KualiAccountingDocumentFormBase kualiAccountingDocumentFormBase = (KualiAccountingDocumentFormBase) form; 1100 CapitalAssetInformation capitalAssetInformation = this.getCurrentCapitalAssetInformationObject(kualiAccountingDocumentFormBase); 1101 if (capitalAssetInformation == null) { 1102 return mapping.findForward(KFSConstants.MAPPING_BASIC); 1103 } 1104 1105 this.addCapitalAssetInfoDetailLines(capitalAssetInformation); 1106 1107 return mapping.findForward(KFSConstants.MAPPING_BASIC); 1108 } 1109 1110 /** 1111 * delete a detail line from the capital asset information 1112 */ 1113 public ActionForward deleteCapitalAssetInfoDetailLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 1114 LOG.debug("deleteCapitalAssetDetailInfo() - start"); 1115 1116 KualiAccountingDocumentFormBase kualiAccountingDocumentFormBase = (KualiAccountingDocumentFormBase) form; 1117 CapitalAssetInformation capitalAssetInformation = this.getCurrentCapitalAssetInformationObject(kualiAccountingDocumentFormBase); 1118 if (capitalAssetInformation == null) { 1119 return mapping.findForward(KFSConstants.MAPPING_BASIC); 1120 } 1121 1122 int lineToDelete = this.getLineToDelete(request); 1123 List<CapitalAssetInformationDetail> detailLines = capitalAssetInformation.getCapitalAssetInformationDetails(); 1124 1125 detailLines.remove(lineToDelete); 1126 1127 return mapping.findForward(KFSConstants.MAPPING_BASIC); 1128 } 1129 1130 /** 1131 * get the capital asset information object currently associated with the document 1132 */ 1133 protected CapitalAssetInformation getCurrentCapitalAssetInformationObject(KualiAccountingDocumentFormBase kualiAccountingDocumentFormBase) { 1134 LOG.debug("getCurrentCapitalAssetInformationObject() - start"); 1135 1136 AccountingDocument financialDocument = kualiAccountingDocumentFormBase.getFinancialDocument(); 1137 if (!(financialDocument instanceof CapitalAssetEditable) || !(kualiAccountingDocumentFormBase instanceof CapitalAssetEditable)) { 1138 return null; 1139 } 1140 1141 CapitalAssetEditable capitalAssetEditable = (CapitalAssetEditable) financialDocument; 1142 CapitalAssetInformation capitalAssetInformation = capitalAssetEditable.getCapitalAssetInformation(); 1143 if (ObjectUtils.isNotNull(capitalAssetInformation)) { 1144 return capitalAssetInformation; 1145 } 1146 1147 CapitalAssetEditable capitalAssetEditableForm = (CapitalAssetEditable) kualiAccountingDocumentFormBase; 1148 CapitalAssetInformation newCapitalAssetInformation = capitalAssetEditableForm.getCapitalAssetInformation(); 1149 1150 return newCapitalAssetInformation; 1151 } 1152 1153 /** 1154 * add detail lines into the given capital asset information 1155 * 1156 * @param capitalAssetInformation the given capital asset information 1157 */ 1158 protected void addCapitalAssetInfoDetailLines(CapitalAssetInformation capitalAssetInformation) { 1159 LOG.debug("addCapitalAssetInfoDetailLines() - start"); 1160 1161 if (ObjectUtils.isNull(capitalAssetInformation)) { 1162 return; 1163 } 1164 1165 Integer quantity = capitalAssetInformation.getCapitalAssetQuantity(); 1166 if (quantity == null || quantity <= 0) { 1167 String errorPath = KFSPropertyConstants.DOCUMENT + "." + KFSPropertyConstants.CAPITAL_ASSET_INFORMATION; 1168 GlobalVariables.getMessageMap().putError(errorPath, KFSKeyConstants.ERROR_INVALID_CAPITAL_ASSET_QUANTITY); 1169 return; 1170 } 1171 1172 List<CapitalAssetInformationDetail> detailLines = capitalAssetInformation.getCapitalAssetInformationDetails(); 1173 // If details collection has old lines, this loop will add new lines to make the total equal to the quantity. 1174 for (int index = 1; detailLines.size() < quantity; index++) { 1175 CapitalAssetInformationDetail detailLine = new CapitalAssetInformationDetail(); 1176 detailLine.setItemLineNumber(getNextItemLineNumberAndIncremented(capitalAssetInformation)); 1177 detailLines.add(detailLine); 1178 } 1179 } 1180 1181 /** 1182 * Get next available item line number. If it's already stored in the session, pick it up and increment by 1. Otherwise get it 1183 * from the DB and save it to session. 1184 * 1185 * @param capitalAssetInformation 1186 * @return 1187 */ 1188 protected Integer getNextItemLineNumberAndIncremented(CapitalAssetInformation capitalAssetInformation) { 1189 Integer nextItemLineNumber = capitalAssetInformation.getNextItemLineNumber(); 1190 if (nextItemLineNumber == null) { 1191 nextItemLineNumber = new Integer(getMaxItemLineNumber(capitalAssetInformation) + 1); 1192 } 1193 capitalAssetInformation.setNextItemLineNumber(new Integer(nextItemLineNumber.intValue() + 1)); 1194 return nextItemLineNumber; 1195 } 1196 1197 /** 1198 * Get the maximum item line number from DB. 1199 * 1200 * @param capitalAssetInformation 1201 * @return 1202 */ 1203 protected int getMaxItemLineNumber(CapitalAssetInformation capitalAssetInformation) { 1204 int maxItemLineNumber = 0; 1205 if (ObjectUtils.isNotNull(capitalAssetInformation)) { 1206 List<CapitalAssetInformationDetail> detailLines = capitalAssetInformation.getCapitalAssetInformationDetails(); 1207 1208 if (detailLines != null && !detailLines.isEmpty()) { 1209 maxItemLineNumber = detailLines.size(); 1210 } 1211 1212 Map<String, Object> fieldValues = new HashMap<String, Object>(); 1213 fieldValues.put(KFSPropertyConstants.DOCUMENT_NUMBER, capitalAssetInformation.getDocumentNumber()); 1214 List<CapitalAssetInformationDetail> perisitentDetails = (List<CapitalAssetInformationDetail>) getBusinessObjectService().findMatching(CapitalAssetInformationDetail.class, fieldValues); 1215 for (CapitalAssetInformationDetail persistentDetail : perisitentDetails) { 1216 if (persistentDetail.getItemLineNumber().intValue() > maxItemLineNumber) { 1217 maxItemLineNumber = persistentDetail.getItemLineNumber().intValue(); 1218 } 1219 } 1220 } 1221 return maxItemLineNumber; 1222 } 1223 1224 /** 1225 * reset the nonkey fields of the given capital asset information 1226 * 1227 * @param capitalAssetInformation the given capital asset information 1228 */ 1229 protected void resetCapitalAssetInfo(CapitalAssetInformation capitalAssetInformation) { 1230 if (capitalAssetInformation != null) { 1231 capitalAssetInformation.setCapitalAssetDescription(null); 1232 capitalAssetInformation.setCapitalAssetManufacturerModelNumber(null); 1233 capitalAssetInformation.setCapitalAssetManufacturerName(null); 1234 1235 capitalAssetInformation.setCapitalAssetNumber(null); 1236 capitalAssetInformation.setCapitalAssetTypeCode(null); 1237 capitalAssetInformation.setCapitalAssetQuantity(null); 1238 1239 capitalAssetInformation.setVendorDetailAssignedIdentifier(null); 1240 capitalAssetInformation.setVendorHeaderGeneratedIdentifier(null); 1241 // Set the BO to null cause it won't be updated automatically when vendorDetailAssetIdentifier and 1242 // VendorHeanderGeneratedIndentifier set to null. 1243 capitalAssetInformation.setVendorDetail(null); 1244 capitalAssetInformation.setVendorName(null); 1245 1246 capitalAssetInformation.getCapitalAssetInformationDetails().clear(); 1247 } 1248 } 1249 1250 // assoicate the new capital asset information with the current document if any 1251 protected void applyCapitalAssetInformation(KualiAccountingDocumentFormBase kualiAccountingDocumentFormBase) { 1252 LOG.debug("applyCapitalAssetInformation() - start"); 1253 1254 // do nothing if the given document is not required to have capital asset collection 1255 AccountingDocument document = kualiAccountingDocumentFormBase.getFinancialDocument(); 1256 if (!(document instanceof CapitalAssetEditable)) { 1257 return; 1258 } 1259 1260 // do nothing if there exists capital asset information associated with the current document 1261 CapitalAssetEditable capitalAssetEditable = (CapitalAssetEditable) document; 1262 CapitalAssetInformation capitalAssetInformation = capitalAssetEditable.getCapitalAssetInformation(); 1263 if (capitalAssetInformation != null || !(kualiAccountingDocumentFormBase instanceof CapitalAssetEditable)) { 1264 return; 1265 } 1266 1267 CapitalAssetEditable capitalAssetEditableForm = (CapitalAssetEditable) kualiAccountingDocumentFormBase; 1268 CapitalAssetInformation newCapitalAssetInformation = capitalAssetEditableForm.getCapitalAssetInformation(); 1269 // apply capitalAsset information if there is at least one movable object code associated with the source accounting 1270 // lines 1271 newCapitalAssetInformation.setDocumentNumber(document.getDocumentNumber()); 1272 capitalAssetEditable.setCapitalAssetInformation(newCapitalAssetInformation); 1273 } 1274 1275 /** 1276 * Overridden to guarantee that form of copied document is set to whatever the entry mode of the document is 1277 * @see org.kuali.rice.kns.web.struts.action.KualiTransactionalDocumentActionBase#copy(org.apache.struts.action.ActionMapping, org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) 1278 */ 1279 @Override 1280 public ActionForward copy(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 1281 ActionForward forward = super.copy(mapping, form, request, response); 1282 1283 // if the copied document has capital asset collection, remove the collection 1284 KualiAccountingDocumentFormBase kualiAccountingDocumentFormBase = (KualiAccountingDocumentFormBase) form; 1285 AccountingDocument document = kualiAccountingDocumentFormBase.getFinancialDocument(); 1286 if (document instanceof CapitalAssetEditable) { 1287 1288 CapitalAssetEditable capitalAssetEditable = (CapitalAssetEditable) document; 1289 resetCapitalAssetInfo(capitalAssetEditable.getCapitalAssetInformation()); 1290 } 1291 1292 return forward; 1293 } 1294 1295 }