View Javadoc
1   /*
2    * Copyright 2007 The Kuali Foundation
3    * 
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    * http://www.opensource.org/licenses/ecl2.php
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.ole.gl.document;
17  
18  import java.math.BigDecimal;
19  import java.text.SimpleDateFormat;
20  import java.util.Collection;
21  import java.util.Date;
22  import java.util.List;
23  
24  import org.apache.commons.lang.StringUtils;
25  import org.kuali.ole.gl.businessobject.CorrectionChange;
26  import org.kuali.ole.gl.businessobject.CorrectionChangeGroup;
27  import org.kuali.ole.gl.businessobject.CorrectionCriteria;
28  import org.kuali.ole.gl.businessobject.OriginEntryFull;
29  import org.kuali.ole.gl.businessobject.OriginEntryStatistics;
30  import org.kuali.ole.gl.businessobject.options.OriginEntryFieldFinder;
31  import org.kuali.ole.sys.OLEConstants;
32  import org.kuali.ole.sys.context.SpringContext;
33  import org.kuali.rice.core.api.util.type.KualiDecimal;
34  import org.kuali.rice.coreservice.framework.parameter.ParameterService;
35  
36  /**
37   * This class provides utility methods for the correction document
38   */
39  public class CorrectionDocumentUtils {
40      private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(CorrectionDocumentUtils.class);
41      public static final int DEFAULT_RECORD_COUNT_FUNCTIONALITY_LIMIT = 1000;
42  
43      /**
44       * The GLCP document will always be on restricted functionality mode, regardless of input group size
45       */
46      public static final int RECORD_COUNT_FUNCTIONALITY_LIMIT_IS_NONE = 0;
47  
48      /**
49       * The GLCP document will never be on restricted functionality mode, regardless of input group size
50       */
51      public static final int RECORD_COUNT_FUNCTIONALITY_LIMIT_IS_UNLIMITED = -1;
52  
53      public static final int DEFAULT_RECORDS_PER_PAGE = 10;
54  
55      /**
56       * This method returns the limit for record count functionality
57       * 
58       * @return limit for record count functionality
59       */
60      public static int getRecordCountFunctionalityLimit() {
61          String limitString = SpringContext.getBean(ParameterService.class).getParameterValueAsString(GeneralLedgerCorrectionProcessDocument.class, OLEConstants.GeneralLedgerCorrectionProcessApplicationParameterKeys.RECORD_COUNT_FUNCTIONALITY_LIMIT);
62          if (limitString != null) {
63              return Integer.valueOf(limitString);
64          }
65  
66          return DEFAULT_RECORD_COUNT_FUNCTIONALITY_LIMIT;
67      }
68  
69      /**
70       * This method returns the number of records per page
71       * 
72       * @return number of records per page
73       */
74      public static int getRecordsPerPage() {
75          String limitString = SpringContext.getBean(ParameterService.class).getParameterValueAsString(GeneralLedgerCorrectionProcessDocument.class, OLEConstants.GeneralLedgerCorrectionProcessApplicationParameterKeys.RECORDS_PER_PAGE);
76          if (limitString != null) {
77              return Integer.valueOf(limitString);
78          }
79          return DEFAULT_RECORDS_PER_PAGE;
80      }
81  
82      /**
83       * This method returns true if input group size is greater than or equal to record count functionality limit
84       * 
85       * @param inputGroupSize size of input groups
86       * @param recordCountFunctionalityLimit limit for record count functionality
87       * @return true if input group size is greater than or equal to record count functionality limit
88       */
89      public static boolean isRestrictedFunctionalityMode(int inputGroupSize, int recordCountFunctionalityLimit) {
90          return (recordCountFunctionalityLimit != CorrectionDocumentUtils.RECORD_COUNT_FUNCTIONALITY_LIMIT_IS_UNLIMITED && inputGroupSize >= recordCountFunctionalityLimit) || recordCountFunctionalityLimit == CorrectionDocumentUtils.RECORD_COUNT_FUNCTIONALITY_LIMIT_IS_NONE;
91      }
92  
93      /**
94       * When a correction criterion is about to be added to a group, this will check if it is valid, meaning that the field name is
95       * not blank
96       * 
97       * @param correctionCriteria validated correction criteria
98       * @return true if correction criteria is valid for adding
99       */
100     public static boolean validCorrectionCriteriaForAdding(CorrectionCriteria correctionCriteria) {
101         String fieldName = correctionCriteria.getCorrectionFieldName();
102         if (StringUtils.isBlank(fieldName)) {
103             return false;
104         }
105         return true;
106     }
107 
108     /**
109      * When a document is about to be saved, this will check if it is valid, meaning that the field name and value are both blank
110      * 
111      * @param correctionCriteria validated correction criteria
112      * @return true if correction criteria is valid for saving
113      */
114     public static boolean validCorrectionCriteriaForSaving(CorrectionCriteria correctionCriteria) {
115         return correctionCriteria == null || (StringUtils.isBlank(correctionCriteria.getCorrectionFieldName()) && StringUtils.isBlank(correctionCriteria.getCorrectionFieldValue()));
116     }
117 
118     /**
119      * When a correction change is about to be added to a group, this will check if it is valid, meaning that the field name is not
120      * blank
121      * 
122      * @param correctionChange validated correction change
123      * @return true is correction change is valid for adding
124      */
125     public static boolean validCorrectionChangeForAdding(CorrectionChange correctionChange) {
126         String fieldName = correctionChange.getCorrectionFieldName();
127         if (StringUtils.isBlank(fieldName)) {
128             return false;
129         }
130         return true;
131     }
132 
133     /**
134      * When a document is about to be saved, this will check if it is valid, meaning that the field name and value are both blank
135      * 
136      * @param correctionCriteria validated correction criteria
137      * @return true if correction change is valid for saving (i.e. correction change is null or correction field name and field
138      *         value are blank)
139      */
140     public static boolean validCorrectionChangeForSaving(CorrectionChange correctionChange) {
141         return correctionChange == null || (StringUtils.isBlank(correctionChange.getCorrectionFieldName()) && StringUtils.isBlank(correctionChange.getCorrectionFieldValue()));
142     }
143 
144     /**
145      * Sets all origin entries' entry IDs to null within the collection.
146      * 
147      * @param originEntries collection of origin entries
148      */
149     public static void setAllEntryIdsToNull(Collection<OriginEntryFull> originEntries) {
150         for (OriginEntryFull entry : originEntries) {
151             entry.setEntryId(null);
152         }
153     }
154 
155     /**
156      * Sets all origin entries' entry IDs to be sequential starting from 0 in the collection
157      * 
158      * @param originEntries collection of origin entries
159      */
160     public static void setSequentialEntryIds(Collection<OriginEntryFull> originEntries) {
161         int index = 0;
162         for (OriginEntryFull entry : originEntries) {
163             entry.setEntryId(new Integer(index));
164             index++;
165         }
166     }
167 
168     /**
169      * Returns whether an origin entry matches the passed in criteria. If both the criteria and actual value are both String types
170      * and are empty, null, or whitespace only, then they will match.
171      * 
172      * @param cc correction criteria to test against origin entry
173      * @param oe origin entry to test
174      * @return true if origin entry matches the passed in criteria
175      */
176     public static boolean entryMatchesCriteria(CorrectionCriteria cc, OriginEntryFull oe) {
177         OriginEntryFieldFinder oeff = new OriginEntryFieldFinder();
178         Object fieldActualValue = oe.getFieldValue(cc.getCorrectionFieldName());
179         String fieldTestValue = StringUtils.isBlank(cc.getCorrectionFieldValue()) ? "" : cc.getCorrectionFieldValue();
180         String fieldType = oeff.getFieldType(cc.getCorrectionFieldName());
181 
182         String fieldActualValueString = convertToString(fieldActualValue, fieldType);
183 
184         if ("String".equals(fieldType) || "sw".equals(cc.getCorrectionOperatorCode()) || "ew".equals(cc.getCorrectionOperatorCode()) || "ct".equals(cc.getCorrectionOperatorCode())) {
185             return compareStringData(cc, fieldTestValue, fieldActualValueString);
186         }
187         int compareTo = 0;
188         try {
189             if (fieldActualValue == null) {
190                 return false;
191             }
192             if ("Integer".equals(fieldType)) {
193                 compareTo = ((Integer) fieldActualValue).compareTo(Integer.parseInt(fieldTestValue));
194             }
195             if ("KualiDecimal".equals(fieldType)) {
196                 compareTo = ((KualiDecimal) fieldActualValue).compareTo(new KualiDecimal(Double.parseDouble(fieldTestValue)));
197             }
198             if ("BigDecimal".equals(fieldType)) {
199                 compareTo = ((BigDecimal) fieldActualValue).compareTo(new BigDecimal(Double.parseDouble(fieldTestValue)));
200 
201             }
202             if ("Date".equals(fieldType)) {
203                 SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
204                 compareTo = ((Date) fieldActualValue).compareTo(df.parse(fieldTestValue));
205             }
206         }
207         catch (Exception e) {
208             // any exception while parsing data return false
209             return false;
210         }
211         return compareTo(compareTo, cc.getCorrectionOperatorCode());
212     }
213 
214 
215     /**
216      * Compares string data
217      * 
218      * @param cc criteria
219      * @param fieldTestValue test value
220      * @param fieldActualValueString actual value
221      * @return flag true if matches with criteria
222      */
223     public static boolean compareStringData(CorrectionCriteria cc, String fieldTestValue, String fieldActualValueString) {
224         if ("eq".equals(cc.getCorrectionOperatorCode())) {
225             return fieldActualValueString.equals(fieldTestValue);
226         }
227         else if ("ne".equals(cc.getCorrectionOperatorCode())) {
228             return (!fieldActualValueString.equals(fieldTestValue));
229         }
230         else if ("sw".equals(cc.getCorrectionOperatorCode())) {
231             return fieldActualValueString.startsWith(fieldTestValue);
232         }
233         else if ("ew".equals(cc.getCorrectionOperatorCode())) {
234             return fieldActualValueString.endsWith(fieldTestValue);
235         }
236         else if ("ct".equals(cc.getCorrectionOperatorCode())) {
237             return (fieldActualValueString.indexOf(fieldTestValue) > -1);
238         }
239         else if ("lt".equals(cc.getCorrectionOperatorCode())) {
240             return (fieldActualValueString.compareTo(fieldTestValue) < 0);
241         }
242         else if ("le".equals(cc.getCorrectionOperatorCode())) {
243             return (fieldActualValueString.compareTo(fieldTestValue) <= 0);
244         }
245         else if ("gt".equals(cc.getCorrectionOperatorCode())) {
246             return (fieldActualValueString.compareTo(fieldTestValue) > 0);
247         }
248         else if ("ge".equals(cc.getCorrectionOperatorCode())) {
249             return (fieldActualValueString.compareTo(fieldTestValue) >= 0);
250         }
251         throw new IllegalArgumentException("Unknown operator: " + cc.getCorrectionOperatorCode());
252     }
253 
254     /**
255      * Returns true is compared indicator matches
256      * 
257      * @param compareTo
258      * @param operatorCode
259      * @return
260      */
261     public static boolean compareTo(int compareTo, String operatorCode) {
262         if ("eq".equals(operatorCode)) {
263             return (compareTo == 0);
264         }
265         else if ("ne".equals(operatorCode)) {
266             return (compareTo != 0);
267         }
268         else if ("lt".equals(operatorCode)) {
269             return (compareTo < 0);
270         }
271         else if ("le".equals(operatorCode)) {
272             return (compareTo <= 0);
273         }
274         else if ("gt".equals(operatorCode)) {
275             return (compareTo > 0);
276         }
277         else if ("ge".equals(operatorCode)) {
278             return (compareTo >= 0);
279         }
280         throw new IllegalArgumentException("Unknown operator: " + operatorCode);
281     }
282 
283     /**
284      * Converts the value into a string, with the appropriate formatting
285      * 
286      * @param fieldActualValue actual field value
287      * @param fieldType field type (i.e. "String", "Integer", "Date")
288      * @return String object value as a string
289      */
290     public static String convertToString(Object fieldActualValue, String fieldType) {
291         if (fieldActualValue == null) {
292             return "";
293         }
294         if ("String".equals(fieldType)) {
295             return (String) fieldActualValue;
296         }
297         else if ("Integer".equals(fieldType)) {
298             Integer i = (Integer) fieldActualValue;
299             return i.toString();
300         }
301         else if ("KualiDecimal".equals(fieldType)) {
302             KualiDecimal kd = (KualiDecimal) fieldActualValue;
303             return kd.toString();
304         }
305         else if ("BigDecimal".equals(fieldType)) {
306             BigDecimal bd = (BigDecimal) fieldActualValue;
307             return bd.toString();
308         }
309         else if ("Date".equals(fieldType)) {
310             Date d = (Date) fieldActualValue;
311             SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
312             return df.format(d);
313         }
314         return "";
315     }
316 
317     /**
318      * Applies a list of change criteria groups to an origin entry. Note that the returned value, if not null, is a reference to the
319      * same instance as the origin entry passed in (i.e. intentional side effect)
320      * 
321      * @param entry origin entry
322      * @param matchCriteriaOnly if true and no criteria match, then this method will return null
323      * @param changeCriteriaGroups list of change criteria groups to apply
324      * @return the passed in entry instance, or null (see above)
325      */
326     public static OriginEntryFull applyCriteriaToEntry(OriginEntryFull entry, boolean matchCriteriaOnly, List<CorrectionChangeGroup> changeCriteriaGroups) {
327         if (matchCriteriaOnly && !doesEntryMatchAnyCriteriaGroups(entry, changeCriteriaGroups)) {
328             return null;
329         }
330 
331         for (CorrectionChangeGroup ccg : changeCriteriaGroups) {
332             int matches = 0;
333             for (CorrectionCriteria cc : ccg.getCorrectionCriteria()) {
334                 if (entryMatchesCriteria(cc, entry)) {
335                     matches++;
336                 }
337             }
338 
339             // If they all match, change it
340             if (matches == ccg.getCorrectionCriteria().size()) {
341                 for (CorrectionChange change : ccg.getCorrectionChange()) {
342                     // Change the row
343                     entry.setFieldValue(change.getCorrectionFieldName(), change.getCorrectionFieldValue());
344                 }
345             }
346         }
347         return entry;
348     }
349 
350     /**
351      * Returns whether the entry matches any of the criteria groups
352      * 
353      * @param entry origin entry
354      * @param groups collection of correction change group
355      * @return true if origin entry matches any of the criteria groups
356      */
357     public static boolean doesEntryMatchAnyCriteriaGroups(OriginEntryFull entry, Collection<CorrectionChangeGroup> groups) {
358         boolean anyGroupMatch = false;
359         for (CorrectionChangeGroup ccg : groups) {
360             int matches = 0;
361             for (CorrectionCriteria cc : ccg.getCorrectionCriteria()) {
362                 if (CorrectionDocumentUtils.entryMatchesCriteria(cc, entry)) {
363                     matches++;
364                 }
365             }
366 
367             // If they all match, change it
368             if (matches == ccg.getCorrectionCriteria().size()) {
369                 anyGroupMatch = true;
370                 break;
371             }
372         }
373         return anyGroupMatch;
374     }
375 
376     /**
377      * Computes the statistics (credit amount, debit amount, row count) of a collection of origin entries.
378      * 
379      * @param entries list of orgin entry entries
380      * @return {@link OriginEntryStatistics} statistics (credit amount, debit amount, row count) of a collection of origin entries.
381      */
382     public static OriginEntryStatistics getStatistics(Collection<OriginEntryFull> entries) {
383         OriginEntryStatistics oes = new OriginEntryStatistics();
384 
385         for (OriginEntryFull oe : entries) {
386             updateStatisticsWithEntry(oe, oes);
387         }
388         return oes;
389     }
390 
391     /**
392      * Returns whether the origin entry represents a debit
393      * 
394      * @param oe origin entry
395      * @return true if origin entry represents a debit
396      */
397     public static boolean isDebit(OriginEntryFull oe) {
398         return (OLEConstants.GL_DEBIT_CODE.equals(oe.getTransactionDebitCreditCode()));
399     }
400 
401     /**
402      * Returns whether the origin entry represents a budget
403      * 
404      * @param oe origin entry
405      * @return true if origin entry represents a budget
406      */
407     public static boolean isBudget(OriginEntryFull oe) {
408         return OLEConstants.GL_BUDGET_CODE.equals(oe.getTransactionDebitCreditCode());
409     }
410 
411     /**
412      * Returns whether the origin entry represents a credit
413      * 
414      * @param oe origin entry
415      * @return true if origin entry represents a credit
416      */
417     public static boolean isCredit(OriginEntryFull oe) {
418         return OLEConstants.GL_CREDIT_CODE.equals(oe.getTransactionDebitCreditCode());
419     }
420 
421     /**
422      * Given an instance of statistics, it adds information from the passed in entry to the statistics
423      * 
424      * @param entry origin entry
425      * @param statistics adds statistics from the passed in origin entry to the passed in statistics
426      */
427     public static void updateStatisticsWithEntry(OriginEntryFull entry, OriginEntryStatistics statistics) {
428         statistics.incrementCount();
429         if (isDebit(entry)) {
430             statistics.addDebit(entry.getTransactionLedgerEntryAmount());
431         }
432         else if (isCredit(entry)) {
433             statistics.addCredit(entry.getTransactionLedgerEntryAmount());
434         }
435         else {
436             statistics.addBudget(entry.getTransactionLedgerEntryAmount());
437         }
438     }
439 
440     /**
441      * Sets document with the statistics data
442      * 
443      * @param statistics origin entry statistics that are being used to set document
444      * @param document document with statistic information being set
445      */
446     public static void copyStatisticsToDocument(OriginEntryStatistics statistics, GeneralLedgerCorrectionProcessDocument document) {
447         document.setCorrectionCreditTotalAmount(statistics.getCreditTotalAmount());
448         document.setCorrectionDebitTotalAmount(statistics.getDebitTotalAmount());
449         document.setCorrectionBudgetTotalAmount(statistics.getBudgetTotalAmount());
450         document.setCorrectionRowCount(statistics.getRowCount());
451     }
452 }