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.cab.document.web.struts; 017 018 019 import java.util.Collection; 020 import java.util.HashMap; 021 import java.util.List; 022 import java.util.Map; 023 024 import javax.servlet.http.HttpServletRequest; 025 import javax.servlet.http.HttpServletResponse; 026 027 import org.apache.commons.lang.StringUtils; 028 import org.apache.log4j.Logger; 029 import org.apache.struts.action.ActionForm; 030 import org.apache.struts.action.ActionForward; 031 import org.apache.struts.action.ActionMapping; 032 import org.kuali.kfs.module.cab.CabConstants; 033 import org.kuali.kfs.module.cab.CabKeyConstants; 034 import org.kuali.kfs.module.cab.CabPropertyConstants; 035 import org.kuali.kfs.module.cab.businessobject.PurchasingAccountsPayableDocument; 036 import org.kuali.kfs.module.cab.businessobject.PurchasingAccountsPayableItemAsset; 037 import org.kuali.kfs.module.cab.businessobject.PurchasingAccountsPayableLineAssetAccount; 038 import org.kuali.kfs.module.cab.document.service.PurApInfoService; 039 import org.kuali.kfs.module.cab.document.service.PurApLineDocumentService; 040 import org.kuali.kfs.module.cab.document.service.PurApLineService; 041 import org.kuali.kfs.module.cab.document.web.PurApLineSession; 042 import org.kuali.kfs.module.cam.CamsConstants; 043 import org.kuali.kfs.module.cam.CamsKeyConstants; 044 import org.kuali.kfs.module.cam.businessobject.AssetGlobal; 045 import org.kuali.kfs.sys.KFSConstants; 046 import org.kuali.kfs.sys.context.SpringContext; 047 import org.kuali.rice.kew.exception.WorkflowException; 048 import org.kuali.rice.kns.question.ConfirmationQuestion; 049 import org.kuali.rice.kns.service.BusinessObjectService; 050 import org.kuali.rice.kns.service.KualiConfigurationService; 051 import org.kuali.rice.kns.service.ParameterService; 052 import org.kuali.rice.kns.util.GlobalVariables; 053 import org.kuali.rice.kns.util.KNSConstants; 054 import org.kuali.rice.kns.util.KualiDecimal; 055 import org.kuali.rice.kns.util.ObjectUtils; 056 import org.kuali.rice.kns.util.RiceKeyConstants; 057 058 public class PurApLineAction extends CabActionBase { 059 private static final Logger LOG = Logger.getLogger(PurApLineAction.class); 060 PurApLineService purApLineService = SpringContext.getBean(PurApLineService.class); 061 PurApInfoService purApInfoService = SpringContext.getBean(PurApInfoService.class); 062 PurApLineDocumentService purApLineDocumentService = SpringContext.getBean(PurApLineDocumentService.class); 063 064 /** 065 * Handle start action. 066 * 067 * @param mapping 068 * @param form 069 * @param request 070 * @param response 071 * @return 072 * @throws Exception 073 */ 074 public ActionForward start(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 075 PurApLineForm purApLineForm = (PurApLineForm) form; 076 if (purApLineForm.getPurchaseOrderIdentifier() == null) { 077 GlobalVariables.getMessageMap().putError(KFSConstants.GLOBAL_ERRORS, CabKeyConstants.ERROR_PO_ID_EMPTY); 078 } 079 else { 080 // set Contact Email Address and Phone Number from PurAp Purchase Order document 081 purApInfoService.setPurchaseOrderFromPurAp(purApLineForm); 082 083 // save PurAp document list into form 084 buildPurApDocList(purApLineForm); 085 086 if (!purApLineForm.getPurApDocs().isEmpty()) { 087 // set item pre-populated fields 088 purApLineService.buildPurApItemAssetList(purApLineForm.getPurApDocs()); 089 // create session object for current processing 090 createPurApLineSession(purApLineForm.getPurchaseOrderIdentifier()); 091 } 092 } 093 return mapping.findForward(KFSConstants.MAPPING_BASIC); 094 } 095 096 protected void createPurApLineSession(Integer purchaseOrderIdentifier) { 097 GlobalVariables.getUserSession().addObject(CabConstants.CAB_PURAP_SESSION.concat(purchaseOrderIdentifier.toString()), new PurApLineSession()); 098 } 099 100 protected void clearPurApLineSession(Integer purchaseOrderIdentifier) { 101 if (purchaseOrderIdentifier != null) { 102 GlobalVariables.getUserSession().removeObject(CabConstants.CAB_PURAP_SESSION.concat(purchaseOrderIdentifier.toString())); 103 } 104 } 105 106 public ActionForward reload(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 107 PurApLineForm purApLineForm = (PurApLineForm) form; 108 if (purApLineForm.getPurchaseOrderIdentifier() == null) { 109 GlobalVariables.getMessageMap().putError(KFSConstants.GLOBAL_ERRORS, CabKeyConstants.ERROR_PO_ID_EMPTY); 110 } 111 purApLineForm.getPurApDocs().clear(); 112 // clear the session and reload the page 113 clearPurApLineSession(purApLineForm.getPurchaseOrderIdentifier()); 114 return start(mapping, form, request, response); 115 } 116 117 /** 118 * Build PurchasingAccountsPayableDocument list in which all documents have the same PO_ID. 119 * 120 * @param purApLineForm 121 */ 122 protected void buildPurApDocList(PurApLineForm purApLineForm) { 123 Map<String, Object> cols = new HashMap<String, Object>(); 124 cols.put(CabPropertyConstants.PurchasingAccountsPayableDocument.PURCHASE_ORDER_IDENTIFIER, purApLineForm.getPurchaseOrderIdentifier()); 125 Collection<PurchasingAccountsPayableDocument> purApDocs = SpringContext.getBean(BusinessObjectService.class).findMatchingOrderBy(PurchasingAccountsPayableDocument.class, cols, CabPropertyConstants.PurchasingAccountsPayableDocument.DOCUMENT_NUMBER, true); 126 127 if (purApDocs == null || purApDocs.isEmpty()) { 128 GlobalVariables.getMessageMap().putError(KFSConstants.GLOBAL_ERRORS, CabKeyConstants.ERROR_PO_ID_INVALID, purApLineForm.getPurchaseOrderIdentifier().toString()); 129 } 130 else { 131 boolean existActiveDoc = false; 132 for (PurchasingAccountsPayableDocument purApDoc : purApDocs) { 133 if (ObjectUtils.isNotNull(purApDoc) && purApDoc.isActive()) { 134 // If there exists active document, set the existActiveDoc indicator. 135 existActiveDoc = true; 136 break; 137 } 138 } 139 purApLineForm.getPurApDocs().clear(); 140 purApLineForm.getPurApDocs().addAll(purApDocs); 141 setupObjectRelationship(purApLineForm.getPurApDocs()); 142 // If no active item exists or no exist document, display a message. 143 if (!existActiveDoc) { 144 GlobalVariables.getMessageList().add(CabKeyConstants.MESSAGE_NO_ACTIVE_PURAP_DOC); 145 } 146 } 147 } 148 149 /** 150 * Setup relationship from account to item and item to doc. In this way, we keep all working objects in the same view as form. 151 * 152 * @param purApDocs 153 */ 154 protected void setupObjectRelationship(List<PurchasingAccountsPayableDocument> purApDocs) { 155 for (PurchasingAccountsPayableDocument purApDoc : purApDocs) { 156 for (PurchasingAccountsPayableItemAsset item : purApDoc.getPurchasingAccountsPayableItemAssets()) { 157 item.setPurchasingAccountsPayableDocument(purApDoc); 158 for (PurchasingAccountsPayableLineAssetAccount account : item.getPurchasingAccountsPayableLineAssetAccounts()) { 159 account.setPurchasingAccountsPayableItemAsset(item); 160 } 161 } 162 } 163 } 164 165 /** 166 * Cancels the action and returns to portal main page 167 * 168 * @param mapping {@link ActionMapping} 169 * @param form {@link ActionForm} 170 * @param request {@link HttpServletRequest} 171 * @param response {@link HttpServletResponse} 172 * @return {@link ActionForward} 173 * @throws Exception 174 */ 175 public ActionForward cancel(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 176 return mapping.findForward(KNSConstants.MAPPING_PORTAL); 177 } 178 179 /** 180 * save the information in the current form into underlying data store 181 */ 182 public ActionForward save(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 183 PurApLineForm purApLineForm = (PurApLineForm) form; 184 // get the current processing object from session 185 PurApLineSession purApLineSession = retrievePurApLineSession(purApLineForm); 186 // persistent changes to CAB tables 187 purApLineService.processSaveBusinessObjects(purApLineForm.getPurApDocs(), purApLineSession); 188 GlobalVariables.getMessageList().add(CabKeyConstants.MESSAGE_CAB_CHANGES_SAVED_SUCCESS); 189 return mapping.findForward(KFSConstants.MAPPING_BASIC); 190 } 191 192 /** 193 * Handling for screen close. Default action is return to caller. 194 */ 195 public ActionForward close(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 196 PurApLineForm purApLineForm = (PurApLineForm) form; 197 198 // Create question page for save before close. 199 Object question = request.getParameter(KNSConstants.QUESTION_INST_ATTRIBUTE_NAME); 200 KualiConfigurationService kualiConfiguration = SpringContext.getBean(KualiConfigurationService.class); 201 202 // logic for close question 203 if (question == null) { 204 // ask question if not already asked 205 return this.performQuestionWithoutInput(mapping, form, request, response, KNSConstants.DOCUMENT_SAVE_BEFORE_CLOSE_QUESTION, kualiConfiguration.getPropertyString(RiceKeyConstants.QUESTION_SAVE_BEFORE_CLOSE), KNSConstants.CONFIRMATION_QUESTION, KNSConstants.MAPPING_CLOSE, ""); 206 } 207 else { 208 Object buttonClicked = request.getParameter(KNSConstants.QUESTION_CLICKED_BUTTON); 209 PurApLineSession purApLineSession = retrievePurApLineSession(purApLineForm); 210 if ((KNSConstants.DOCUMENT_SAVE_BEFORE_CLOSE_QUESTION.equals(question)) && ConfirmationQuestion.YES.equals(buttonClicked)) { 211 purApLineService.processSaveBusinessObjects(purApLineForm.getPurApDocs(), purApLineSession); 212 } 213 // remove current processing object from session 214 removePurApLineSession(purApLineForm.getPurchaseOrderIdentifier()); 215 } 216 217 return mapping.findForward(KNSConstants.MAPPING_PORTAL); 218 } 219 220 /** 221 * Remove PurApLineSession object from user session. 222 * 223 * @param purApLineForm 224 */ 225 private void removePurApLineSession(Integer purchaseOrderIdentifier) { 226 GlobalVariables.getUserSession().removeObject(CabConstants.CAB_PURAP_SESSION.concat(purchaseOrderIdentifier.toString())); 227 } 228 229 230 /** 231 * This method handles split action. Create one item with split quantity 232 * 233 * @param mapping 234 * @param form 235 * @param request 236 * @param response 237 * @return 238 * @throws Exception 239 */ 240 public ActionForward split(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 241 PurApLineForm purApLineForm = (PurApLineForm) form; 242 // Get the line item for applying split action. 243 PurchasingAccountsPayableItemAsset selectedLineItem = getSelectedLineItem((PurApLineForm) form); 244 245 if (selectedLineItem == null) { 246 return mapping.findForward(KFSConstants.MAPPING_BASIC); 247 } 248 249 String errorPath = CabPropertyConstants.PurApLineForm.PURAP_DOCS + KFSConstants.SQUARE_BRACKET_LEFT + purApLineForm.getActionPurApDocIndex() + KFSConstants.SQUARE_BRACKET_RIGHT + "." + CabPropertyConstants.PurchasingAccountsPayableDocument.PURCHASEING_ACCOUNTS_PAYABLE_ITEM_ASSETS + KFSConstants.SQUARE_BRACKET_LEFT + purApLineForm.getActionItemAssetIndex() + KFSConstants.SQUARE_BRACKET_RIGHT; 250 GlobalVariables.getMessageMap().addToErrorPath(errorPath); 251 // check user input split quantity. 252 checkSplitQty(selectedLineItem, errorPath); 253 GlobalVariables.getMessageMap().removeFromErrorPath(errorPath); 254 255 // apply split when error free 256 if (GlobalVariables.getMessageMap().hasNoErrors() && selectedLineItem != null) { 257 PurApLineSession purApLineSession = retrievePurApLineSession(purApLineForm); 258 // create a new item with split quantity from selected item 259 purApLineService.processSplit(selectedLineItem, purApLineSession.getActionsTakenHistory()); 260 } 261 262 return mapping.findForward(KFSConstants.MAPPING_BASIC); 263 } 264 265 /** 266 * Get PurApLineSession object from user session. 267 * 268 * @param purApLineForm 269 * @return 270 */ 271 private PurApLineSession retrievePurApLineSession(PurApLineForm purApForm) { 272 PurApLineSession purApLineSession = (PurApLineSession) GlobalVariables.getUserSession().retrieveObject(CabConstants.CAB_PURAP_SESSION.concat(purApForm.getPurchaseOrderIdentifier().toString())); 273 if (purApLineSession == null) { 274 purApLineSession = new PurApLineSession(); 275 GlobalVariables.getUserSession().addObject(CabConstants.CAB_PURAP_SESSION.concat(purApForm.getPurchaseOrderIdentifier().toString()), purApLineSession); 276 } 277 return purApLineSession; 278 } 279 280 /** 281 * Check user input splitQty. It must be required and can't be zero or greater than current quantity. 282 * 283 * @param itemAsset 284 * @param errorPath 285 */ 286 protected void checkSplitQty(PurchasingAccountsPayableItemAsset itemAsset, String errorPath) { 287 if (itemAsset.getSplitQty() == null) 288 itemAsset.setSplitQty(KualiDecimal.ZERO); 289 290 if (itemAsset.getAccountsPayableItemQuantity() == null) 291 itemAsset.setAccountsPayableItemQuantity(KualiDecimal.ZERO); 292 293 KualiDecimal splitQty = itemAsset.getSplitQty(); 294 KualiDecimal oldQty = itemAsset.getAccountsPayableItemQuantity(); 295 KualiDecimal maxAllowQty = oldQty.subtract(new KualiDecimal(0.1)); 296 297 if (splitQty == null) { 298 GlobalVariables.getMessageMap().putError(CabPropertyConstants.PurchasingAccountsPayableItemAsset.SPLIT_QTY, CabKeyConstants.ERROR_SPLIT_QTY_REQUIRED); 299 } 300 else if (splitQty.isLessEqual(KualiDecimal.ZERO) || splitQty.isGreaterEqual(oldQty)) { 301 GlobalVariables.getMessageMap().putError(CabPropertyConstants.PurchasingAccountsPayableItemAsset.SPLIT_QTY, CabKeyConstants.ERROR_SPLIT_QTY_INVALID, maxAllowQty.toString()); 302 } 303 return; 304 } 305 306 /** 307 * Merge Action includes merge all functionality. 308 * 309 * @param mapping 310 * @param form 311 * @param request 312 * @param response 313 * @return 314 * @throws Exception 315 */ 316 public ActionForward merge(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 317 PurApLineForm purApForm = (PurApLineForm) form; 318 boolean isMergeAll = purApLineService.isMergeAllAction(purApForm.getPurApDocs()); 319 List<PurchasingAccountsPayableItemAsset> mergeLines = purApLineService.getSelectedMergeLines(isMergeAll, purApForm.getPurApDocs()); 320 321 Object question = request.getParameter(KNSConstants.QUESTION_INST_ATTRIBUTE_NAME); 322 // logic for trade-in allowance question 323 if (question != null) { 324 Object buttonClicked = request.getParameter(KNSConstants.QUESTION_CLICKED_BUTTON); 325 if (CabConstants.TRADE_IN_INDICATOR_QUESTION.equals(question) && ConfirmationQuestion.YES.equals(buttonClicked)) { 326 if (purApLineService.mergeLinesHasDifferentObjectSubTypes(mergeLines)) { 327 // check if objectSubTypes are different and bring the obj sub types warning message 328 String warningMessage = generateObjectSubTypeQuestion(); 329 return this.performQuestionWithoutInput(mapping, form, request, response, CabConstants.PAYMENT_DIFFERENT_OBJECT_SUB_TYPE_CONFIRMATION_QUESTION, warningMessage, KNSConstants.CONFIRMATION_QUESTION, CabConstants.Actions.MERGE, ""); 330 } 331 else { 332 performMerge(purApForm, mergeLines, isMergeAll); 333 } 334 } 335 else if (CabConstants.PAYMENT_DIFFERENT_OBJECT_SUB_TYPE_CONFIRMATION_QUESTION.equals(question) && ConfirmationQuestion.YES.equals(buttonClicked)) { 336 performMerge(purApForm, mergeLines, isMergeAll); 337 } 338 339 return mapping.findForward(KFSConstants.MAPPING_BASIC); 340 } 341 342 boolean tradeInAllowanceInAllLines = purApLineService.isTradeInAllowanceExist(purApForm.getPurApDocs()); 343 boolean tradeInIndicatorInSelectedLines = purApLineService.isTradeInIndExistInSelectedLines(mergeLines); 344 // validating... 345 validateMergeAction(purApForm, mergeLines, isMergeAll, tradeInAllowanceInAllLines, tradeInIndicatorInSelectedLines); 346 347 if (GlobalVariables.getMessageMap().hasNoErrors()) { 348 // Display a warning message without blocking the action if TI indicator exists but TI allowance not exist. 349 if (tradeInIndicatorInSelectedLines && !tradeInAllowanceInAllLines) { 350 return this.performQuestionWithoutInput(mapping, form, request, response, CabConstants.TRADE_IN_INDICATOR_QUESTION, SpringContext.getBean(KualiConfigurationService.class).getPropertyString(CabKeyConstants.QUESTION_TRADE_IN_INDICATOR_EXISTING), KNSConstants.CONFIRMATION_QUESTION, CabConstants.Actions.MERGE, ""); 351 } 352 else if (purApLineService.mergeLinesHasDifferentObjectSubTypes(mergeLines)) { 353 // check if objectSubTypes are different and bring the obj sub types warning message 354 String warningMessage = generateObjectSubTypeQuestion(); 355 return this.performQuestionWithoutInput(mapping, form, request, response, CabConstants.PAYMENT_DIFFERENT_OBJECT_SUB_TYPE_CONFIRMATION_QUESTION, warningMessage, KNSConstants.CONFIRMATION_QUESTION, CabConstants.Actions.MERGE, ""); 356 } 357 else { 358 performMerge(purApForm, mergeLines, isMergeAll); 359 } 360 } 361 362 return mapping.findForward(KFSConstants.MAPPING_BASIC); 363 } 364 365 366 /** 367 * Generate the question string for different object sub type codes. 368 * 369 * @return 370 */ 371 protected String generateObjectSubTypeQuestion() { 372 String parameterDetail = "(module:" + getParameterService().getNamespace(AssetGlobal.class) + "/component:" + getParameterService().getDetailType(AssetGlobal.class) + ")"; 373 KualiConfigurationService kualiConfiguration = this.getKualiConfigurationService(); 374 375 String continueQuestion = kualiConfiguration.getPropertyString(CamsKeyConstants.CONTINUE_QUESTION); 376 String warningMessage = kualiConfiguration.getPropertyString(CabKeyConstants.QUESTION_DIFFERENT_OBJECT_SUB_TYPES) + " " + CamsConstants.Parameters.OBJECT_SUB_TYPE_GROUPS + " " + parameterDetail + ". " + continueQuestion; 377 return warningMessage; 378 } 379 380 /** 381 * Merge with service help. 382 * 383 * @param purApForm 384 * @param mergeLines 385 */ 386 protected void performMerge(PurApLineForm purApForm, List<PurchasingAccountsPayableItemAsset> mergeLines, boolean isMergeAll) { 387 PurApLineSession purApLineSession = retrievePurApLineSession(purApForm); 388 // handle merging lines including merge all situation. 389 retrieveUserInputForMerge(mergeLines.get(0), purApForm); 390 purApLineService.processMerge(mergeLines, purApLineSession.getActionsTakenHistory(), isMergeAll); 391 // add all other mergeLines except the first one into processedItem list. 392 mergeLines.remove(0); 393 purApLineSession.getProcessedItems().addAll(mergeLines); 394 clearForMerge(purApForm); 395 } 396 397 /** 398 * Retrieve user input merge quantity and merge description. 399 * 400 * @param firstItem 401 * @param purApForm 402 */ 403 protected void retrieveUserInputForMerge(PurchasingAccountsPayableItemAsset firstItem, PurApLineForm purApForm) { 404 if (ObjectUtils.isNotNull(firstItem)) { 405 // Set new value for quantity and description. 406 firstItem.setAccountsPayableItemQuantity(purApForm.getMergeQty()); 407 firstItem.setAccountsPayableLineItemDescription(purApForm.getMergeDesc()); 408 } 409 } 410 411 /** 412 * Clear user input after merge. 413 * 414 * @param purApForm 415 */ 416 protected void clearForMerge(PurApLineForm purApForm) { 417 // reset user input values. 418 purApLineService.resetSelectedValue(purApForm.getPurApDocs()); 419 purApForm.setMergeQty(null); 420 purApForm.setMergeDesc(null); 421 purApForm.setSelectAll(false); 422 } 423 424 /** 425 * Check if the merge action is valid or not. 426 * 427 * @param purApForm 428 * @param mergeLines 429 */ 430 protected void validateMergeAction(PurApLineForm purApForm, List<PurchasingAccountsPayableItemAsset> mergeLines, boolean isMergeAll, boolean tradeInAllowanceInAllLines, boolean tradeInIndicatorInSelectedLines) { 431 // check if the user entered merge quantity and merge description 432 checkMergeRequiredFields(purApForm); 433 434 // Check if the selected merge lines violate the business constraints. 435 if (isMergeAll) { 436 checkMergeAllValid(tradeInAllowanceInAllLines, tradeInIndicatorInSelectedLines); 437 } 438 else { 439 checkMergeLinesValid(mergeLines, tradeInAllowanceInAllLines, tradeInIndicatorInSelectedLines); 440 } 441 442 // Check the associated pre-tagging data entries. 443 checkPreTagValidForMerge(mergeLines, purApForm.getPurchaseOrderIdentifier()); 444 } 445 446 /** 447 * If to be merged items have: (1) No Pretag data: No problem; (2) 1 Pretag data entry: Associate this one with the new item 448 * created after merge;(3) 1+ Pretag data entries: Display error, user has to manually fix data 449 * 450 * @param mergeLines 451 */ 452 protected void checkPreTagValidForMerge(List<PurchasingAccountsPayableItemAsset> mergeLines, Integer purchaseOrderIdentifier) { 453 Map validNumberMap = getItemLineNumberMap(mergeLines); 454 455 if (!validNumberMap.isEmpty() && validNumberMap.size() > 1 && purApLineService.isMultipleTagExisting(purchaseOrderIdentifier, validNumberMap.keySet())) { 456 GlobalVariables.getMessageMap().putError(CabPropertyConstants.PurApLineForm.PURAP_DOCS, CabKeyConstants.ERROR_MERGE_WITH_PRETAGGING); 457 } 458 } 459 460 /** 461 * Build a Hashmap for itemLineNumber since itemLines could exist duplicate itemLineNumber 462 * 463 * @param itemLines 464 * @return 465 */ 466 protected Map getItemLineNumberMap(List<PurchasingAccountsPayableItemAsset> itemLines) { 467 Map validNumberMap = new HashMap<Integer, Integer>(); 468 for (PurchasingAccountsPayableItemAsset item : itemLines) { 469 if (item.getItemLineNumber() != null) { 470 validNumberMap.put(item.getItemLineNumber(), item.getItemLineNumber()); 471 } 472 } 473 return validNumberMap; 474 } 475 476 /** 477 * For merge all, check if exists Trade-in allowance pending for allocate. 478 * 479 * @param mergeLines 480 * @param purApForm 481 */ 482 protected void checkMergeAllValid(boolean tradeInAllowanceInAllLines, boolean tradeInIndicatorInSelectedLines) { 483 if (tradeInAllowanceInAllLines && tradeInIndicatorInSelectedLines) { 484 GlobalVariables.getMessageMap().putError(CabPropertyConstants.PurApLineForm.PURAP_DOCS, CabKeyConstants.ERROR_TRADE_IN_PENDING); 485 } 486 } 487 488 /** 489 * Check if merge lines selected are allowed to continue this action. Constraints include: merge lines must more than 1; no 490 * additional charges pending for allocate. 491 * 492 * @param mergeLines 493 * @param purApForm 494 */ 495 protected void checkMergeLinesValid(List<PurchasingAccountsPayableItemAsset> mergeLines, boolean tradeInAllowanceInAllLines, boolean tradeInIndicatorInSelectedLines) { 496 if (mergeLines.size() <= 1) { 497 GlobalVariables.getMessageMap().putError(CabPropertyConstants.PurApLineForm.PURAP_DOCS, CabKeyConstants.ERROR_MERGE_LINE_SELECTED); 498 } 499 else { 500 // if merge for different document lines and that document has additional charge allocation pending, additional charges 501 // should be allocated first. 502 if (purApLineService.isAdditionalChargePending(mergeLines)) { 503 GlobalVariables.getMessageMap().putError(CabPropertyConstants.PurApLineForm.PURAP_DOCS, CabKeyConstants.ERROR_ADDL_CHARGE_PENDING); 504 } 505 506 // if merge lines has indicator exists and trade-in allowance is pending for allocation, we will block this action. 507 if (tradeInIndicatorInSelectedLines && tradeInAllowanceInAllLines) { 508 GlobalVariables.getMessageMap().putError(CabPropertyConstants.PurApLineForm.PURAP_DOCS, CabKeyConstants.ERROR_TRADE_IN_PENDING); 509 } 510 } 511 } 512 513 514 /** 515 * Check the required fields entered for merge. 516 */ 517 protected void checkMergeRequiredFields(PurApLineForm purApForm) { 518 if (purApForm.getMergeQty() == null) { 519 GlobalVariables.getMessageMap().putError(CabPropertyConstants.PurApLineForm.MERGE_QTY, CabKeyConstants.ERROR_MERGE_QTY_EMPTY); 520 } 521 522 if (StringUtils.isBlank(purApForm.getMergeDesc())) { 523 GlobalVariables.getMessageMap().putError(CabPropertyConstants.PurApLineForm.MERGE_DESC, CabKeyConstants.ERROR_MERGE_DESCRIPTION_EMPTY); 524 } 525 } 526 527 528 /** 529 * Update the item quantity value from a decimal(less than 1) to 1. 530 * 531 * @param mapping 532 * @param form 533 * @param request 534 * @param response 535 * @return 536 * @throws Exception 537 */ 538 public ActionForward percentPayment(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 539 PurApLineForm purApform = (PurApLineForm) form; 540 PurchasingAccountsPayableItemAsset itemAsset = getSelectedLineItem(purApform); 541 542 if (itemAsset != null) { 543 PurApLineSession purApLineSession = retrievePurApLineSession(purApform); 544 purApLineService.processPercentPayment(itemAsset, purApLineSession.getActionsTakenHistory()); 545 } 546 547 return mapping.findForward(KFSConstants.MAPPING_BASIC); 548 } 549 550 /** 551 * Allocate line items including allocate additional charges functionality. 552 * 553 * @param mapping 554 * @param form 555 * @param request 556 * @param response 557 * @return 558 * @throws Exception 559 */ 560 public ActionForward allocate(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 561 PurApLineForm purApForm = (PurApLineForm) form; 562 PurchasingAccountsPayableItemAsset allocateSourceLine = getSelectedLineItem(purApForm); 563 if (allocateSourceLine == null) { 564 return mapping.findForward(KFSConstants.MAPPING_BASIC); 565 } 566 List<PurchasingAccountsPayableItemAsset> allocateTargetLines = purApLineService.getAllocateTargetLines(allocateSourceLine, purApForm.getPurApDocs()); 567 568 Object question = request.getParameter(KNSConstants.QUESTION_INST_ATTRIBUTE_NAME); 569 // logic for trade-in allowance question 570 if (question != null) { 571 Object buttonClicked = request.getParameter(KNSConstants.QUESTION_CLICKED_BUTTON); 572 if ((CabConstants.TRADE_IN_INDICATOR_QUESTION.equals(question)) && ConfirmationQuestion.YES.equals(buttonClicked)) { 573 if (purApLineService.allocateLinesHasDifferentObjectSubTypes(allocateTargetLines, allocateSourceLine)) { 574 // check if objectSubTypes are different and bring the obj sub types warning message 575 String warningMessage = generateObjectSubTypeQuestion(); 576 return this.performQuestionWithoutInput(mapping, form, request, response, CabConstants.PAYMENT_DIFFERENT_OBJECT_SUB_TYPE_CONFIRMATION_QUESTION, warningMessage, KNSConstants.CONFIRMATION_QUESTION, CabConstants.Actions.ALLOCATE, ""); 577 } 578 else { 579 performAllocate(purApForm, allocateSourceLine, allocateTargetLines); 580 } 581 } 582 else if (CabConstants.PAYMENT_DIFFERENT_OBJECT_SUB_TYPE_CONFIRMATION_QUESTION.equals(question) && ConfirmationQuestion.YES.equals(buttonClicked)) { 583 performAllocate(purApForm, allocateSourceLine, allocateTargetLines); 584 } 585 586 return mapping.findForward(KFSConstants.MAPPING_BASIC); 587 } 588 589 boolean targetLineHasTradeIn = purApLineService.isTradeInIndExistInSelectedLines(allocateTargetLines); 590 boolean hasTradeInAllowance = purApLineService.isTradeInAllowanceExist(purApForm.getPurApDocs()); 591 // Check if this allocate is valid. 592 validateAllocateAction(allocateSourceLine, allocateTargetLines, targetLineHasTradeIn, hasTradeInAllowance, purApForm.getPurApDocs()); 593 if (GlobalVariables.getMessageMap().hasNoErrors()) { 594 // TI indicator exists in either source or target lines, but TI allowance not found, bring up a warning message 595 // to confirm this action. 596 if (!allocateSourceLine.isAdditionalChargeNonTradeInIndicator() && !allocateSourceLine.isTradeInAllowance() && (allocateSourceLine.isItemAssignedToTradeInIndicator() || targetLineHasTradeIn) && hasTradeInAllowance) { 597 return this.performQuestionWithoutInput(mapping, form, request, response, CabConstants.TRADE_IN_INDICATOR_QUESTION, SpringContext.getBean(KualiConfigurationService.class).getPropertyString(CabKeyConstants.QUESTION_TRADE_IN_INDICATOR_EXISTING), KNSConstants.CONFIRMATION_QUESTION, CabConstants.Actions.ALLOCATE, ""); 598 } 599 else if (purApLineService.allocateLinesHasDifferentObjectSubTypes(allocateTargetLines, allocateSourceLine)) { 600 // check if objectSubTypes are different and bring the obj sub types warning message 601 String warningMessage = generateObjectSubTypeQuestion(); 602 return this.performQuestionWithoutInput(mapping, form, request, response, CabConstants.PAYMENT_DIFFERENT_OBJECT_SUB_TYPE_CONFIRMATION_QUESTION, warningMessage, KNSConstants.CONFIRMATION_QUESTION, CabConstants.Actions.ALLOCATE, ""); 603 } 604 else { 605 performAllocate(purApForm, allocateSourceLine, allocateTargetLines); 606 } 607 } 608 609 return mapping.findForward(KFSConstants.MAPPING_BASIC); 610 } 611 612 /** 613 * Allocate with service help. 614 * 615 * @param purApForm 616 * @param allocateSourceLine 617 * @param allocateTargetLines 618 */ 619 protected void performAllocate(PurApLineForm purApForm, PurchasingAccountsPayableItemAsset allocateSourceLine, List<PurchasingAccountsPayableItemAsset> allocateTargetLines) { 620 PurApLineSession purApLineSession = retrievePurApLineSession(purApForm); 621 if (!purApLineService.processAllocate(allocateSourceLine, allocateTargetLines, purApLineSession.getActionsTakenHistory(), purApForm.getPurApDocs(), false)) { 622 GlobalVariables.getMessageMap().putError(CabPropertyConstants.PurApLineForm.PURAP_DOCS, CabKeyConstants.ERROR_ALLOCATE_NO_TARGET_ACCOUNT); 623 } 624 else { 625 purApLineSession.getProcessedItems().add(allocateSourceLine); 626 // clear select check box 627 purApLineService.resetSelectedValue(purApForm.getPurApDocs()); 628 purApForm.setSelectAll(false); 629 } 630 } 631 632 /** 633 * Check if the line items are allowed to allocate. 634 * 635 * @param selectedLine 636 * @param allocateTargetLines 637 * @param purApForm 638 */ 639 protected void validateAllocateAction(PurchasingAccountsPayableItemAsset allocateSourceLine, List<PurchasingAccountsPayableItemAsset> allocateTargetLines, boolean targetLineHasTradeIn, boolean hasTradeInAllowance, List<PurchasingAccountsPayableDocument> purApDocs) { 640 // if no target selected... 641 if (allocateTargetLines.isEmpty()) { 642 GlobalVariables.getMessageMap().putError(CabPropertyConstants.PurApLineForm.PURAP_DOCS, CabKeyConstants.ERROR_ALLOCATE_NO_LINE_SELECTED); 643 } 644 645 // For allocate trade-in allowance, additional charges(non trade-in) must be allocated before allocate trade-in. 646 if (allocateSourceLine.isTradeInAllowance() && purApLineService.isAdditionalChargeExistInAllLines(purApDocs)) { 647 GlobalVariables.getMessageMap().putError(CabPropertyConstants.PurApLineForm.PURAP_DOCS, CabKeyConstants.ERROR_ADDL_CHARGE_PENDING); 648 } 649 // For line item, we need to check... 650 if (!allocateSourceLine.isAdditionalChargeNonTradeInIndicator() && !allocateSourceLine.isTradeInAllowance()) { 651 allocateTargetLines.add(allocateSourceLine); 652 // Pending additional charges(non trade-in) can't associate with either source line or target lines. 653 if (purApLineService.isAdditionalChargePending(allocateTargetLines)) { 654 GlobalVariables.getMessageMap().putError(CabPropertyConstants.PurApLineForm.PURAP_DOCS, CabKeyConstants.ERROR_ADDL_CHARGE_PENDING); 655 } 656 657 // For line item, check if trade-in allowance allocation pending. 658 if (targetLineHasTradeIn && hasTradeInAllowance) { 659 GlobalVariables.getMessageMap().putError(CabPropertyConstants.PurApLineForm.PURAP_DOCS, CabKeyConstants.ERROR_TRADE_IN_PENDING); 660 } 661 allocateTargetLines.remove(allocateSourceLine); 662 } 663 664 } 665 666 /** 667 * Get the user selected line item and set the link reference to document 668 * 669 * @param purApLineForm 670 * @return 671 */ 672 private PurchasingAccountsPayableItemAsset getSelectedLineItem(PurApLineForm purApLineForm) { 673 PurchasingAccountsPayableDocument purApDoc = purApLineForm.getPurApDocs().get(purApLineForm.getActionPurApDocIndex()); 674 PurchasingAccountsPayableItemAsset selectedItem = purApDoc.getPurchasingAccountsPayableItemAssets().get(purApLineForm.getActionItemAssetIndex()); 675 if (!selectedItem.isActive()) { 676 selectedItem = null; 677 } 678 return selectedItem; 679 } 680 681 682 /** 683 * Get the user selected document. 684 * 685 * @param purApLineForm 686 * @return 687 */ 688 private PurchasingAccountsPayableDocument getSelectedPurApDoc(PurApLineForm purApLineForm) { 689 return purApLineForm.getPurApDocs().get(purApLineForm.getActionPurApDocIndex()); 690 } 691 692 /** 693 * Handle apply payment action. 694 * 695 * @param mapping 696 * @param form 697 * @param request 698 * @param response 699 * @return 700 * @throws Exception 701 */ 702 public ActionForward applyPayment(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 703 PurApLineForm purApForm = (PurApLineForm) form; 704 PurchasingAccountsPayableItemAsset selectedLine = getSelectedLineItem(purApForm); 705 706 if (selectedLine == null) { 707 return mapping.findForward(KFSConstants.MAPPING_BASIC); 708 } 709 710 Object question = request.getParameter(KNSConstants.QUESTION_INST_ATTRIBUTE_NAME); 711 if (question != null) { 712 Object buttonClicked = request.getParameter(KNSConstants.QUESTION_CLICKED_BUTTON); 713 if ((CabConstants.TRADE_IN_INDICATOR_QUESTION.equals(question)) && ConfirmationQuestion.YES.equals(buttonClicked)) { 714 // create CAMS asset payment global document. 715 return createApplyPaymentDocument(mapping, purApForm, selectedLine); 716 } 717 else { 718 return mapping.findForward(KFSConstants.MAPPING_BASIC); 719 } 720 } 721 if (selectedLine.isItemAssignedToTradeInIndicator()) { 722 // TI indicator exists, bring up a warning message to confirm this action. 723 return this.performQuestionWithoutInput(mapping, form, request, response, CabConstants.TRADE_IN_INDICATOR_QUESTION, SpringContext.getBean(KualiConfigurationService.class).getPropertyString(CabKeyConstants.QUESTION_TRADE_IN_INDICATOR_EXISTING), KNSConstants.CONFIRMATION_QUESTION, CabConstants.Actions.APPLY_PAYMENT, ""); 724 } 725 726 // create CAMS asset payment global document. 727 return createApplyPaymentDocument(mapping, purApForm, selectedLine); 728 } 729 730 /** 731 * Create CAMS asset payment document. 732 * 733 * @param mapping 734 * @param purApForm 735 * @param selectedLine 736 * @param purApLineSession 737 * @return 738 * @throws WorkflowException 739 */ 740 private ActionForward createApplyPaymentDocument(ActionMapping mapping, PurApLineForm purApForm, PurchasingAccountsPayableItemAsset selectedLine) throws WorkflowException { 741 PurApLineSession purApLineSession = retrievePurApLineSession(purApForm); 742 String documentNumber; 743 // create CAMS asset payment global document. 744 if ((documentNumber = purApLineDocumentService.processApplyPayment(selectedLine, purApForm.getPurApDocs(), purApLineSession, purApForm.getRequisitionIdentifier())) != null) { 745 purApForm.setDocumentNumber(documentNumber); 746 } 747 748 return mapping.findForward(KFSConstants.MAPPING_BASIC); 749 750 } 751 752 /** 753 * Handle create asset action. 754 * 755 * @param mapping 756 * @param form 757 * @param request 758 * @param response 759 * @return 760 * @throws Exception 761 */ 762 public ActionForward createAsset(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 763 PurApLineForm purApForm = (PurApLineForm) form; 764 PurchasingAccountsPayableItemAsset selectedLine = getSelectedLineItem(purApForm); 765 766 if (selectedLine == null) { 767 return mapping.findForward(KFSConstants.MAPPING_BASIC); 768 } 769 770 Object question = request.getParameter(KNSConstants.QUESTION_INST_ATTRIBUTE_NAME); 771 if (question != null) { 772 Object buttonClicked = request.getParameter(KNSConstants.QUESTION_CLICKED_BUTTON); 773 if ((CabConstants.TRADE_IN_INDICATOR_QUESTION.equals(question)) && ConfirmationQuestion.YES.equals(buttonClicked)) { 774 // If PurAp user set capitalAssetNumbers for apply Asset Payment document, bring up a warning message for 775 // confirmation. 776 if (isSettingAssetsInPurAp(selectedLine)) { 777 return this.performQuestionWithoutInput(mapping, form, request, response, CabConstants.SKIP_ASSET_NUMBERS_TO_ASSET_GLOBAL_QUESTION, SpringContext.getBean(KualiConfigurationService.class).getPropertyString(CabKeyConstants.QUESTION_SKIP_ASSET_NUMBERS_TO_ASSET_GLOBAL), KNSConstants.CONFIRMATION_QUESTION, CabConstants.Actions.CREATE_ASSET, ""); 778 } 779 else { 780 return createAssetGlobalDocument(mapping, purApForm, selectedLine); 781 } 782 } 783 else if (CabConstants.SKIP_ASSET_NUMBERS_TO_ASSET_GLOBAL_QUESTION.equals(question) && ConfirmationQuestion.YES.equals(buttonClicked)) { 784 return createAssetGlobalDocument(mapping, purApForm, selectedLine); 785 } 786 return mapping.findForward(KFSConstants.MAPPING_BASIC); 787 } 788 789 // validate selected line item 790 validateCreateAssetAction(selectedLine); 791 792 if (GlobalVariables.getMessageMap().hasNoErrors()) { 793 // TI indicator exists, bring up a warning message to confirm this action. 794 if (selectedLine.isItemAssignedToTradeInIndicator()) { 795 return this.performQuestionWithoutInput(mapping, form, request, response, CabConstants.TRADE_IN_INDICATOR_QUESTION, SpringContext.getBean(KualiConfigurationService.class).getPropertyString(CabKeyConstants.QUESTION_TRADE_IN_INDICATOR_EXISTING), KNSConstants.CONFIRMATION_QUESTION, CabConstants.Actions.CREATE_ASSET, ""); 796 } 797 // If PurAp user set capitalAssetNumbers for apply Asset Payment document, bring up a warning message to confirm using 798 // Asset Global document. 799 else if (isSettingAssetsInPurAp(selectedLine)) { 800 return this.performQuestionWithoutInput(mapping, form, request, response, CabConstants.SKIP_ASSET_NUMBERS_TO_ASSET_GLOBAL_QUESTION, SpringContext.getBean(KualiConfigurationService.class).getPropertyString(CabKeyConstants.QUESTION_SKIP_ASSET_NUMBERS_TO_ASSET_GLOBAL), KNSConstants.CONFIRMATION_QUESTION, CabConstants.Actions.CREATE_ASSET, ""); 801 } 802 else { 803 return createAssetGlobalDocument(mapping, purApForm, selectedLine); 804 } 805 } 806 807 return mapping.findForward(KFSConstants.MAPPING_BASIC); 808 } 809 810 /** 811 * check if PurAp set CAMS Assets information 812 * 813 * @param selectedLine 814 * @return 815 */ 816 private boolean isSettingAssetsInPurAp(PurchasingAccountsPayableItemAsset selectedLine) { 817 return selectedLine.getPurApItemAssets() != null && !selectedLine.getPurApItemAssets().isEmpty(); 818 } 819 820 /** 821 * Create asset global document 822 * 823 * @param mapping 824 * @param purApForm 825 * @param selectedLine 826 * @param purApLineSession 827 * @return 828 * @throws WorkflowException 829 */ 830 private ActionForward createAssetGlobalDocument(ActionMapping mapping, PurApLineForm purApForm, PurchasingAccountsPayableItemAsset selectedLine) throws WorkflowException { 831 PurApLineSession purApLineSession = retrievePurApLineSession(purApForm); 832 String documentNumber = null; 833 // create CAMS asset global document. 834 if ((documentNumber = purApLineDocumentService.processCreateAsset(selectedLine, purApForm.getPurApDocs(), purApLineSession, purApForm.getRequisitionIdentifier())) != null) { 835 // forward link to asset global 836 purApForm.setDocumentNumber(documentNumber); 837 } 838 839 return mapping.findForward(KFSConstants.MAPPING_BASIC); 840 } 841 842 /** 843 * Validate selected line item for asset global creation. 844 * 845 * @param selectedLine 846 */ 847 protected void validateCreateAssetAction(PurchasingAccountsPayableItemAsset selectedLine) { 848 KualiDecimal integerOne = new KualiDecimal(1); 849 KualiDecimal quantity = selectedLine.getAccountsPayableItemQuantity(); 850 // check if item quantity is a fractional value greater than 1. 851 if (quantity.isGreaterThan(integerOne) && quantity.mod(integerOne).isNonZero()) { 852 GlobalVariables.getMessageMap().putError(CabPropertyConstants.PurApLineForm.PURAP_DOCS, CabKeyConstants.ERROR_FRACTIONAL_QUANTITY); 853 } 854 // if quantity is between (0,1) , set it to 1. 855 else if (quantity.isGreaterThan(KualiDecimal.ZERO) && quantity.isLessThan(integerOne)) { 856 selectedLine.setAccountsPayableItemQuantity(integerOne); 857 } 858 859 } 860 861 protected ParameterService getParameterService() { 862 return (ParameterService) SpringContext.getBean(ParameterService.class); 863 } 864 865 protected KualiConfigurationService getKualiConfigurationService() { 866 return SpringContext.getBean(KualiConfigurationService.class); 867 } 868 }