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.gl.web.struts; 017 018 import java.util.ArrayList; 019 import java.util.Collection; 020 import java.util.HashMap; 021 import java.util.List; 022 import java.util.Map; 023 import java.util.Properties; 024 025 import javax.servlet.http.HttpServletRequest; 026 import javax.servlet.http.HttpServletResponse; 027 028 import org.apache.commons.lang.StringUtils; 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.gl.GeneralLedgerConstants; 033 import org.kuali.kfs.gl.ObjectHelper; 034 import org.kuali.kfs.gl.businessobject.AccountBalance; 035 import org.kuali.kfs.gl.businessobject.lookup.AccountBalanceByConsolidationLookupableHelperServiceImpl; 036 import org.kuali.kfs.integration.ld.SegmentedBusinessObject; 037 import org.kuali.kfs.sys.KFSConstants; 038 import org.kuali.kfs.sys.KFSKeyConstants; 039 import org.kuali.kfs.sys.KFSPropertyConstants; 040 import org.kuali.kfs.sys.context.SpringContext; 041 import org.kuali.rice.kns.lookup.CollectionIncomplete; 042 import org.kuali.rice.kns.lookup.LookupResultsService; 043 import org.kuali.rice.kns.lookup.Lookupable; 044 import org.kuali.rice.kns.service.KNSServiceLocator; 045 import org.kuali.rice.kns.service.KualiConfigurationService; 046 import org.kuali.rice.kns.service.SequenceAccessorService; 047 import org.kuali.rice.kns.util.GlobalVariables; 048 import org.kuali.rice.kns.util.KNSUtils; 049 import org.kuali.rice.kns.util.KNSConstants; 050 import org.kuali.rice.kns.util.KualiDecimal; 051 import org.kuali.rice.kns.util.UrlFactory; 052 import org.kuali.rice.kns.web.struts.action.KualiMultipleValueLookupAction; 053 import org.kuali.rice.kns.web.struts.form.MultipleValueLookupForm; 054 import org.kuali.rice.kns.web.ui.Column; 055 import org.kuali.rice.kns.web.ui.ResultRow; 056 057 /** 058 * Balance inquiries are pretty much just lookups already, but are not used in the traditional sense. In most cases, balance 059 * inquiries only show the end-user data, and allow the end-user to drill-down into inquiries. A traditional lookup allows the user 060 * to return data to a form. This class is for balance inquiries implemented in the sense of a traditional lookup for forms that 061 * pull data out of inquiries.<br/> <br/> One example of this is the 062 * <code>{@link org.kuali.kfs.module.ld.document.SalaryExpenseTransferDocument}</code> which creates source lines from a labor 063 * ledger balance inquiry screen.<br/> <br/> This is a <code>{@link KualiMultipleValueLookupAction}</code> which required some 064 * customization because requirements were not possible with displaytag. 065 * 066 * @see org.kuali.kfs.module.ld.document.SalaryExpenseTransferDocument 067 * @see org.kuali.kfs.module.ld.document.web.struts.SalaryExpenseTransferAction; 068 * @see org.kuali.kfs.module.ld.document.web.struts.SalaryExpenseTransferForm; 069 */ 070 public class BalanceInquiryLookupAction extends KualiMultipleValueLookupAction { 071 private static final org.apache.commons.logging.Log LOG = org.apache.commons.logging.LogFactory.getLog(BalanceInquiryLookupAction.class); 072 073 private static final String TOTALS_TABLE_KEY = "totalsTable"; 074 075 /** 076 * If there is no app param defined for the # rows/page, then this value will be used for the default 077 * 078 * @see KualiMultipleValueLookupAction#getMaxRowsPerPage(MultipleValueLookupForm) 079 */ 080 public static final int DEFAULT_MAX_ROWS_PER_PAGE = 50; 081 082 private KualiConfigurationService kualiConfigurationService; 083 private String[] totalTitles; 084 085 public BalanceInquiryLookupAction() { 086 super(); 087 kualiConfigurationService = SpringContext.getBean(KualiConfigurationService.class); 088 } 089 090 private void setTotalTitles() { 091 totalTitles = new String[7]; 092 093 totalTitles[0] = kualiConfigurationService.getPropertyString(KFSKeyConstants.AccountBalanceService.INCOME); 094 totalTitles[1] = kualiConfigurationService.getPropertyString(KFSKeyConstants.AccountBalanceService.INCOME_FROM_TRANSFERS); 095 totalTitles[2] = kualiConfigurationService.getPropertyString(KFSKeyConstants.AccountBalanceService.INCOME_TOTAL); 096 totalTitles[3] = kualiConfigurationService.getPropertyString(KFSKeyConstants.AccountBalanceService.EXPENSE); 097 totalTitles[4] = kualiConfigurationService.getPropertyString(KFSKeyConstants.AccountBalanceService.EXPENSE_FROM_TRANSFERS); 098 totalTitles[5] = kualiConfigurationService.getPropertyString(KFSKeyConstants.AccountBalanceService.EXPENSE_TOTAL); 099 totalTitles[6] = kualiConfigurationService.getPropertyString(KFSKeyConstants.AccountBalanceService.TOTAL); 100 101 } 102 103 private String[] getTotalTitles() { 104 if (null == totalTitles) { 105 setTotalTitles(); 106 } 107 108 return totalTitles; 109 } 110 111 /** 112 * search - sets the values of the data entered on the form on the jsp into a map and then searches for the results. 113 */ 114 public ActionForward search(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 115 BalanceInquiryLookupForm lookupForm = (BalanceInquiryLookupForm) form; 116 Lookupable lookupable = lookupForm.getLookupable(); 117 118 if (lookupable == null) { 119 LOG.error("Lookupable is null."); 120 throw new RuntimeException("Lookupable is null."); 121 } 122 123 Collection displayList = new ArrayList(); 124 CollectionIncomplete incompleteDisplayList; 125 List<ResultRow> resultTable = new ArrayList<ResultRow>(); 126 Long totalSize; 127 boolean bounded = true; 128 129 lookupable.validateSearchParameters(lookupForm.getFields()); 130 131 displayList = performMultipleValueLookup(lookupForm, resultTable, getMaxRowsPerPage(lookupForm), bounded); 132 incompleteDisplayList = (CollectionIncomplete) displayList; 133 totalSize = incompleteDisplayList.getActualSizeIfTruncated(); 134 135 if (lookupable.isSearchUsingOnlyPrimaryKeyValues()) { 136 lookupForm.setSearchUsingOnlyPrimaryKeyValues(true); 137 lookupForm.setPrimaryKeyFieldLabels(lookupable.getPrimaryKeyFieldLabels()); 138 } 139 else { 140 lookupForm.setSearchUsingOnlyPrimaryKeyValues(false); 141 lookupForm.setPrimaryKeyFieldLabels(KFSConstants.EMPTY_STRING); 142 } 143 144 145 // TODO: use inheritance instead of this if statement 146 if (lookupable.getLookupableHelperService() instanceof AccountBalanceByConsolidationLookupableHelperServiceImpl) { 147 Object[] resultTableAsArray = resultTable.toArray(); 148 Collection totalsTable = new ArrayList(); 149 150 int arrayIndex = 0; 151 152 try { 153 for (int listIndex = 0; listIndex < incompleteDisplayList.size(); listIndex++) { 154 AccountBalance balance = (AccountBalance) incompleteDisplayList.get(listIndex); 155 boolean ok = ObjectHelper.isOneOf(balance.getTitle(), getTotalTitles()); 156 if (ok) { 157 if (totalSize > 7) { 158 totalsTable.add(resultTableAsArray[arrayIndex]); 159 } 160 resultTable.remove(resultTableAsArray[arrayIndex]); 161 incompleteDisplayList.remove(balance); 162 } 163 arrayIndex++; 164 } 165 166 request.setAttribute(TOTALS_TABLE_KEY, totalsTable); 167 GlobalVariables.getUserSession().addObject(TOTALS_TABLE_KEY, totalsTable); 168 } 169 catch (NumberFormatException e) { 170 GlobalVariables.getMessageMap().putError(KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR, KFSKeyConstants.ERROR_CUSTOM, new String[] { "Fiscal Year must be a four-digit number" }); 171 } 172 catch (Exception e) { 173 GlobalVariables.getMessageMap().putError(KFSConstants.DOCUMENT_ERRORS, KFSKeyConstants.ERROR_CUSTOM, new String[] { "Please report the server error." }); 174 LOG.error("Application Errors", e); 175 } 176 } 177 178 request.setAttribute(KFSConstants.REQUEST_SEARCH_RESULTS_SIZE, totalSize); 179 request.setAttribute(KFSConstants.REQUEST_SEARCH_RESULTS, resultTable); 180 lookupForm.setResultsActualSize((int) totalSize.longValue()); 181 lookupForm.setResultsLimitedSize(resultTable.size()); 182 183 if (lookupForm.isSegmented()) { 184 LOG.debug("I'm segmented"); 185 request.setAttribute(GeneralLedgerConstants.LookupableBeanKeys.SEGMENTED_LOOKUP_FLAG_NAME, Boolean.TRUE); 186 } 187 188 if (request.getParameter(KFSConstants.SEARCH_LIST_REQUEST_KEY) != null) { 189 GlobalVariables.getUserSession().removeObject(request.getParameter(KFSConstants.SEARCH_LIST_REQUEST_KEY)); 190 request.setAttribute(KFSConstants.SEARCH_LIST_REQUEST_KEY, GlobalVariables.getUserSession().addObject(resultTable)); 191 } 192 193 return mapping.findForward(KFSConstants.MAPPING_BASIC); 194 } 195 196 /** 197 * This method returns none of the selected results and redirects back to the lookup caller. 198 * 199 * @param mapping 200 * @param form must be an instance of MultipleValueLookupForm 201 * @param request 202 * @param response 203 * @return 204 * @throws Exception 205 */ 206 public ActionForward prepareToReturnNone(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 207 MultipleValueLookupForm multipleValueLookupForm = (MultipleValueLookupForm) form; 208 prepareToReturnNone(multipleValueLookupForm); 209 210 // build the parameters for the refresh url 211 Properties parameters = new Properties(); 212 parameters.put(KFSConstants.DOC_FORM_KEY, multipleValueLookupForm.getFormKey()); 213 parameters.put(KFSConstants.DISPATCH_REQUEST_PARAMETER, KFSConstants.RETURN_METHOD_TO_CALL); 214 parameters.put(KFSConstants.REFRESH_CALLER, KFSConstants.MULTIPLE_VALUE); 215 parameters.put(KFSConstants.ANCHOR, multipleValueLookupForm.getLookupAnchor()); 216 217 String backUrl = UrlFactory.parameterizeUrl(multipleValueLookupForm.getBackLocation(), parameters); 218 return new ActionForward(backUrl, true); 219 } 220 221 /** 222 * This method does the processing necessary to return selected results and sends a redirect back to the lookup caller 223 * 224 * @param mapping 225 * @param form must be an instance of MultipleValueLookupForm 226 * @param request 227 * @param response 228 * @return 229 * @throws Exception 230 */ 231 public ActionForward prepareToReturnSelectedResults(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 232 MultipleValueLookupForm multipleValueLookupForm = (MultipleValueLookupForm) form; 233 if (StringUtils.isBlank(multipleValueLookupForm.getLookupResultsSequenceNumber())) { 234 // no search was executed 235 return prepareToReturnNone(mapping, form, request, response); 236 } 237 238 prepareToReturnSelectedResultBOs(multipleValueLookupForm); 239 240 // build the parameters for the refresh url 241 Properties parameters = new Properties(); 242 parameters.put(KFSConstants.LOOKUP_RESULTS_BO_CLASS_NAME, multipleValueLookupForm.getBusinessObjectClassName()); 243 parameters.put(KFSConstants.LOOKUP_RESULTS_SEQUENCE_NUMBER, multipleValueLookupForm.getLookupResultsSequenceNumber()); 244 parameters.put(KFSConstants.DOC_FORM_KEY, multipleValueLookupForm.getFormKey()); 245 parameters.put(KFSConstants.DISPATCH_REQUEST_PARAMETER, KFSConstants.RETURN_METHOD_TO_CALL); 246 parameters.put(KFSConstants.REFRESH_CALLER, KFSConstants.MULTIPLE_VALUE); 247 parameters.put(KFSConstants.ANCHOR, multipleValueLookupForm.getLookupAnchor()); 248 String backUrl = UrlFactory.parameterizeUrl(multipleValueLookupForm.getBackLocation(), parameters); 249 return new ActionForward(backUrl, true); 250 } 251 252 /** 253 * @see org.kuali.rice.kns.web.struts.action.KualiMultipleValueLookupAction#sort(org.apache.struts.action.ActionMapping, 254 * org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) 255 */ 256 @Override 257 public ActionForward sort(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 258 request.setAttribute(GeneralLedgerConstants.LookupableBeanKeys.SEGMENTED_LOOKUP_FLAG_NAME, Boolean.TRUE); 259 return super.sort(mapping, form, request, response); 260 } 261 262 /** 263 * @see org.kuali.rice.kns.web.struts.action.KualiMultipleValueLookupAction#selectAll(org.apache.struts.action.ActionMapping, 264 * org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) 265 */ 266 @Override 267 public ActionForward selectAll(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 268 request.setAttribute(GeneralLedgerConstants.LookupableBeanKeys.SEGMENTED_LOOKUP_FLAG_NAME, Boolean.TRUE); 269 return super.selectAll(mapping, form, request, response); 270 } 271 272 /** 273 * @see org.kuali.rice.kns.web.struts.action.KualiMultipleValueLookupAction#unselectAll(org.apache.struts.action.ActionMapping, 274 * org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) 275 */ 276 @Override 277 public ActionForward unselectAll(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 278 request.setAttribute(GeneralLedgerConstants.LookupableBeanKeys.SEGMENTED_LOOKUP_FLAG_NAME, Boolean.TRUE); 279 return super.unselectAll(mapping, form, request, response); 280 } 281 282 /** 283 * @see org.kuali.rice.kns.web.struts.action.KualiMultipleValueLookupAction#switchToPage(org.apache.struts.action.ActionMapping, 284 * org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) 285 */ 286 @Override 287 public ActionForward switchToPage(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 288 request.setAttribute(GeneralLedgerConstants.LookupableBeanKeys.SEGMENTED_LOOKUP_FLAG_NAME, Boolean.TRUE); 289 return super.switchToPage(mapping, form, request, response); 290 } 291 292 /** 293 * This method performs the lookup and returns a collection of lookup items. Also initializes values in the form that will allow 294 * the multiple value lookup page to render 295 * 296 * @param multipleValueLookupForm 297 * @param resultTable a list of result rows (used to generate what's shown in the UI). This list will be modified by this method 298 * @param maxRowsPerPage 299 * @param bounded whether the results will be bounded 300 * @return the list of result BOs, possibly bounded by size 301 */ 302 protected Collection performMultipleValueLookup(MultipleValueLookupForm multipleValueLookupForm, List<ResultRow> resultTable, int maxRowsPerPage, boolean bounded) { 303 Lookupable lookupable = multipleValueLookupForm.getLookupable(); 304 Collection displayList = lookupable.performLookup(multipleValueLookupForm, resultTable, bounded); 305 306 List defaultSortColumns = lookupable.getDefaultSortColumns(); 307 if (defaultSortColumns != null && !defaultSortColumns.isEmpty() && resultTable != null && !resultTable.isEmpty()) { 308 // there's a default sort order, just find the first sort column, and we can't go wrong 309 String firstSortColumn = (String) defaultSortColumns.get(0); 310 311 // go thru the first result row to find the index of the column (more efficient than calling lookupable.getColumns since 312 // we don't have to recreate column list) 313 int firstSortColumnIdx = -1; 314 List<Column> columnsForFirstResultRow = resultTable.get(0).getColumns(); 315 for (int i = 0; i < columnsForFirstResultRow.size(); i++) { 316 if (StringUtils.equals(firstSortColumn, columnsForFirstResultRow.get(i).getPropertyName())) { 317 firstSortColumnIdx = i; 318 break; 319 } 320 } 321 multipleValueLookupForm.setColumnToSortIndex(firstSortColumnIdx); 322 } 323 else { 324 // don't know how results were sorted, so we just say -1 325 multipleValueLookupForm.setColumnToSortIndex(-1); 326 } 327 328 // we just performed the lookup, so we're on the first page (indexed from 0) 329 multipleValueLookupForm.jumpToFirstPage(resultTable.size(), maxRowsPerPage); 330 331 SequenceAccessorService sequenceAccessorService = SpringContext.getBean(SequenceAccessorService.class); 332 String lookupResultsSequenceNumber = String.valueOf(sequenceAccessorService.getNextAvailableSequenceNumber(KNSConstants.LOOKUP_RESULTS_SEQUENCE)); 333 multipleValueLookupForm.setLookupResultsSequenceNumber(lookupResultsSequenceNumber); 334 try { 335 LookupResultsService lookupResultsService = SpringContext.getBean(LookupResultsService.class); 336 lookupResultsService.persistResultsTable(lookupResultsSequenceNumber, resultTable, GlobalVariables.getUserSession().getPerson().getPrincipalId()); 337 } 338 catch (Exception e) { 339 LOG.error("error occured trying to persist multiple lookup results", e); 340 throw new RuntimeException("error occured trying to persist multiple lookup results"); 341 } 342 343 // since new search, nothing's checked 344 multipleValueLookupForm.setCompositeObjectIdMap(new HashMap<String, String>()); 345 346 return displayList; 347 } 348 349 /** 350 * @see org.kuali.rice.kns.web.struts.action.KualiMultipleValueLookupAction#selectAll(org.kuali.rice.kns.web.struts.form.MultipleValueLookupForm, 351 * int) 352 */ 353 @Override 354 protected List<ResultRow> selectAll(MultipleValueLookupForm multipleValueLookupForm, int maxRowsPerPage) { 355 List<ResultRow> resultTable = null; 356 try { 357 LookupResultsService lookupResultsService = KNSServiceLocator.getLookupResultsService(); 358 String lookupResultsSequenceNumber = multipleValueLookupForm.getLookupResultsSequenceNumber(); 359 360 resultTable = lookupResultsService.retrieveResultsTable(lookupResultsSequenceNumber, GlobalVariables.getUserSession().getPerson().getPrincipalId()); 361 } 362 catch (Exception e) { 363 LOG.error("error occured trying to export multiple lookup results", e); 364 throw new RuntimeException("error occured trying to export multiple lookup results"); 365 } 366 367 Map<String, String> selectedObjectIds = this.getSelectedObjectIds(multipleValueLookupForm, resultTable); 368 369 multipleValueLookupForm.jumpToPage(multipleValueLookupForm.getViewedPageNumber(), resultTable.size(), maxRowsPerPage); 370 multipleValueLookupForm.setColumnToSortIndex(Integer.parseInt(multipleValueLookupForm.getPreviouslySortedColumnIndex())); 371 multipleValueLookupForm.setCompositeObjectIdMap(selectedObjectIds); 372 373 return resultTable; 374 } 375 376 /** 377 * put all enties into select object map. This implmentation only deals with the money amount objects. 378 * 379 * @param multipleValueLookupForm the given struts form 380 * @param resultTable the given result table that holds all data being presented 381 * @return the map containing all entries available for selection 382 */ 383 private Map<String, String> getSelectedObjectIds(MultipleValueLookupForm multipleValueLookupForm, List<ResultRow> resultTable) { 384 String businessObjectClassName = multipleValueLookupForm.getBusinessObjectClassName(); 385 SegmentedBusinessObject segmentedBusinessObject; 386 try { 387 segmentedBusinessObject = (SegmentedBusinessObject) Class.forName(multipleValueLookupForm.getBusinessObjectClassName()).newInstance(); 388 } 389 catch (Exception e) { 390 throw new RuntimeException("Fail to create an object of " + businessObjectClassName + e); 391 } 392 393 Map<String, String> selectedObjectIds = new HashMap<String, String>(); 394 Collection<String> segmentedPropertyNames = segmentedBusinessObject.getSegmentedPropertyNames(); 395 for (ResultRow row : resultTable) { 396 for (Column column : row.getColumns()) { 397 String propertyName = column.getPropertyName(); 398 if (segmentedPropertyNames.contains(propertyName)) { 399 String propertyValue = StringUtils.replace(column.getPropertyValue(), ",", ""); 400 KualiDecimal amount = new KualiDecimal(propertyValue); 401 402 if (amount.isNonZero()) { 403 String objectId = row.getObjectId() + "." + propertyName + "." + KNSUtils.convertDecimalIntoInteger(amount); 404 selectedObjectIds.put(objectId, objectId); 405 } 406 } 407 } 408 } 409 410 return selectedObjectIds; 411 } 412 } 413