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.document.workflow; 017 018 import java.util.ArrayList; 019 import java.util.Arrays; 020 import java.util.HashMap; 021 import java.util.Iterator; 022 import java.util.List; 023 import java.util.Map; 024 025 import org.apache.commons.lang.StringUtils; 026 import org.kuali.kfs.coa.businessobject.Account; 027 import org.kuali.kfs.coa.businessobject.Organization; 028 import org.kuali.kfs.integration.ld.LaborLedgerPendingEntryForSearching; 029 import org.kuali.kfs.integration.ld.LaborLedgerPostingDocumentForSearching; 030 import org.kuali.kfs.sys.KFSPropertyConstants; 031 import org.kuali.kfs.sys.businessobject.AccountingLine; 032 import org.kuali.kfs.sys.businessobject.FinancialSystemDocumentHeader; 033 import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntry; 034 import org.kuali.kfs.sys.businessobject.SourceAccountingLine; 035 import org.kuali.kfs.sys.context.SpringContext; 036 import org.kuali.kfs.sys.document.AccountingDocument; 037 import org.kuali.kfs.sys.document.AmountTotaling; 038 import org.kuali.kfs.sys.document.GeneralLedgerPostingDocument; 039 import org.kuali.kfs.sys.document.datadictionary.AccountingLineGroupDefinition; 040 import org.kuali.kfs.sys.document.datadictionary.FinancialSystemTransactionalDocumentEntry; 041 import org.kuali.rice.kew.docsearch.DocumentSearchContext; 042 import org.kuali.rice.kew.docsearch.SearchableAttribute; 043 import org.kuali.rice.kew.docsearch.SearchableAttributeFloatValue; 044 import org.kuali.rice.kew.docsearch.SearchableAttributeStringValue; 045 import org.kuali.rice.kew.docsearch.SearchableAttributeValue; 046 import org.kuali.rice.kew.exception.WorkflowException; 047 import org.kuali.rice.kew.rule.WorkflowAttributeValidationError; 048 import org.kuali.rice.kns.bo.BusinessObject; 049 import org.kuali.rice.kns.datadictionary.DocumentEntry; 050 import org.kuali.rice.kns.document.Document; 051 import org.kuali.rice.kns.lookup.LookupUtils; 052 import org.kuali.rice.kns.service.DataDictionaryService; 053 import org.kuali.rice.kns.service.DictionaryValidationService; 054 import org.kuali.rice.kns.service.DocumentService; 055 import org.kuali.rice.kns.service.KNSServiceLocator; 056 import org.kuali.rice.kns.util.FieldUtils; 057 import org.kuali.rice.kns.util.ObjectUtils; 058 import org.kuali.rice.kns.web.ui.Field; 059 import org.kuali.rice.core.util.KeyLabelPair; 060 import org.kuali.rice.kns.web.ui.Row; 061 import org.kuali.rice.kns.workflow.attribute.DataDictionarySearchableAttribute; 062 063 public class FinancialSystemSearchableAttribute extends DataDictionarySearchableAttribute { 064 065 //used to map the special fields to the DD Entry that validate it. 066 private static Map<String, String> magicFields = new HashMap<String, String>(); 067 068 static { 069 magicFields.put("chartOfAccountsCode","SourceAccountingLine"); 070 magicFields.put("organizationCode","Organization"); 071 magicFields.put("accountNumber","SourceAccountingLine"); 072 magicFields.put("financialDocumentTypeCode","GeneralLedgerPendingEntry"); 073 magicFields.put("financialDocumentTotalAmount","FinancialSystemDocumentHeader"); 074 } 075 076 077 public List<Row> getSearchingRows(DocumentSearchContext documentSearchContext) { 078 DataDictionaryService ddService = SpringContext.getBean(DataDictionaryService.class); 079 080 List<Row> docSearchRows = super.getSearchingRows(documentSearchContext); 081 082 DocumentEntry entry = ddService.getDataDictionary().getDocumentEntry(documentSearchContext.getDocumentTypeName()); 083 084 if (entry == null) { 085 return docSearchRows; 086 } 087 Class<? extends Document> docClass = entry.getDocumentClass(); 088 089 List<String> displayedFieldNames = new ArrayList<String>(); 090 091 if (AccountingDocument.class.isAssignableFrom(docClass)) { 092 Map<String, AccountingLineGroupDefinition> alGroups = ((FinancialSystemTransactionalDocumentEntry)entry).getAccountingLineGroups(); 093 Class alClass = SourceAccountingLine.class; 094 095 if (ObjectUtils.isNotNull(alGroups)) { 096 if (alGroups.containsKey("source")) { 097 alClass = alGroups.get("source").getAccountingLineClass(); 098 } 099 } 100 101 BusinessObject alBusinessObject = null; 102 103 Class orgClass = Organization.class; 104 BusinessObject orgBusinessObject = null; 105 106 try { 107 alBusinessObject = (BusinessObject)alClass.newInstance(); 108 orgBusinessObject = (BusinessObject)orgClass.newInstance(); 109 110 } catch (Exception cnfe) { 111 throw new RuntimeException(cnfe); 112 } 113 114 Field chartField = FieldUtils.getPropertyField(alClass, "chartOfAccountsCode", true); 115 chartField.setFieldDataType(SearchableAttribute.DATA_TYPE_STRING); 116 displayedFieldNames.add("chartOfAccountsCode"); 117 LookupUtils.setFieldQuickfinder(alBusinessObject, "chartOfAccountsCode", chartField, displayedFieldNames); 118 119 Field orgField = FieldUtils.getPropertyField(orgClass, "organizationCode", true); 120 orgField.setFieldDataType(SearchableAttribute.DATA_TYPE_STRING); 121 displayedFieldNames.clear(); 122 displayedFieldNames.add("organizationCode"); 123 LookupUtils.setFieldQuickfinder(new Account(), "organizationCode", orgField, displayedFieldNames); 124 125 Field accountField = FieldUtils.getPropertyField(alClass, "accountNumber", true); 126 accountField.setFieldDataType(SearchableAttribute.DATA_TYPE_STRING); 127 displayedFieldNames.clear(); 128 displayedFieldNames.add("accountNumber"); 129 LookupUtils.setFieldQuickfinder(alBusinessObject, "accountNumber", accountField, displayedFieldNames); 130 131 List<Field> fieldList = new ArrayList<Field>(); 132 fieldList.add(chartField); 133 docSearchRows.add(new Row(fieldList)); 134 135 fieldList = new ArrayList<Field>(); 136 fieldList.add(accountField); 137 docSearchRows.add(new Row(fieldList)); 138 139 fieldList = new ArrayList<Field>(); 140 fieldList.add(orgField); 141 docSearchRows.add(new Row(fieldList)); 142 } 143 144 boolean displayedLedgerPostingDoc = false; 145 if (LaborLedgerPostingDocumentForSearching.class.isAssignableFrom(docClass)) { 146 Class boClass = GeneralLedgerPendingEntry.class; 147 148 Field searchField = FieldUtils.getPropertyField(boClass, "financialDocumentTypeCode", true); 149 searchField.setFieldDataType(SearchableAttribute.DATA_TYPE_STRING); 150 151 displayedFieldNames.clear(); 152 displayedFieldNames.add("financialDocumentTypeCode"); 153 LookupUtils.setFieldQuickfinder(new GeneralLedgerPendingEntry(), "financialDocumentTypeCode", searchField, displayedFieldNames); 154 155 List<Field> fieldList = new ArrayList<Field>(); 156 fieldList.add(searchField); 157 docSearchRows.add(new Row(fieldList)); 158 displayedLedgerPostingDoc = true; 159 } 160 161 if (GeneralLedgerPostingDocument.class.isAssignableFrom(docClass) && !displayedLedgerPostingDoc) { 162 Class boClass = GeneralLedgerPendingEntry.class; 163 164 Field searchField = FieldUtils.getPropertyField(boClass, "financialDocumentTypeCode", true); 165 searchField.setFieldDataType(SearchableAttribute.DATA_TYPE_STRING); 166 167 displayedFieldNames.clear(); 168 displayedFieldNames.add("financialDocumentTypeCode"); 169 LookupUtils.setFieldQuickfinder(new GeneralLedgerPendingEntry(), "financialDocumentTypeCode", searchField, displayedFieldNames); 170 171 List<Field> fieldList = new ArrayList<Field>(); 172 fieldList.add(searchField); 173 docSearchRows.add(new Row(fieldList)); 174 175 } 176 177 if (AmountTotaling.class.isAssignableFrom( docClass)) { 178 Class boClass = FinancialSystemDocumentHeader.class; 179 180 Field searchField = FieldUtils.getPropertyField(boClass, "financialDocumentTotalAmount", true); 181 searchField.setFieldDataType(SearchableAttribute.DATA_TYPE_FLOAT); 182 183 List<Field> fieldList = new ArrayList<Field>(); 184 fieldList.add(searchField); 185 docSearchRows.add(new Row(fieldList)); 186 187 } 188 189 190 191 Row resultType = createSearchResultReturnRow(); 192 docSearchRows.add(resultType); 193 return docSearchRows; 194 } 195 196 public List<SearchableAttributeValue> getSearchStorageValues(DocumentSearchContext documentSearchContext) { 197 List<SearchableAttributeValue> searchAttrValues = super.getSearchStorageValues(documentSearchContext); 198 199 String docId = documentSearchContext.getDocumentId(); 200 DocumentService docService = SpringContext.getBean(DocumentService.class); 201 Document doc = null; 202 try { 203 doc = docService.getByDocumentHeaderIdSessionless(docId); 204 } catch (WorkflowException we) { 205 206 } 207 208 if (doc instanceof AmountTotaling) { 209 SearchableAttributeFloatValue searchableAttributeValue = new SearchableAttributeFloatValue(); 210 searchableAttributeValue.setSearchableAttributeKey("financialDocumentTotalAmount"); 211 searchableAttributeValue.setSearchableAttributeValue(((AmountTotaling)doc).getTotalDollarAmount().bigDecimalValue()); 212 searchAttrValues.add(searchableAttributeValue); 213 } 214 215 if (doc instanceof AccountingDocument) { 216 AccountingDocument accountingDoc = (AccountingDocument)doc; 217 searchAttrValues.addAll(harvestAccountingDocumentSearchableAttributes(accountingDoc)); 218 } 219 220 boolean indexedLedgerDoc = false; 221 if (doc instanceof LaborLedgerPostingDocumentForSearching) { 222 LaborLedgerPostingDocumentForSearching LLPostingDoc = (LaborLedgerPostingDocumentForSearching)doc; 223 searchAttrValues.addAll(harvestLLPDocumentSearchableAttributes(LLPostingDoc)); 224 indexedLedgerDoc = true; 225 } 226 227 if (doc instanceof GeneralLedgerPostingDocument && !indexedLedgerDoc) { 228 GeneralLedgerPostingDocument GLPostingDoc = (GeneralLedgerPostingDocument)doc; 229 searchAttrValues.addAll(harvestGLPDocumentSearchableAttributes(GLPostingDoc)); 230 } 231 232 233 return searchAttrValues; 234 } 235 236 /** 237 * 238 * @see org.kuali.rice.kns.workflow.attribute.DataDictionarySearchableAttribute#validateUserSearchInputs(java.util.Map, org.kuali.rice.kew.docsearch.DocumentSearchContext) 239 */ 240 241 @Override 242 public List<WorkflowAttributeValidationError> validateUserSearchInputs(Map<Object, Object> paramMap, DocumentSearchContext searchContext) { 243 // this list is irrelevant. the validation errors are put on the stack in the validationService. 244 List<WorkflowAttributeValidationError> errors = super.validateUserSearchInputs(paramMap, searchContext); 245 246 DictionaryValidationService validationService = KNSServiceLocator.getDictionaryValidationService(); 247 248 for (Object key : paramMap.keySet()) { 249 String value = (String)paramMap.get(key); 250 251 if (!StringUtils.isEmpty(value)) { 252 253 if (magicFields.containsKey(key)) { 254 validationService.validateAttributeFormat(magicFields.get(key), (String)key, value, (String)key); 255 } 256 257 } 258 259 } 260 return errors; 261 } 262 263 264 /** 265 * Harvest chart of accounts code, account number, and organization code as searchable attributes from an accounting document 266 * @param accountingDoc the accounting document to pull values from 267 * @return a List of searchable values 268 */ 269 protected List<SearchableAttributeValue> harvestAccountingDocumentSearchableAttributes(AccountingDocument accountingDoc) { 270 List<SearchableAttributeValue> searchAttrValues = new ArrayList<SearchableAttributeValue>(); 271 272 for (Iterator itr = accountingDoc.getSourceAccountingLines().iterator(); itr.hasNext();) { 273 AccountingLine accountingLine = (AccountingLine)itr.next(); 274 addSearchableAttributesForAccountingLine(searchAttrValues, accountingLine); 275 } 276 for (Iterator itr = accountingDoc.getTargetAccountingLines().iterator(); itr.hasNext();) { 277 AccountingLine accountingLine = (AccountingLine)itr.next(); 278 addSearchableAttributesForAccountingLine(searchAttrValues, accountingLine); 279 } 280 281 return searchAttrValues; 282 } 283 284 /** 285 * Harvest GLPE document type as searchable attributes from a GL posting document 286 * @param GLPDoc the GLP document to pull values from 287 * @return a List of searchable values 288 */ 289 protected List<SearchableAttributeValue> harvestGLPDocumentSearchableAttributes(GeneralLedgerPostingDocument GLPDoc) { 290 List<SearchableAttributeValue> searchAttrValues = new ArrayList<SearchableAttributeValue>(); 291 292 for (Iterator itr = GLPDoc.getGeneralLedgerPendingEntries().iterator(); itr.hasNext();) { 293 GeneralLedgerPendingEntry glpe = (GeneralLedgerPendingEntry)itr.next(); 294 addSearchableAttributesForGLPE(searchAttrValues, glpe); 295 } 296 return searchAttrValues; 297 } 298 299 /** 300 * Harvest LLPE document type as searchable attributes from a LL posting document 301 * @param LLPDoc the LLP document to pull values from 302 * @return a List of searchable values 303 */ 304 protected List<SearchableAttributeValue> harvestLLPDocumentSearchableAttributes(LaborLedgerPostingDocumentForSearching LLPDoc) { 305 List<SearchableAttributeValue> searchAttrValues = new ArrayList<SearchableAttributeValue>(); 306 307 for (Iterator itr = LLPDoc.getLaborLedgerPendingEntriesForSearching().iterator(); itr.hasNext();) { 308 LaborLedgerPendingEntryForSearching llpe = (LaborLedgerPendingEntryForSearching)itr.next(); 309 addSearchableAttributesForLLPE(searchAttrValues, llpe); 310 } 311 return searchAttrValues; 312 } 313 314 315 /** 316 * Pulls the default searchable attributes - chart code, account number, and account organization code - from a given accounting line and populates 317 * the searchable attribute values in the given list 318 * @param searchAttrValues a List of SearchableAttributeValue objects to populate 319 * @param accountingLine an AccountingLine to get values from 320 */ 321 protected void addSearchableAttributesForAccountingLine(List<SearchableAttributeValue> searchAttrValues, AccountingLine accountingLine) { 322 SearchableAttributeStringValue searchableAttributeValue = new SearchableAttributeStringValue(); 323 searchableAttributeValue.setSearchableAttributeKey(KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE); 324 searchableAttributeValue.setSearchableAttributeValue(accountingLine.getChartOfAccountsCode()); 325 searchAttrValues.add(searchableAttributeValue); 326 327 searchableAttributeValue = new SearchableAttributeStringValue(); 328 searchableAttributeValue.setSearchableAttributeKey(KFSPropertyConstants.ACCOUNT_NUMBER); 329 searchableAttributeValue.setSearchableAttributeValue(accountingLine.getAccountNumber()); 330 searchAttrValues.add(searchableAttributeValue); 331 332 searchableAttributeValue = new SearchableAttributeStringValue(); 333 searchableAttributeValue.setSearchableAttributeKey(KFSPropertyConstants.ORGANIZATION_CODE); 334 searchableAttributeValue.setSearchableAttributeValue(accountingLine.getAccount().getOrganizationCode()); 335 searchAttrValues.add(searchableAttributeValue); 336 } 337 338 /** 339 * Pulls the default searchable attribute - financialSystemTypeCode - from a given accounting line and populates 340 * the searchable attribute values in the given list 341 * @param searchAttrValues a List of SearchableAttributeValue objects to populate 342 * @param glpe a GeneralLedgerPendingEntry to get values from 343 */ 344 protected void addSearchableAttributesForGLPE(List<SearchableAttributeValue> searchAttrValues, GeneralLedgerPendingEntry glpe) { 345 SearchableAttributeStringValue searchableAttributeValue = new SearchableAttributeStringValue(); 346 searchableAttributeValue.setSearchableAttributeKey(KFSPropertyConstants.FINANCIAL_DOCUMENT_TYPE_CODE); 347 searchableAttributeValue.setSearchableAttributeValue(glpe.getFinancialDocumentTypeCode()); 348 searchAttrValues.add(searchableAttributeValue); 349 350 } 351 352 /** 353 * Pulls the default searchable attribute - financialSystemTypeCode from a given accounting line and populates 354 * the searchable attribute values in the given list 355 * @param searchAttrValues a List of SearchableAttributeValue objects to populate 356 * @param llpe a LaborLedgerPendingEntry to get values from 357 */ 358 protected void addSearchableAttributesForLLPE(List<SearchableAttributeValue> searchAttrValues, LaborLedgerPendingEntryForSearching llpe) { 359 SearchableAttributeStringValue searchableAttributeValue = new SearchableAttributeStringValue(); 360 searchableAttributeValue.setSearchableAttributeKey(KFSPropertyConstants.FINANCIAL_DOCUMENT_TYPE_CODE); 361 searchableAttributeValue.setSearchableAttributeValue(llpe.getFinancialDocumentTypeCode()); 362 searchAttrValues.add(searchableAttributeValue); 363 } 364 365 private Row createSearchResultReturnRow() { 366 String attributeName = "displayType"; 367 Field searchField = new Field(); 368 searchField.setPropertyName(attributeName); 369 searchField.setFieldType(Field.RADIO); 370 searchField.setFieldLabel("Search Result Type"); 371 searchField.setIndexedForSearch(false); 372 searchField.setBusinessObjectClassName(""); 373 searchField.setFieldHelpName(""); 374 searchField.setFieldHelpSummary(""); 375 searchField.setColumnVisible(false); 376 List<KeyLabelPair> values = new ArrayList<KeyLabelPair>(); 377 values.add(new KeyLabelPair("document", "Document Specific Data")); 378 values.add(new KeyLabelPair("workflow", "Workflow Data")); 379 searchField.setFieldValidValues(values); 380 searchField.setPropertyValue("document"); 381 382 List<Field> fieldList = new ArrayList<Field>(); 383 fieldList.add(searchField); 384 385 return new Row(fieldList); 386 387 } 388 389 }