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