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.businessobject.inquiry;
017    
018    import java.util.ArrayList;
019    import java.util.HashMap;
020    import java.util.Iterator;
021    import java.util.List;
022    import java.util.Map;
023    import java.util.Properties;
024    
025    import org.apache.commons.lang.StringUtils;
026    import org.kuali.kfs.gl.Constant;
027    import org.kuali.kfs.gl.businessobject.AccountBalance;
028    import org.kuali.kfs.gl.businessobject.lookup.BusinessObjectFieldConverter;
029    import org.kuali.kfs.sys.KFSConstants;
030    import org.kuali.kfs.sys.KFSPropertyConstants;
031    import org.kuali.kfs.sys.businessobject.inquiry.KfsInquirableImpl;
032    import org.kuali.kfs.sys.context.SpringContext;
033    import org.kuali.rice.kew.doctype.bo.DocumentTypeEBO;
034    import org.kuali.rice.kew.doctype.service.DocumentTypeService;
035    import org.kuali.rice.kew.dto.DocumentTypeDTO;
036    import org.kuali.rice.kns.bo.BusinessObject;
037    import org.kuali.rice.kns.datadictionary.AttributeDefinition;
038    import org.kuali.rice.kns.datadictionary.DataDictionaryEntryBase;
039    import org.kuali.rice.kns.lookup.HtmlData;
040    import org.kuali.rice.kns.lookup.HtmlData.AnchorHtmlData;
041    import org.kuali.rice.kns.lookup.LookupUtils;
042    import org.kuali.rice.kns.service.BusinessObjectDictionaryService;
043    import org.kuali.rice.kns.service.DataDictionaryService;
044    import org.kuali.rice.kns.service.PersistenceStructureService;
045    import org.kuali.rice.kns.util.KNSConstants;
046    import org.kuali.rice.kns.util.ObjectUtils;
047    import org.kuali.rice.kns.util.UrlFactory;
048    
049    /**
050     * This class is the template class for the customized inqurable implementations used to generate balance inquiry screens.
051     */
052    public abstract class AbstractGeneralLedgerInquirableImpl extends KfsInquirableImpl {
053        private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(AbstractGeneralLedgerInquirableImpl.class);
054        
055        private BusinessObject businessObject; 
056        
057        /**
058         * @see org.kuali.kfs.sys.businessobject.inquiry.KfsInquirableImpl#getInquiryUrl(org.kuali.rice.kns.bo.BusinessObject, java.lang.String, boolean)
059         */
060        @Override
061        public HtmlData getInquiryUrl(BusinessObject businessObject, String attributeName, boolean forceInquiry) {
062            return this.getInquiryUrl(businessObject, attributeName);
063        }
064    
065        /**
066         * Helper method to build an inquiry url for a result field.
067         * 
068         * @param businessObject the business object instance to build the urls for
069         * @param attributeName the attribute name which links to an inquirable
070         * @return String url to inquiry
071         */
072        public HtmlData getInquiryUrl(BusinessObject businessObject, String attributeName) {
073            this.setBusinessObject(businessObject);
074            AnchorHtmlData inquiryHref = new AnchorHtmlData(KNSConstants.EMPTY_STRING, KNSConstants.EMPTY_STRING);
075            BusinessObjectDictionaryService businessDictionary = SpringContext.getBean(BusinessObjectDictionaryService.class);
076            PersistenceStructureService persistenceStructureService = SpringContext.getBean(PersistenceStructureService.class);
077    
078            String baseUrl = KFSConstants.INQUIRY_ACTION;
079            Properties parameters = new Properties();
080            parameters.put(KFSConstants.DISPATCH_REQUEST_PARAMETER, KFSConstants.START_METHOD);
081    
082            Object attributeValue = null;
083            Class inquiryBusinessObjectClass = null;
084            String attributeRefName = "";
085            boolean isPkReference = false;
086    
087            Map userDefinedAttributeMap = getUserDefinedAttributeMap();
088            boolean isUserDefinedAttribute = userDefinedAttributeMap == null ? false : userDefinedAttributeMap.containsKey(attributeName);
089    
090            // determine the type of the given attribute: user-defined, regular, nested-referenced or primitive reference
091            if (isUserDefinedAttribute) {
092                attributeName = getAttributeName(attributeName);
093                inquiryBusinessObjectClass = getInquiryBusinessObjectClass(attributeName);
094                isPkReference = true;
095            }
096            else if (attributeName.equals(businessDictionary.getTitleAttribute(businessObject.getClass()))) {
097                inquiryBusinessObjectClass = businessObject.getClass();
098                isPkReference = true;
099            }
100            else if (ObjectUtils.isNestedAttribute(attributeName)) {
101                if (!"financialObject.financialObjectType.financialReportingSortCode".equals(attributeName)) {
102                    inquiryBusinessObjectClass = LookupUtils.getNestedReferenceClass(businessObject, attributeName);
103                }
104                else {
105                    return inquiryHref;
106                }
107            }
108            else {
109                Map primitiveReference = LookupUtils.getPrimitiveReference(businessObject, attributeName);
110                if (primitiveReference != null && !primitiveReference.isEmpty()) {
111                    attributeRefName = (String) primitiveReference.keySet().iterator().next();
112                    inquiryBusinessObjectClass = (Class) primitiveReference.get(attributeRefName);
113                }
114                attributeValue = ObjectUtils.getPropertyValue(businessObject, attributeName);
115                attributeValue = (attributeValue == null) ? "" : attributeValue.toString();
116            }
117    
118            // process the business object class if the attribute name is not user-defined
119            if (!isUserDefinedAttribute) {
120                if (isExclusiveField(attributeName, attributeValue)) {
121                    return inquiryHref;
122                }
123    
124                if (inquiryBusinessObjectClass == null || businessDictionary.isInquirable(inquiryBusinessObjectClass) == null || !businessDictionary.isInquirable(inquiryBusinessObjectClass).booleanValue()) {
125                    return inquiryHref;
126                }
127            }
128            parameters.put(KFSConstants.BUSINESS_OBJECT_CLASS_ATTRIBUTE, inquiryBusinessObjectClass.getName());
129    
130            List keys = new ArrayList();
131            Map<String,String> inquiryFields = new HashMap<String, String>();
132            if (isUserDefinedAttribute) {
133                baseUrl = getBaseUrl();
134                keys = buildUserDefinedAttributeKeyList();
135    
136                parameters.put(KFSConstants.RETURN_LOCATION_PARAMETER, Constant.RETURN_LOCATION_VALUE);
137                parameters.put(KFSConstants.GL_BALANCE_INQUIRY_FLAG, "true");
138                parameters.put(KFSConstants.DISPATCH_REQUEST_PARAMETER, KFSConstants.SEARCH_METHOD);
139                parameters.put(KFSConstants.DOC_FORM_KEY, "88888888");
140    
141                // add more customized parameters into the current parameter map
142                addMoreParameters(parameters, attributeName);
143            }
144            else if (persistenceStructureService.isPersistable(inquiryBusinessObjectClass)) {
145                keys = persistenceStructureService.listPrimaryKeyFieldNames(inquiryBusinessObjectClass);
146            }
147    
148            // build key value url parameters used to retrieve the business object
149            if (keys != null) {
150                for (Iterator keyIterator = keys.iterator(); keyIterator.hasNext();) {
151                    String keyName = (String) keyIterator.next();
152    
153                    // convert the key names based on their formats and types
154                    String keyConversion = keyName;
155                    if (ObjectUtils.isNestedAttribute(attributeName)) {
156                        if (isUserDefinedAttribute) {
157                            keyConversion = keyName;
158                        }
159                        else {
160                            keyConversion = ObjectUtils.getNestedAttributePrefix(attributeName) + "." + keyName;
161                        }
162                    }
163                    else {
164                        if (isPkReference) {
165                            keyConversion = keyName;
166                        }
167                        else {
168                            keyConversion = persistenceStructureService.getForeignKeyFieldName(businessObject.getClass(), attributeRefName, keyName);
169                        }
170                    }
171    
172                    Object keyValue = ObjectUtils.getPropertyValue(businessObject, keyConversion);
173                    keyValue = (keyValue == null) ? "" : keyValue.toString();
174    
175                    // convert the key value and name into the given ones
176                    Object tempKeyValue = this.getKeyValue(keyName, keyValue);
177                    keyValue = tempKeyValue == null ? keyValue : tempKeyValue;
178    
179                    String tempKeyName = this.getKeyName(keyName);
180                    keyName = tempKeyName == null ? keyName : tempKeyName;
181    
182                    // add the key-value pair into the parameter map
183                    if (keyName != null){
184                        parameters.put(keyName, keyValue);
185                        inquiryFields.put(keyName, keyValue.toString());
186                    }
187                }
188            }
189    
190            // Hack to make this work. I don't know why it doesn't pick up the whole primary key for these. The last big change to
191            // KualiInquirableImpl
192            // broke all of this
193            if (businessObject instanceof AccountBalance) {
194                AccountBalance ab = (AccountBalance) businessObject;
195                if ("financialObject.financialObjectLevel.financialConsolidationObject.finConsolidationObjectCode".equals(attributeName)) {
196                    parameters.put(KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE, ab.getChartOfAccountsCode());
197                }
198                else if ("financialObject.financialObjectLevel.financialObjectLevelCode".equals(attributeName)) {
199                    parameters.put(KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE, ab.getChartOfAccountsCode());
200                }
201            }
202    
203            return getHyperLink(inquiryBusinessObjectClass, inquiryFields, UrlFactory.parameterizeUrl(baseUrl, parameters));
204        }
205    
206        /**
207         * This method builds the inquiry url for user-defined attribute
208         * 
209         * @return a List of attribute keys for the inquiry url
210         */
211        protected abstract List buildUserDefinedAttributeKeyList();
212    
213        /**
214         * This method defines the user-defined attribute map
215         * 
216         * @return the user-defined attribute map
217         */
218        protected abstract Map getUserDefinedAttributeMap();
219    
220        /**
221         * This method finds the matching attribute name of given one
222         * 
223         * @param attributeName the given attribute name
224         * @return the attribute name from the given one
225         */
226        protected abstract String getAttributeName(String attributeName);
227    
228        /**
229         * This method finds the matching the key value of the given one
230         * 
231         * @param keyName the given key name
232         * @param keyValue the given key value
233         * @return the key value from the given key value
234         */
235        protected abstract Object getKeyValue(String keyName, Object keyValue);
236    
237        /**
238         * This method finds the matching the key name of the given one
239         * 
240         * @param keyName the given key name
241         * @return the key value from the given key name
242         */
243        protected abstract String getKeyName(String keyName);
244    
245        /**
246         * This method defines the lookupable implementation attribute name
247         * 
248         * @return the lookupable implementation attribute name
249         */
250        protected abstract String getLookupableImplAttributeName();
251    
252        /**
253         * This method defines the base inquiry url
254         * 
255         * @return the base inquiry url
256         */
257        protected abstract String getBaseUrl();
258    
259        /**
260         * This method gets the class name of the inquiry business object for a given attribute.
261         * 
262         * @return the class name of the inquiry business object for a given attribute
263         */
264        protected abstract Class getInquiryBusinessObjectClass(String attributeName);
265    
266        /**
267         * This method adds more parameters into the curren parameter map
268         * 
269         * @param parameter the current parameter map
270         */
271        protected abstract void addMoreParameters(Properties parameter, String attributeName);
272    
273        /**
274         * This method determines whether the input name-value pair is exclusive from the processing
275         * 
276         * @param keyName the name of the name-value pair
277         * @param keyValue the value of the name-value pair
278         * @return true if the input key is in the exclusive list; otherwise, false
279         */
280        protected boolean isExclusiveField(Object keyName, Object keyValue) {
281    
282            if (keyName != null && keyValue != null) {
283                String convertedKeyName = BusinessObjectFieldConverter.convertFromTransactionPropertyName(keyName.toString());
284    
285                if (convertedKeyName.equals(KFSPropertyConstants.SUB_ACCOUNT_NUMBER) && keyValue.equals(Constant.CONSOLIDATED_SUB_ACCOUNT_NUMBER)) {
286                    return true;
287                }
288                else if (convertedKeyName.equals(KFSPropertyConstants.SUB_OBJECT_CODE) && keyValue.equals(Constant.CONSOLIDATED_SUB_OBJECT_CODE)) {
289                    return true;
290                }
291                else if (convertedKeyName.equals(KFSPropertyConstants.OBJECT_TYPE_CODE) && keyValue.equals(Constant.CONSOLIDATED_OBJECT_TYPE_CODE)) {
292                    return true;
293                }
294                if (convertedKeyName.equals(KFSPropertyConstants.SUB_ACCOUNT_NUMBER) && keyValue.equals(KFSConstants.getDashSubAccountNumber())) {
295                    return true;
296                }
297                else if (convertedKeyName.equals(KFSPropertyConstants.SUB_OBJECT_CODE) && keyValue.equals(KFSConstants.getDashFinancialSubObjectCode())) {
298                    return true;
299                }
300                else if (convertedKeyName.equals(KFSPropertyConstants.PROJECT_CODE) && keyValue.equals(KFSConstants.getDashProjectCode())) {
301                    return true;
302                }
303            }
304            return false;
305        }
306    
307        /**
308         * This method recovers the values of the given keys
309         * 
310         * @param fieldValues unconsolidated values
311         * @param keyName a key name that may be in the fieldValues map
312         * @param keyValue a key value that may be in the fieldValues map
313         * @return the original value for a previously consolidated value
314         */
315        protected String recoverFieldValueFromConsolidation(Map fieldValues, Object keyName, Object keyValue) {
316            if (fieldValues == null || keyName == null || keyValue == null) {
317                return Constant.EMPTY_STRING;
318            }
319    
320            Map convertedFieldValues = BusinessObjectFieldConverter.convertFromTransactionFieldValues(fieldValues);
321            String convertedKeyName = BusinessObjectFieldConverter.convertFromTransactionPropertyName(keyName.toString());
322    
323            if (convertedKeyName.equals(KFSPropertyConstants.SUB_ACCOUNT_NUMBER) && keyValue.equals(Constant.CONSOLIDATED_SUB_ACCOUNT_NUMBER)) {
324                return this.getValueFromFieldValues(convertedFieldValues, keyName);
325            }
326            else if (convertedKeyName.equals(KFSPropertyConstants.SUB_OBJECT_CODE) && keyValue.equals(Constant.CONSOLIDATED_SUB_OBJECT_CODE)) {
327                return this.getValueFromFieldValues(convertedFieldValues, keyName);
328            }
329            else if (convertedKeyName.equals(KFSPropertyConstants.OBJECT_TYPE_CODE) && keyValue.equals(Constant.CONSOLIDATED_OBJECT_TYPE_CODE)) {
330                return this.getValueFromFieldValues(convertedFieldValues, keyName);
331            }
332    
333            return Constant.EMPTY_STRING;
334        }
335    
336        /**
337         * Utility method to get the value of the given key from the field values
338         * 
339         * @param fieldValues a Map of key values
340         * @param keyName the name of the key to retrieve the value from
341         * @return the value for the key, or, if not found, an empty String
342         */
343        private String getValueFromFieldValues(Map fieldValues, Object keyName) {
344            String keyValue = Constant.EMPTY_STRING;
345    
346            if (fieldValues.containsKey(keyName)) {
347                keyValue = (String) fieldValues.get(keyName);
348            }
349            return keyValue;
350        }
351    
352        /**
353         * This takes a map of field values and then returns it without processing it, making this a sort
354         * of identity method for Maps
355         * 
356         * @param fieldValues field values to return to the user
357         * @return the Map you sent in as a parameter
358         */
359        public Map getFieldValues(Map fieldValues) {
360            return fieldValues;
361        }
362    
363        /**
364         * Given the nested name of an attribute in an object, returns the class that attribute will return
365         * 
366         * @param businessObject the business object to find the propery class for
367         * @param attributeName the nested name of the attribute to find the class for
368         * @return the class of the nested attribute
369         */
370        public Class getNestedInquiryBusinessObjectClass(BusinessObject businessObject, String attributeName) {
371            // TODO: not finished
372            Class inquiryBusinessObjectClass = null;
373            String entryName = businessObject.getClass().getName();
374            LOG.debug("businessObject: " + entryName);
375            LOG.debug("attributeName: " + attributeName);
376    
377            DataDictionaryService dataDictionary = SpringContext.getBean(DataDictionaryService.class);
378            AttributeDefinition attributeDefinition = null;
379    
380            if (StringUtils.isBlank(attributeName)) {
381                throw new IllegalArgumentException("invalid (blank) attributeName");
382            }
383    
384            DataDictionaryEntryBase entry = (DataDictionaryEntryBase) dataDictionary.getDataDictionary().getDictionaryObjectEntry(entryName);
385            if (entry != null) {
386                attributeDefinition = entry.getAttributeDefinition(attributeName);
387                inquiryBusinessObjectClass = LookupUtils.getNestedReferenceClass(businessObject, attributeName);
388            }
389    
390            // TODO: need to get class for which this property belongs
391    //        if (attributeDefinition instanceof AttributeReferenceDefinition) {
392    //            AttributeReferenceDefinition attributeReferenceDefinition = (AttributeReferenceDefinition) attributeDefinition;
393    //            LOG.debug("Source Classname = " + attributeReferenceDefinition.getSourceClassName());
394    //            LOG.debug("Source Attribute = " + attributeReferenceDefinition.getSourceAttributeName());
395    //
396    //            try {
397    //                inquiryBusinessObjectClass = Class.forName(attributeReferenceDefinition.getSourceClassName());
398    //            }
399    //            catch (Exception e) {
400    //                throw new IllegalArgumentException("fail to construct a Class");
401    //            }
402    //        }
403    
404            return inquiryBusinessObjectClass;
405        }
406        
407        /**
408         * Builds URL to document type inquiry based on a given document type code
409         * 
410         * @param docTypeCode - document type code to inquiry on
411         * @return {@link HtmlData} representing inquiry URL
412         */
413        protected HtmlData getDocTypeInquiryUrl(String docTypeCode) {
414            DocumentTypeDTO docTypeDTO = SpringContext.getBean(DocumentTypeService.class).getDocumentTypeVO(docTypeCode);
415            if ( docTypeDTO == null ) {
416                return new AnchorHtmlData();
417            }
418            String baseUrl = KFSConstants.INQUIRY_ACTION;
419    
420            Properties parameters = new Properties();
421            parameters.put(KFSConstants.DISPATCH_REQUEST_PARAMETER, KFSConstants.START_METHOD);
422            parameters.put(KFSConstants.BUSINESS_OBJECT_CLASS_ATTRIBUTE, DocumentTypeEBO.class.getName());
423            parameters.put(KFSConstants.DOC_FORM_KEY, "88888888");
424            parameters.put(KFSPropertyConstants.DOCUMENT_TYPE_ID, docTypeDTO.getDocTypeId().toString());
425    
426            Map<String, String> inquiryFields = new HashMap<String, String>();
427            inquiryFields.put(KFSPropertyConstants.DOCUMENT_TYPE_ID, docTypeDTO.getDocTypeId().toString());
428    
429            return getHyperLink(DocumentTypeEBO.class, inquiryFields, UrlFactory.parameterizeUrl(baseUrl, parameters));
430        }
431    
432        /**
433         * Gets the businessObject attribute. 
434         * @return Returns the businessObject.
435         */
436        public BusinessObject getBusinessObject() {
437            return businessObject;
438        }
439    
440        /**
441         * Sets the businessObject attribute value.
442         * @param businessObject The businessObject to set.
443         */
444        public void setBusinessObject(BusinessObject businessObject) {
445            this.businessObject = businessObject;
446        }
447    }