001: /*
002: * Copyright 2007 The Kuali Foundation.
003: *
004: * Licensed under the Educational Community License, Version 1.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/ecl1.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.module.gl.util;
017:
018: import java.text.SimpleDateFormat;
019: import java.util.Collection;
020: import java.util.Date;
021: import java.util.List;
022:
023: import org.apache.commons.lang.StringUtils;
024: import org.kuali.core.util.KualiDecimal;
025: import org.kuali.kfs.KFSConstants;
026: import org.kuali.kfs.context.SpringContext;
027: import org.kuali.kfs.service.ParameterService;
028: import org.kuali.module.gl.bo.CorrectionChange;
029: import org.kuali.module.gl.bo.CorrectionChangeGroup;
030: import org.kuali.module.gl.bo.CorrectionCriteria;
031: import org.kuali.module.gl.bo.OriginEntryFull;
032: import org.kuali.module.gl.document.CorrectionDocument;
033: import org.kuali.module.gl.web.optionfinder.OriginEntryFieldFinder;
034: import org.kuali.module.labor.bo.LaborOriginEntry;
035: import org.kuali.module.labor.web.optionfinder.LaborOriginEntryFieldFinder;
036:
037: /**
038: * This class provides utility methods for the correction document
039: */
040: public class CorrectionDocumentUtils {
041: private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger
042: .getLogger(CorrectionDocumentUtils.class);
043: public static final int DEFAULT_RECORD_COUNT_FUNCTIONALITY_LIMIT = 1000;
044:
045: /**
046: * The GLCP document will always be on restricted functionality mode, regardless of input group size
047: */
048: public static final int RECORD_COUNT_FUNCTIONALITY_LIMIT_IS_NONE = 0;
049:
050: /**
051: * The GLCP document will never be on restricted functionality mode, regardless of input group size
052: */
053: public static final int RECORD_COUNT_FUNCTIONALITY_LIMIT_IS_UNLIMITED = -1;
054:
055: public static final int DEFAULT_RECORDS_PER_PAGE = 10;
056:
057: /**
058: * This method returns the limit for record count functionality
059: *
060: * @return limit for record count functionality
061: */
062: public static int getRecordCountFunctionalityLimit() {
063: String limitString = SpringContext
064: .getBean(ParameterService.class)
065: .getParameterValue(
066: CorrectionDocument.class,
067: KFSConstants.GeneralLedgerCorrectionProcessApplicationParameterKeys.RECORD_COUNT_FUNCTIONALITY_LIMIT);
068: if (limitString != null) {
069: return Integer.valueOf(limitString);
070: }
071:
072: return DEFAULT_RECORD_COUNT_FUNCTIONALITY_LIMIT;
073: }
074:
075: /**
076: * This method returns the number of records per page
077: *
078: * @return number of records per page
079: *
080: */
081: public static int getRecordsPerPage() {
082: String limitString = SpringContext
083: .getBean(ParameterService.class)
084: .getParameterValue(
085: CorrectionDocument.class,
086: KFSConstants.GeneralLedgerCorrectionProcessApplicationParameterKeys.RECORDS_PER_PAGE);
087: if (limitString != null) {
088: return Integer.valueOf(limitString);
089: }
090: return DEFAULT_RECORDS_PER_PAGE;
091: }
092:
093: /**
094: * This method returns true if input group size is greater than or equal to record count functionality limit
095: *
096: * @param inputGroupSize size of input groups
097: * @param recordCountFunctionalityLimit limit for record count functionality
098: * @return true if input group size is greater than or equal to record count functionality limit
099: */
100: public static boolean isRestrictedFunctionalityMode(
101: int inputGroupSize, int recordCountFunctionalityLimit) {
102: return (recordCountFunctionalityLimit != CorrectionDocumentUtils.RECORD_COUNT_FUNCTIONALITY_LIMIT_IS_UNLIMITED && inputGroupSize >= recordCountFunctionalityLimit)
103: || recordCountFunctionalityLimit == CorrectionDocumentUtils.RECORD_COUNT_FUNCTIONALITY_LIMIT_IS_NONE;
104: }
105:
106: /**
107: * 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
108: * not blank
109: *
110: * @param correctionCriteria validated correction criteria
111: * @return true if correction criteria is valid for adding
112: */
113: public static boolean validCorrectionCriteriaForAdding(
114: CorrectionCriteria correctionCriteria) {
115: String fieldName = correctionCriteria.getCorrectionFieldName();
116: if (StringUtils.isBlank(fieldName)) {
117: return false;
118: }
119: return true;
120: }
121:
122: /**
123: * 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
124: *
125: * @param correctionCriteria validated correction criteria
126: * @return true if correction criteria is valid for saving
127: */
128: public static boolean validCorrectionCriteriaForSaving(
129: CorrectionCriteria correctionCriteria) {
130: return correctionCriteria == null
131: || (StringUtils.isBlank(correctionCriteria
132: .getCorrectionFieldName()) && StringUtils
133: .isBlank(correctionCriteria
134: .getCorrectionFieldValue()));
135: }
136:
137: /**
138: * 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
139: * blank
140: *
141: * @param correctionChange validated correction change
142: * @return true is correction change is valid for adding
143: */
144: public static boolean validCorrectionChangeForAdding(
145: CorrectionChange correctionChange) {
146: String fieldName = correctionChange.getCorrectionFieldName();
147: if (StringUtils.isBlank(fieldName)) {
148: return false;
149: }
150: return true;
151: }
152:
153: /**
154: * 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
155: *
156: * @param correctionCriteria validated correction criteria
157: * @return true if correction change is valid for saving (i.e. correction change is null or correction field name and field value are blank)
158: */
159: public static boolean validCorrectionChangeForSaving(
160: CorrectionChange correctionChange) {
161: return correctionChange == null
162: || (StringUtils.isBlank(correctionChange
163: .getCorrectionFieldName()) && StringUtils
164: .isBlank(correctionChange
165: .getCorrectionFieldValue()));
166: }
167:
168: /**
169: * Sets all origin entries' entry IDs to null within the collection.
170: *
171: * @param originEntries collection of origin entries
172: */
173: public static void setAllEntryIdsToNull(
174: Collection<OriginEntryFull> originEntries) {
175: for (OriginEntryFull entry : originEntries) {
176: entry.setEntryId(null);
177: }
178: }
179:
180: /**
181: * Sets all origin entries' entry IDs to be sequential starting from 0 in the collection
182: *
183: * @param originEntries collection of origin entries
184: */
185: public static void setSequentialEntryIds(
186: Collection<OriginEntryFull> originEntries) {
187: int index = 0;
188: for (OriginEntryFull entry : originEntries) {
189: entry.setEntryId(new Integer(index));
190: index++;
191: }
192: }
193:
194: /**
195: * Returns whether an origin entry matches the passed in criteria. If both the criteria and actual value are both String types
196: * and are empty, null, or whitespace only, then they will match.
197: *
198: * @param cc correction criteria to test against origin entry
199: * @param oe origin entry to test
200: * @return true if origin entry matches the passed in criteria
201: */
202: public static boolean entryMatchesCriteria(CorrectionCriteria cc,
203: OriginEntryFull oe) {
204: OriginEntryFieldFinder oeff = new OriginEntryFieldFinder();
205: Object fieldActualValue = oe.getFieldValue(cc
206: .getCorrectionFieldName());
207: String fieldTestValue = StringUtils.isBlank(cc
208: .getCorrectionFieldValue()) ? "" : cc
209: .getCorrectionFieldValue();
210: String fieldType = oeff.getFieldType(cc
211: .getCorrectionFieldName());
212: String fieldActualValueString = convertToString(
213: fieldActualValue, fieldType);
214:
215: if ("String".equals(fieldType)
216: && StringUtils.isBlank(fieldActualValueString)) {
217: fieldActualValueString = "";
218: }
219:
220: if ("eq".equals(cc.getCorrectionOperatorCode())) {
221: return fieldActualValueString.equals(fieldTestValue);
222: } else if ("ne".equals(cc.getCorrectionOperatorCode())) {
223: return (!fieldActualValueString.equals(fieldTestValue));
224: } else if ("sw".equals(cc.getCorrectionOperatorCode())) {
225: return fieldActualValueString.startsWith(fieldTestValue);
226: } else if ("ew".equals(cc.getCorrectionOperatorCode())) {
227: return fieldActualValueString.endsWith(fieldTestValue);
228: } else if ("ct".equals(cc.getCorrectionOperatorCode())) {
229: return (fieldActualValueString.indexOf(fieldTestValue) > -1);
230: }
231: throw new IllegalArgumentException("Unknown operator: "
232: + cc.getCorrectionOperatorCode());
233: }
234:
235: /**
236: * Returns whether an origin entry matches the passed in criteria. If both the criteria and actual value are both String types
237: * and are empty, null, or whitespace only, then they will match.
238: *
239: * @param cc correction criteria to test against origin entry
240: * @param oe origin entry to test
241: * @return true if origin entry matches the passed in criteria
242: */
243: public static boolean laborEntryMatchesCriteria(
244: CorrectionCriteria cc, OriginEntryFull oe) {
245: LaborOriginEntryFieldFinder loeff = new LaborOriginEntryFieldFinder();
246: LaborOriginEntry loe = (LaborOriginEntry) oe;
247: Object fieldActualValue = loe.getFieldValue(cc
248: .getCorrectionFieldName());
249: String fieldTestValue = StringUtils.isBlank(cc
250: .getCorrectionFieldValue()) ? "" : cc
251: .getCorrectionFieldValue();
252: String fieldType = loeff.getFieldType(cc
253: .getCorrectionFieldName());
254: String fieldActualValueString = convertToString(
255: fieldActualValue, fieldType);
256:
257: if ("String".equals(fieldType)
258: && StringUtils.isBlank(fieldActualValueString)) {
259: fieldActualValueString = "";
260: }
261:
262: if ("eq".equals(cc.getCorrectionOperatorCode())) {
263: return fieldActualValueString.equals(fieldTestValue);
264: } else if ("ne".equals(cc.getCorrectionOperatorCode())) {
265: return (!fieldActualValueString.equals(fieldTestValue));
266: } else if ("sw".equals(cc.getCorrectionOperatorCode())) {
267: return fieldActualValueString.startsWith(fieldTestValue);
268: } else if ("ew".equals(cc.getCorrectionOperatorCode())) {
269: return fieldActualValueString.endsWith(fieldTestValue);
270: } else if ("ct".equals(cc.getCorrectionOperatorCode())) {
271: return (fieldActualValueString.indexOf(fieldTestValue) > -1);
272: }
273: throw new IllegalArgumentException("Unknown operator: "
274: + cc.getCorrectionOperatorCode());
275: }
276:
277: /**
278: * Converts the value into a string, with the appropriate formatting
279: *
280: * @param fieldActualValue actual field value
281: * @param fieldType field type (i.e. "String", "Integer", "Date")
282: * @return String object value as a string
283: */
284: public static String convertToString(Object fieldActualValue,
285: String fieldType) {
286: if (fieldActualValue == null) {
287: return "";
288: }
289: if ("String".equals(fieldType)) {
290: return (String) fieldActualValue;
291: } else if ("Integer".equals(fieldType)) {
292: Integer i = (Integer) fieldActualValue;
293: return i.toString();
294: } else if ("KualiDecimal".equals(fieldType)) {
295: KualiDecimal kd = (KualiDecimal) fieldActualValue;
296: return kd.toString();
297: } else if ("Date".equals(fieldType)) {
298: Date d = (Date) fieldActualValue;
299: SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
300: return df.format(d);
301: }
302: return "";
303: }
304:
305: /**
306: * Applies a list of change criteria groups to an origin entry. Note that the returned value, if not null, is a reference to the
307: * same instance as the origin entry passed in (i.e. intentional side effect)
308: *
309: * @param entry origin entry
310: * @param matchCriteriaOnly if true and no criteria match, then this method will return null
311: * @param changeCriteriaGroups list of change criteria groups to apply
312: * @return the passed in entry instance, or null (see above)
313: */
314: public static OriginEntryFull applyCriteriaToEntry(
315: OriginEntryFull entry, boolean matchCriteriaOnly,
316: List<CorrectionChangeGroup> changeCriteriaGroups) {
317: if (matchCriteriaOnly
318: && !doesEntryMatchAnyCriteriaGroups(entry,
319: changeCriteriaGroups)) {
320: return null;
321: }
322:
323: for (CorrectionChangeGroup ccg : changeCriteriaGroups) {
324: int matches = 0;
325: for (CorrectionCriteria cc : ccg.getCorrectionCriteria()) {
326: if (entryMatchesCriteria(cc, entry)) {
327: matches++;
328: }
329: }
330:
331: // If they all match, change it
332: if (matches == ccg.getCorrectionCriteria().size()) {
333: for (CorrectionChange change : ccg
334: .getCorrectionChange()) {
335: // Change the row
336: entry.setFieldValue(
337: change.getCorrectionFieldName(), change
338: .getCorrectionFieldValue());
339: }
340: }
341: }
342: return entry;
343: }
344:
345: /**
346: * Returns whether the entry matches any of the criteria groups
347: *
348: * @param entry origin entry
349: * @param groups collection of correction change group
350: * @return true if origin entry matches any of the criteria groups
351: */
352: public static boolean doesEntryMatchAnyCriteriaGroups(
353: OriginEntryFull entry,
354: Collection<CorrectionChangeGroup> groups) {
355: boolean anyGroupMatch = false;
356: for (CorrectionChangeGroup ccg : groups) {
357: int matches = 0;
358: for (CorrectionCriteria cc : ccg.getCorrectionCriteria()) {
359: if (CorrectionDocumentUtils.entryMatchesCriteria(cc,
360: entry)) {
361: matches++;
362: }
363: }
364:
365: // If they all match, change it
366: if (matches == ccg.getCorrectionCriteria().size()) {
367: anyGroupMatch = true;
368: break;
369: }
370: }
371: return anyGroupMatch;
372: }
373:
374: /**
375: * Returns whether the labor entry matches any of the criteria groups
376: *
377: * @param entry labor origin entry
378: * @param groups collection of correction change group
379: * @return true if labor origin entry matches any of the criteria groups
380: */
381: public static boolean doesLaborEntryMatchAnyCriteriaGroups(
382: OriginEntryFull entry,
383: Collection<CorrectionChangeGroup> groups) {
384: boolean anyGroupMatch = false;
385: for (CorrectionChangeGroup ccg : groups) {
386: int matches = 0;
387: for (CorrectionCriteria cc : ccg.getCorrectionCriteria()) {
388: if (CorrectionDocumentUtils.laborEntryMatchesCriteria(
389: cc, entry)) {
390: matches++;
391: }
392: }
393:
394: // If they all match, change it
395: if (matches == ccg.getCorrectionCriteria().size()) {
396: anyGroupMatch = true;
397: break;
398: }
399: }
400: return anyGroupMatch;
401: }
402:
403: /**
404: * Computes the statistics (credit amount, debit amount, row count) of a collection of origin entries.
405: *
406: * @param entries list of orgin entry entries
407: * @return {@link OriginEntryStatistics} statistics (credit amount, debit amount, row count) of a collection of origin entries.
408: */
409: public static OriginEntryStatistics getStatistics(
410: Collection<OriginEntryFull> entries) {
411: OriginEntryStatistics oes = new OriginEntryStatistics();
412:
413: for (OriginEntryFull oe : entries) {
414: updateStatisticsWithEntry(oe, oes);
415: }
416: return oes;
417: }
418:
419: /**
420: * Returns whether the origin entry represents a debit
421: *
422: * @param oe origin entry
423: * @return true if origin entry represents a debit
424: */
425: public static boolean isDebit(OriginEntryFull oe) {
426: return (KFSConstants.GL_DEBIT_CODE.equals(oe
427: .getTransactionDebitCreditCode()));
428: }
429:
430: /**
431: * Returns whether the origin entry represents a budget
432: *
433: * @param oe origin entry
434: * @return true if origin entry represents a budget
435: */
436: public static boolean isBudget(OriginEntryFull oe) {
437: return KFSConstants.GL_BUDGET_CODE.equals(oe
438: .getTransactionDebitCreditCode());
439: }
440:
441: /**
442: * Returns whether the origin entry represents a credit
443: *
444: * @param oe origin entry
445: * @return true if origin entry represents a credit
446: */
447: public static boolean isCredit(OriginEntryFull oe) {
448: return KFSConstants.GL_CREDIT_CODE.equals(oe
449: .getTransactionDebitCreditCode());
450: }
451:
452: /**
453: * Given an instance of statistics, it adds information from the passed in entry to the statistics
454: *
455: * @param entry origin entry
456: * @param statistics adds statistics from the passed in origin entry to the passed in statistics
457: */
458: public static void updateStatisticsWithEntry(OriginEntryFull entry,
459: OriginEntryStatistics statistics) {
460: statistics.incrementCount();
461: if (isDebit(entry)) {
462: statistics
463: .addDebit(entry.getTransactionLedgerEntryAmount());
464: } else if (isCredit(entry)) {
465: statistics.addCredit(entry
466: .getTransactionLedgerEntryAmount());
467: } else {
468: statistics.addBudget(entry
469: .getTransactionLedgerEntryAmount());
470: }
471: }
472:
473: /**
474: * Sets document with the statistics data
475: *
476: * @param statistics origin entry statistics that are being used to set document
477: * @param document document with statistic information being set
478: */
479: public static void copyStatisticsToDocument(
480: OriginEntryStatistics statistics,
481: CorrectionDocument document) {
482: document.setCorrectionCreditTotalAmount(statistics
483: .getCreditTotalAmount());
484: document.setCorrectionDebitTotalAmount(statistics
485: .getDebitTotalAmount());
486: document.setCorrectionBudgetTotalAmount(statistics
487: .getBudgetTotalAmount());
488: document.setCorrectionRowCount(statistics.getRowCount());
489: }
490: }
|