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 }