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.module.ec.util;
017    
018    import java.util.ArrayList;
019    import java.util.HashMap;
020    import java.util.List;
021    import java.util.Map;
022    
023    import org.kuali.kfs.module.ec.EffortConstants;
024    import org.kuali.kfs.module.ec.EffortPropertyConstants;
025    import org.kuali.kfs.module.ec.businessobject.EffortCertificationDetail;
026    import org.kuali.kfs.sys.DynamicCollectionComparator;
027    import org.kuali.kfs.sys.ObjectUtil;
028    import org.kuali.kfs.sys.DynamicCollectionComparator.SortOrder;
029    import org.kuali.rice.kns.util.KualiDecimal;
030    
031    /**
032     * grouping a set of detail lines. The class is implemented to manage: summary line and delegating line.
033     */
034    public class DetailLineGroup {
035        private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(DetailLineGroup.class);
036    
037        EffortCertificationDetail summaryDetailLine;
038        EffortCertificationDetail delegateDetailLine;
039        List<EffortCertificationDetail> detailLines;
040    
041        /**
042         * Constructs a DetailLineGroup.java.
043         */
044        public DetailLineGroup() {
045            this(null);
046        }
047    
048        /**
049         * Constructs a DetailLineGroup.java.
050         * 
051         * @param detailLine the given detail line
052         */
053        public DetailLineGroup(EffortCertificationDetail newDetailLine) {
054            detailLines = new ArrayList<EffortCertificationDetail>();
055            summaryDetailLine = new EffortCertificationDetail();
056    
057            if (newDetailLine != null) {
058                String groupId = getKeysAsString(newDetailLine);
059                ObjectUtil.buildObject(summaryDetailLine, newDetailLine);
060                summaryDetailLine.setGroupId(groupId);
061                
062                this.addNewLineIntoGroup(newDetailLine, groupId);
063            }
064    
065            summaryDetailLine.setFinancialObjectCode(null);
066            summaryDetailLine.setPositionNumber(null);
067        }
068    
069        /**
070         * update the effort percent of the delegate detail line if the effort on the summary line has been changed
071         */
072        public void updateDelegateDetailLineEffort() {
073            Integer difference = this.getEffortPercentChanged();
074            if (difference != 0) {
075                Integer effortPercent = delegateDetailLine.getEffortCertificationUpdatedOverallPercent() + difference;
076                delegateDetailLine.setEffortCertificationUpdatedOverallPercent(effortPercent);
077            }
078        }
079    
080        /**
081         * update the effort percents of the detail lines if the effort on the summary line has been changed
082         */
083        public void updateDetailLineEffortPercent() {
084            int totalDifference = this.getEffortPercentChanged();
085    
086            List<EffortCertificationDetail> detailLines = this.getDetailLines();
087            DynamicCollectionComparator.sort(detailLines, SortOrder.DESC, EffortPropertyConstants.PERSISED_PAYROLL_AMOUNT);
088    
089            // restore the intial effort percents before update the detail lines
090            for (EffortCertificationDetail detailLine : detailLines) {
091                detailLine.setEffortCertificationUpdatedOverallPercent(detailLine.getPersistedEffortPercent());
092            }
093    
094            for (EffortCertificationDetail detailLine : detailLines) {
095                if (totalDifference == 0) {
096                    break;
097                }
098    
099                int currentPercent = detailLine.getPersistedEffortPercent();
100                int currentDifference = currentPercent + totalDifference;
101                boolean needUpdateMultipleLines = (currentDifference < 0);
102    
103                int effortPercent = needUpdateMultipleLines ? 0 : currentDifference;
104                detailLine.setEffortCertificationUpdatedOverallPercent(effortPercent);
105    
106                totalDifference = needUpdateMultipleLines ? currentDifference : 0;
107            }
108        }
109    
110        /**
111         * update the payroll amounts of the detail lines if the payroll amount on the summary line has been changed
112         */
113        public void updateDetailLinePayrollAmount() {
114            KualiDecimal totalDifference = this.getPayrollAmountChanged();
115            if (totalDifference.isZero()) {
116                return;
117            }
118    
119            List<EffortCertificationDetail> detailLines = this.getDetailLines();
120            DynamicCollectionComparator.sort(detailLines, SortOrder.DESC, EffortPropertyConstants.PERSISED_PAYROLL_AMOUNT);
121    
122            // restore the intial payroll amounts before update the detail lines
123            for (EffortCertificationDetail detailLine : detailLines) {
124                detailLine.setEffortCertificationPayrollAmount(detailLine.getPersistedPayrollAmount());
125            }
126    
127            for (EffortCertificationDetail detailLine : detailLines) {
128                if (totalDifference.isZero()) {
129                    break;
130                }
131    
132                KualiDecimal currentAmount = detailLine.getPersistedPayrollAmount();
133                KualiDecimal currentDifference = currentAmount.add(totalDifference);
134                boolean needUpdateMultipleLines = currentDifference.isNegative();
135    
136                KualiDecimal payrollAmount = needUpdateMultipleLines ? KualiDecimal.ZERO : currentDifference;
137                detailLine.setEffortCertificationPayrollAmount(payrollAmount);
138    
139                totalDifference = needUpdateMultipleLines ? currentDifference : KualiDecimal.ZERO;
140            }
141        }
142    
143        /**
144         * group the given detail lines by the key fields
145         * 
146         * @param detailLines the given detail lines
147         * @param keyFields the given key fields
148         * @return the groups of detail lines
149         */
150        public static Map<String, DetailLineGroup> groupDetailLines(List<EffortCertificationDetail> detailLines) {
151            Map<String, DetailLineGroup> detailLineGroupMap = new HashMap<String, DetailLineGroup>();
152    
153            for (EffortCertificationDetail line : detailLines) {
154                String groupId = getKeysAsString(line);
155    
156                if (detailLineGroupMap.containsKey(groupId)) {
157                    DetailLineGroup group = detailLineGroupMap.get(groupId);
158                    group.addNewLineIntoGroup(line, groupId);
159                }
160                else {
161                    DetailLineGroup group = new DetailLineGroup(line);
162                    detailLineGroupMap.put(groupId, group);
163                }
164            }
165    
166            return detailLineGroupMap;
167        }
168    
169        /**
170         * concat the keys of the given detail line as a single string
171         * 
172         * @param line the given detail line
173         * @return a single string built from the keys of the given detail line
174         */
175        public static String getKeysAsString(EffortCertificationDetail line) {
176            return ObjectUtil.concatPropertyAsString(line, EffortConstants.DETAIL_LINES_GROUPING_FILEDS);
177        }
178    
179        /**
180         * get the difference between the updated effort amount and the current effort amount
181         * 
182         * @return the difference between the updated effort amount and the current effort amount
183         */
184        private Integer getEffortPercentChanged() {
185            Integer currentEffortPercent = EffortCertificationDetail.getTotalPersistedEffortPercent(detailLines);
186            Integer updatedEffortPercent = summaryDetailLine.getEffortCertificationUpdatedOverallPercent();
187    
188            return updatedEffortPercent - currentEffortPercent;
189        }
190    
191        /**
192         * get the difference between the updated payroll amount and the current payroll amount
193         * 
194         * @return the difference between the updated payroll amount and the current payroll amount
195         */
196        private KualiDecimal getPayrollAmountChanged() {
197            KualiDecimal currentAmount = EffortCertificationDetail.getTotalPersistedPayrollAmount(detailLines);
198            KualiDecimal updatedAmount = summaryDetailLine.getEffortCertificationPayrollAmount();
199    
200            return updatedAmount.subtract(currentAmount);
201        }
202    
203        /**
204         * update the group when a new detail line is added
205         * 
206         * @param line the new detail line
207         */
208        private void addNewLineIntoGroup(EffortCertificationDetail newDetailLine, String groupId) {
209            if (detailLines.contains(newDetailLine)) {
210                return;
211            }
212            
213            newDetailLine.setGroupId(groupId);
214            
215            detailLines.add(newDetailLine);
216            delegateDetailLine = this.getDetailLineWithMaxPayrollAmount(detailLines);
217    
218            this.updateSummaryDetailLineAmount();
219        }
220    
221        /**
222         * update the payroll amounts and effort percents based on current detail lines
223         */
224        private void updateSummaryDetailLineAmount() {
225            Integer originalEffortPercent = EffortCertificationDetail.getTotalOriginalEffortPercent(detailLines);
226            summaryDetailLine.setEffortCertificationCalculatedOverallPercent(originalEffortPercent);
227    
228            Integer effortPercent = EffortCertificationDetail.getTotalEffortPercent(detailLines);
229            summaryDetailLine.setEffortCertificationUpdatedOverallPercent(effortPercent);
230    
231            Integer persistedEffortPercent = EffortCertificationDetail.getTotalPersistedEffortPercent(detailLines);
232            summaryDetailLine.setPersistedEffortPercent(persistedEffortPercent);
233    
234            KualiDecimal originalPayrollAmount = EffortCertificationDetail.getTotalOriginalPayrollAmount(detailLines);
235            summaryDetailLine.setEffortCertificationOriginalPayrollAmount(originalPayrollAmount);
236    
237            KualiDecimal payrollAmount = EffortCertificationDetail.getTotalPayrollAmount(detailLines);
238            summaryDetailLine.setEffortCertificationPayrollAmount(payrollAmount);
239    
240            KualiDecimal persistedPayrollAmount = EffortCertificationDetail.getTotalPersistedPayrollAmount(detailLines);
241            summaryDetailLine.setPersistedPayrollAmount(persistedPayrollAmount);
242        }
243    
244        /**
245         * find the detail lines that have max payroll amount
246         * 
247         * @return the detail lines that have max payroll amount
248         */
249        private EffortCertificationDetail getDetailLineWithMaxPayrollAmount(List<EffortCertificationDetail> detailLines) {
250            KualiDecimal maxAmount = null;
251            EffortCertificationDetail detailLineWithMaxPayrollAmount = null;
252    
253            for (EffortCertificationDetail line : detailLines) {
254                KualiDecimal currentAmount = line.getEffortCertificationOriginalPayrollAmount();
255    
256                if (detailLineWithMaxPayrollAmount == null) {
257                    maxAmount = currentAmount;
258                    detailLineWithMaxPayrollAmount = line;
259                    continue;
260                }
261    
262                if (maxAmount.isLessThan(currentAmount)) {
263                    maxAmount = currentAmount;
264                    detailLineWithMaxPayrollAmount = line;
265                }
266            }
267    
268            return detailLineWithMaxPayrollAmount;
269        }
270    
271        /**
272         * Gets the summaryDetailLine attribute.
273         * 
274         * @return Returns the summaryDetailLine.
275         */
276        public EffortCertificationDetail getSummaryDetailLine() {
277            return summaryDetailLine;
278        }
279    
280        /**
281         * Sets the summaryDetailLine attribute value.
282         * 
283         * @param summaryDetailLine The summaryDetailLine to set.
284         */
285        public void setSummaryDetailLine(EffortCertificationDetail summaryDetailLine) {
286            this.summaryDetailLine = summaryDetailLine;
287        }
288    
289        /**
290         * Gets the detailLines attribute.
291         * 
292         * @return Returns the detailLines.
293         */
294        public List<EffortCertificationDetail> getDetailLines() {
295            return detailLines;
296        }
297    
298        /**
299         * Sets the detailLines attribute value.
300         * 
301         * @param detailLines The detailLines to set.
302         */
303        public void setDetailLines(List<EffortCertificationDetail> detailLines) {
304            this.detailLines = detailLines;
305        }
306    
307        /**
308         * Gets the delegateDetailLine attribute.
309         * 
310         * @return Returns the delegateDetailLine.
311         */
312        public EffortCertificationDetail getDelegateDetailLine() {
313            return delegateDetailLine;
314        }
315    
316        /**
317         * Sets the delegateDetailLine attribute value.
318         * 
319         * @param delegateDetailLine The delegateDetailLine to set.
320         */
321        public void setDelegateDetailLine(EffortCertificationDetail delegateDetailLine) {
322            this.delegateDetailLine = delegateDetailLine;
323        }
324    }