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.document.web.struts;
017    
018    import java.math.BigDecimal;
019    import java.util.ArrayList;
020    import java.util.HashMap;
021    import java.util.List;
022    import java.util.Map;
023    
024    import javax.servlet.http.HttpServletRequest;
025    import javax.servlet.http.HttpServletResponse;
026    
027    import org.apache.commons.lang.StringUtils;
028    import org.apache.struts.action.ActionForm;
029    import org.apache.struts.action.ActionForward;
030    import org.apache.struts.action.ActionMapping;
031    import org.kuali.kfs.integration.cg.ContractsAndGrantsModuleService;
032    import org.kuali.kfs.module.ec.EffortConstants;
033    import org.kuali.kfs.module.ec.EffortConstants.EffortCertificationEditMode;
034    import org.kuali.kfs.module.ec.EffortPropertyConstants;
035    import org.kuali.kfs.module.ec.businessobject.EffortCertificationDetail;
036    import org.kuali.kfs.module.ec.businessobject.EffortCertificationDetailLineOverride;
037    import org.kuali.kfs.module.ec.document.EffortCertificationDocument;
038    import org.kuali.kfs.module.ec.document.authorization.EffortCertificationDocumentAuthorizer;
039    import org.kuali.kfs.module.ec.document.validation.event.AddDetailLineEvent;
040    import org.kuali.kfs.module.ec.document.validation.event.CheckDetailLineAmountEvent;
041    import org.kuali.kfs.module.ec.document.validation.impl.EffortCertificationDocumentRuleUtil;
042    import org.kuali.kfs.module.ec.service.EffortCertificationDocumentService;
043    import org.kuali.kfs.module.ec.util.DetailLineGroup;
044    import org.kuali.kfs.sys.DynamicCollectionComparator;
045    import org.kuali.kfs.sys.DynamicCollectionComparator.SortOrder;
046    import org.kuali.kfs.sys.KFSConstants;
047    import org.kuali.kfs.sys.KFSPropertyConstants;
048    import org.kuali.kfs.sys.ObjectUtil;
049    import org.kuali.kfs.sys.context.SpringContext;
050    import org.kuali.rice.kew.exception.WorkflowException;
051    import org.kuali.rice.kim.bo.impl.KimAttributes;
052    import org.kuali.rice.kim.util.KimConstants;
053    import org.kuali.rice.kns.service.BusinessObjectService;
054    import org.kuali.rice.kns.service.DocumentHelperService;
055    import org.kuali.rice.kns.util.GlobalVariables;
056    import org.kuali.rice.kns.util.KNSConstants;
057    import org.kuali.rice.kns.util.KualiDecimal;
058    import org.kuali.rice.kns.util.ObjectUtils;
059    import org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase;
060    
061    /**
062     * This class handles Actions for EffortCertification document approval.
063     */
064    public class CertificationReportAction extends EffortCertificationAction {
065        protected static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(CertificationReportAction.class);
066    
067        /**
068         * recalculate the detail line
069         */
070        public ActionForward recalculate(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
071            int lineToRecalculateIndex = getLineToDelete(request);
072    
073            EffortCertificationForm effortForm = (EffortCertificationForm) form;
074            EffortCertificationDocument effortDocument = (EffortCertificationDocument) effortForm.getDocument();
075            List<EffortCertificationDetail> detailLines = effortDocument.getEffortCertificationDetailLines();
076            EffortCertificationDetail lineToRecalculate = detailLines.get(lineToRecalculateIndex);
077            EffortCertificationDocumentRuleUtil.applyDefaultValues(lineToRecalculate);
078    
079            String errorPathPrefix = KFSPropertyConstants.DOCUMENT + "." + EffortPropertyConstants.EFFORT_CERTIFICATION_DETAIL_LINES;
080            boolean isValid = this.invokeRules(new CheckDetailLineAmountEvent("", errorPathPrefix, effortDocument, lineToRecalculate));
081            if (isValid) {
082                KualiDecimal totalPayrollAmount = effortDocument.getTotalOriginalPayrollAmount();
083                lineToRecalculate.recalculatePayrollAmount(totalPayrollAmount);
084            }
085    
086            return mapping.findForward(KFSConstants.MAPPING_BASIC);
087        }
088    
089        /**
090         * add New Effort Certification Detail Lines
091         */
092        public ActionForward add(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
093            CertificationReportForm certificationReportForm = (CertificationReportForm) form;
094            EffortCertificationDocument effortDocument = (EffortCertificationDocument) certificationReportForm.getDocument();
095            List<EffortCertificationDetail> detailLines = effortDocument.getEffortCertificationDetailLines();
096            EffortCertificationDetail newDetailLine = certificationReportForm.getNewDetailLine();
097    
098            // KFSMI-798 - refreshNonUpdatableReferences() used instead of refresh(),
099            // EffortCertificationDetail does not have any updatable references
100            newDetailLine.refreshNonUpdateableReferences();
101            newDetailLine.setPositionNumber(effortDocument.getDefaultPositionNumber());
102            newDetailLine.setFinancialObjectCode(effortDocument.getDefaultObjectCode());
103            newDetailLine.setNewLineIndicator(true);
104    
105            EffortCertificationDetail workingDetailLine = new EffortCertificationDetail();
106            ObjectUtil.buildObject(workingDetailLine, newDetailLine);
107    
108            EffortCertificationDocumentRuleUtil.applyDefaultValues(workingDetailLine);
109    
110            if (newDetailLine.isAccountExpiredOverride()) {
111                this.updateDetailLineOverrideCode(workingDetailLine);
112                this.updateDetailLineOverrideCode(newDetailLine);
113            }
114    
115            // check business rules
116            boolean isValid = this.invokeRules(new AddDetailLineEvent("", EffortPropertyConstants.EFFORT_CERTIFICATION_DOCUMENT_NEW_LINE, effortDocument, workingDetailLine));
117            if (isValid) {
118                if (EffortCertificationDocumentRuleUtil.hasA21SubAccount(workingDetailLine)) {
119                    EffortCertificationDocumentRuleUtil.updateSourceAccountInformation(workingDetailLine);
120                }
121    
122                this.resetPersistedFields(workingDetailLine);
123                detailLines.add(workingDetailLine);
124                certificationReportForm.setNewDetailLine(certificationReportForm.createNewDetailLine());
125            }
126            else {
127                EffortCertificationDetailLineOverride.processForOutput(newDetailLine);
128            }
129    
130            this.processDetailLineOverrides(detailLines);
131    
132            return mapping.findForward(KFSConstants.MAPPING_BASIC);
133        }
134    
135        /**
136         * delete detail line
137         */
138        public ActionForward delete(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
139            int lineToDeleteIndex = getLineToDelete(request);
140    
141            EffortCertificationForm effortForm = (EffortCertificationForm) form;
142            EffortCertificationDocument effortDocument = (EffortCertificationDocument) effortForm.getDocument();
143            List<EffortCertificationDetail> detailLines = effortDocument.getEffortCertificationDetailLines();
144    
145            detailLines.remove(lineToDeleteIndex);
146    
147            return mapping.findForward(KFSConstants.MAPPING_BASIC);
148        }
149    
150        /**
151         * revert the detail line to the original values
152         */
153        public ActionForward revert(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
154            int lineToRevertIndex = getLineToDelete(request);
155    
156            EffortCertificationForm effortForm = (EffortCertificationForm) form;
157            EffortCertificationDocument effortDocument = (EffortCertificationDocument) effortForm.getDocument();
158            List<EffortCertificationDetail> detailLines = effortDocument.getEffortCertificationDetailLines();
159    
160            this.revertDetaiLine(detailLines, lineToRevertIndex);
161    
162            return mapping.findForward(KFSConstants.MAPPING_BASIC);
163        }
164    
165        /**
166         * @see org.kuali.rice.kns.web.struts.action.KualiDocumentActionBase#loadDocument(org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase)
167         */
168        @Override
169        protected void loadDocument(KualiDocumentFormBase kualiDocumentFormBase) throws WorkflowException {
170            super.loadDocument(kualiDocumentFormBase);
171    
172            CertificationReportForm certificationReportForm = (CertificationReportForm) kualiDocumentFormBase;
173            if (this.isSummarizeDetailLinesRendered(certificationReportForm)) {
174                this.refreshDetailLineGroupMap(certificationReportForm);
175            }
176        }
177    
178        /**
179         * @see org.kuali.rice.kns.web.struts.action.KualiAction#refresh(org.apache.struts.action.ActionMapping,
180         *      org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
181         */
182        @Override
183        public ActionForward refresh(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
184            ActionForward actionForward = super.refresh(mapping, form, request, response);
185    
186            CertificationReportForm certificationReportForm = (CertificationReportForm) form;
187            for (EffortCertificationDetail detailLine : certificationReportForm.getDetailLines()) {
188                detailLine.refreshNonUpdateableReferences();
189    
190                EffortCertificationDocumentRuleUtil.applyDefaultValues(detailLine);
191            }
192    
193            return actionForward;
194        }
195    
196        /**
197         * @see org.apache.struts.action.Action#execute(org.apache.struts.action.ActionMapping, org.apache.struts.action.ActionForm,
198         *      javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
199         */
200        @Override
201        public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
202            CertificationReportForm certificationReportForm = (CertificationReportForm) form;
203            if (this.isSummarizeDetailLinesRendered(certificationReportForm)) {
204                this.updateDetailLinesFromSummaryLines(certificationReportForm);
205            }
206    
207            ActionForward actionForward = super.execute(mapping, form, request, response);
208            this.refresh(mapping, form, request, response);
209    
210            return actionForward;
211        }
212    
213        /**
214         * sort the detail lines by column
215         */
216        public ActionForward sortDetailLineByColumn(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
217            CertificationReportForm certificationReportForm = (CertificationReportForm) form;
218    
219            String methodToCallAttribute = (String) request.getAttribute(KNSConstants.METHOD_TO_CALL_ATTRIBUTE);
220            String sortMethodName = EffortConstants.SORT_DETAIL_LINE_BY_COLUMN_METHOD_NAME + ".";
221            String sortColumn = StringUtils.substringBetween(methodToCallAttribute, sortMethodName, ".");
222    
223            this.toggleSortOrder(certificationReportForm);
224            this.sortDetailLine(certificationReportForm, certificationReportForm.getDetailLines(), sortColumn);
225    
226            if (this.isSummarizeDetailLinesRendered(certificationReportForm)) {
227                this.sortDetailLine(certificationReportForm, certificationReportForm.getSummarizedDetailLines(), sortColumn);
228            }
229    
230            return mapping.findForward(KFSConstants.MAPPING_BASIC);
231        }
232    
233        /**
234         * recalculate a detail line in summarized detail lines and make a corresponding update on the underlying detail lines
235         */
236        public ActionForward recalculateSummarizedDetailLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
237            CertificationReportForm certificationReportForm = (CertificationReportForm) form;
238            EffortCertificationDocument effortDocument = (EffortCertificationDocument) certificationReportForm.getDocument();
239            KualiDecimal totalPayrollAmount = effortDocument.getTotalOriginalPayrollAmount();
240    
241            // recalculate the selected summary line
242            List<EffortCertificationDetail> summarizedDetailLines = certificationReportForm.getSummarizedDetailLines();
243            int lineToRecalculateIndex = this.getSelectedLine(request);
244            EffortCertificationDetail lineToRecalculate = summarizedDetailLines.get(lineToRecalculateIndex);
245            EffortCertificationDocumentRuleUtil.applyDefaultValues(lineToRecalculate);
246    
247            String errorPathPrefix = KFSPropertyConstants.DOCUMENT + "." + EffortPropertyConstants.SUMMARIZED_DETAIL_LINES + "[" + lineToRecalculateIndex + "]";
248            boolean isValid = this.invokeRules(new CheckDetailLineAmountEvent("", errorPathPrefix, effortDocument, lineToRecalculate));
249            if (isValid) {
250                lineToRecalculate.recalculatePayrollAmount(totalPayrollAmount);
251    
252                String groupId = lineToRecalculate.getGroupId();
253                List<EffortCertificationDetail> detailLines = certificationReportForm.getDetailLines();
254                List<EffortCertificationDetail> detailLinesToRecalculate = this.findDetailLinesInGroup(detailLines, groupId);
255                for (EffortCertificationDetail line : detailLinesToRecalculate) {
256                    ObjectUtil.buildObject(line, lineToRecalculate, EffortConstants.DETAIL_LINES_GROUPING_FILEDS);
257                }
258    
259                // rebuild the detail line groups from the detail lines of the current document
260                Map<String, DetailLineGroup> detailLineGroupMap = DetailLineGroup.groupDetailLines(certificationReportForm.getDetailLines());
261                DetailLineGroup detailLineGroup = detailLineGroupMap.get(DetailLineGroup.getKeysAsString(lineToRecalculate));
262                this.updateDetailLineGroup(detailLineGroup, lineToRecalculate, totalPayrollAmount);
263            }
264    
265            return mapping.findForward(KFSConstants.MAPPING_BASIC);
266        }
267    
268        /**
269         * add a new detail line and make a corresponding update on the underlying detail lines
270         */
271        public ActionForward addSummarizedDetailLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
272            ActionForward actionForward = this.add(mapping, form, request, response);
273    
274            if (GlobalVariables.getMessageMap().getErrorCount() >= 0) {
275                CertificationReportForm certificationReportForm = (CertificationReportForm) form;
276                this.refreshDetailLineGroupMap(certificationReportForm);
277            }
278    
279            return actionForward;
280        }
281    
282        /**
283         * delete a detail line from summarized detail lines and make a corresponding update on the underlying detail lines
284         */
285        public ActionForward deleteSummarizedDetailLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
286            CertificationReportForm certificationReportForm = (CertificationReportForm) form;
287    
288            // remove the selected summary line
289            List<EffortCertificationDetail> summarizedDetailLines = certificationReportForm.getSummarizedDetailLines();
290            int lineToRecalculateIndex = this.getSelectedLine(request);
291            EffortCertificationDetail lineToDelete = summarizedDetailLines.get(lineToRecalculateIndex);
292            summarizedDetailLines.remove(lineToDelete);
293    
294            String groupId = lineToDelete.getGroupId();
295            List<EffortCertificationDetail> detailLines = certificationReportForm.getDetailLines();
296            List<EffortCertificationDetail> detailLinesToDelete = this.findDetailLinesInGroup(detailLines, groupId);
297            detailLines.removeAll(detailLinesToDelete);
298    
299            return mapping.findForward(KFSConstants.MAPPING_BASIC);
300        }
301    
302        /**
303         * revert a detail line in summarized detail lines and make a corresponding update on the underlying detail lines
304         */
305        public ActionForward revertSummarizedDetailLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
306            CertificationReportForm certificationReportForm = (CertificationReportForm) form;
307    
308            List<EffortCertificationDetail> summarizedDetailLines = certificationReportForm.getSummarizedDetailLines();
309            int lineToRevertIndex = this.getSelectedLine(request);
310            EffortCertificationDetail lineToRevert = summarizedDetailLines.get(lineToRevertIndex);
311    
312            Map<String, DetailLineGroup> detailLineGroupMap = DetailLineGroup.groupDetailLines(certificationReportForm.getDetailLines());
313            DetailLineGroup detailLineGroup = detailLineGroupMap.get(DetailLineGroup.getKeysAsString(lineToRevert));
314            List<EffortCertificationDetail> detailLinesInGroup = detailLineGroup.getDetailLines();
315    
316            List<EffortCertificationDetail> detailLines = certificationReportForm.getDetailLines();
317            for (EffortCertificationDetail detailLine : detailLinesInGroup) {
318                this.revertDetaiLine(detailLines, detailLine);
319            }
320    
321            this.refreshDetailLineGroupMap(certificationReportForm);
322    
323            return mapping.findForward(KFSConstants.MAPPING_BASIC);
324        }
325    
326        /**
327         * find the detail lines belonging to the given group from the given detail lines
328         * 
329         * @param detailLines the given detail lines
330         * @param groupId the given group id
331         * @return the detail lines belonging to the given group
332         */
333        protected List<EffortCertificationDetail> findDetailLinesInGroup(List<EffortCertificationDetail> detailLines, String groupId) {
334            List<EffortCertificationDetail> detailLinesInGroup = new ArrayList<EffortCertificationDetail>();
335    
336            for (EffortCertificationDetail line : detailLines) {
337                if (StringUtils.equals(groupId, line.getGroupId())) {
338                    detailLinesInGroup.add(line);
339                }
340            }
341    
342            return detailLinesInGroup;
343        }
344    
345        /**
346         * determine whether the summarized detail lines need to be rendered
347         * 
348         * @param certificationReportForm the action form
349         * @return true if the summarized detail lines need to be rendered; otherwise, false
350         */
351        protected boolean isSummarizeDetailLinesRendered(CertificationReportForm certificationReportForm) {
352            populateAuthorizationFields(certificationReportForm);
353            //super.populateAuthorizationFields(certificationReportForm);
354            
355            return certificationReportForm.getEditingMode().containsKey(EffortCertificationEditMode.SUMMARY_TAB_ENTRY);
356        }
357    
358        /**
359         * recalculate all detail lines with the information in summarized detail lines
360         * 
361         * @param certificationReportForm the given action form
362         */
363        protected void recalculateAllDetailLines(CertificationReportForm certificationReportForm) {
364            Map<String, DetailLineGroup> detailLineGroupMap = DetailLineGroup.groupDetailLines(certificationReportForm.getDetailLines());
365    
366            EffortCertificationDocument effortDocument = (EffortCertificationDocument) certificationReportForm.getDocument();
367            KualiDecimal totalPayrollAmount = effortDocument.getTotalOriginalPayrollAmount();
368    
369            List<EffortCertificationDetail> summarizedDetailLines = certificationReportForm.getSummarizedDetailLines();
370            for (EffortCertificationDetail detailLine : summarizedDetailLines) {
371                // recalculate the selected summary line
372                detailLine.recalculatePayrollAmount(totalPayrollAmount);
373    
374                // rebuild the detail line groups from the detail lines of the current document
375                DetailLineGroup detailLineGroup = detailLineGroupMap.get(DetailLineGroup.getKeysAsString(detailLine));
376                this.updateDetailLineGroup(detailLineGroup, detailLine, totalPayrollAmount);
377            }
378        }
379    
380        /**
381         * recalculate all detail lines with the information in summarized detail lines
382         * 
383         * @param certificationReportForm the given action form
384         */
385        protected void updateDetailLinesFromSummaryLines(CertificationReportForm certificationReportForm) {
386            EffortCertificationDocument effortDocument = (EffortCertificationDocument) certificationReportForm.getDocument();
387            List<EffortCertificationDetail> detailLines = certificationReportForm.getDetailLines();
388            List<EffortCertificationDetail> summarizedDetailLines = certificationReportForm.getSummarizedDetailLines();
389    
390            boolean isValid = true;
391            int index = 0;
392            for (EffortCertificationDetail detailLine : summarizedDetailLines) {
393                EffortCertificationDocumentRuleUtil.applyDefaultValues(detailLine);
394    
395                String errorPathPrefix = KFSPropertyConstants.DOCUMENT + "." + EffortPropertyConstants.SUMMARIZED_DETAIL_LINES + "[" + index + "]";
396                isValid &= this.invokeRules(new CheckDetailLineAmountEvent("", errorPathPrefix, effortDocument, detailLine));
397    
398                ++index;
399            }
400    
401            if (!isValid) {
402                return;
403            }
404    
405            for (EffortCertificationDetail detailLine : summarizedDetailLines) {
406                if (!detailLine.isNewLineIndicator()) {
407                    continue;
408                }
409    
410                if (detailLine.isAccountExpiredOverride()) {
411                    detailLine.refreshReferenceObject(KFSPropertyConstants.ACCOUNT);
412                    this.updateDetailLineOverrideCode(detailLine);
413                }
414    
415                List<EffortCertificationDetail> detailLinesToUpdate = this.findDetailLinesInGroup(detailLines, detailLine.getGroupId());
416                for (EffortCertificationDetail line : detailLinesToUpdate) {
417                    ObjectUtil.buildObject(line, detailLine, EffortConstants.DETAIL_LINES_GROUPING_FILEDS);
418    
419                    line.setAccountExpiredOverride(detailLine.isAccountExpiredOverride());
420                    line.setAccountExpiredOverrideNeeded(detailLine.isAccountExpiredOverrideNeeded());
421                    line.setOverrideCode(detailLine.getOverrideCode());
422                }
423            }
424    
425            Map<String, DetailLineGroup> detailLineGroupMap = DetailLineGroup.groupDetailLines(detailLines);
426            KualiDecimal totalPayrollAmount = effortDocument.getTotalOriginalPayrollAmount();
427            for (EffortCertificationDetail detailLine : summarizedDetailLines) {
428                // rebuild the detail line groups from the detail lines of the current document
429                detailLine.setGroupId(DetailLineGroup.getKeysAsString(detailLine));
430                DetailLineGroup detailLineGroup = detailLineGroupMap.get(DetailLineGroup.getKeysAsString(detailLine));
431                this.updateDetailLineGroup(detailLineGroup, detailLine, totalPayrollAmount);
432            }
433    
434            this.processDetailLineOverrides(detailLines);
435            this.processDetailLineOverrides(summarizedDetailLines);
436        }
437    
438        /**
439         * update detail line group with the the information in the given detail line
440         * 
441         * @param detailLineGroup the given detail line group
442         * @param detailLine the given detail line
443         * @param totalPayrollAmount the total payroll amount of the document associating with the detail line group
444         */
445        protected void updateDetailLineGroup(DetailLineGroup detailLineGroup, EffortCertificationDetail detailLine, KualiDecimal totalPayrollAmount) {
446            EffortCertificationDetail summaryLine = detailLineGroup.getSummaryDetailLine();
447            summaryLine.setEffortCertificationUpdatedOverallPercent(detailLine.getEffortCertificationUpdatedOverallPercent());
448            summaryLine.setEffortCertificationPayrollAmount(detailLine.getEffortCertificationPayrollAmount());
449    
450            detailLineGroup.updateDetailLineEffortPercent();
451            detailLineGroup.updateDetailLinePayrollAmount();
452        }
453    
454        /**
455         * Toggles the sort order between ascending and descending. If the current order is ascending, then the sort order will be set
456         * to descending, and vice versa.
457         */
458        protected void toggleSortOrder(CertificationReportForm certificationReportForm) {
459            if (SortOrder.ASC.name().equals(certificationReportForm.getSortOrder())) {
460                certificationReportForm.setSortOrder(SortOrder.DESC.name());
461            }
462            else {
463                certificationReportForm.setSortOrder(SortOrder.ASC.name());
464            }
465        }
466    
467        /**
468         * sort the detail lines based on the values of the sort order and sort column
469         */
470        protected void sortDetailLine(CertificationReportForm certificationReportForm, List<EffortCertificationDetail> detailLines, String... sortColumn) {
471            String sortOrder = certificationReportForm.getSortOrder();
472            DynamicCollectionComparator.sort(detailLines, SortOrder.valueOf(sortOrder), sortColumn);
473        }
474    
475        /**
476         * rebuild the detail line group map from the detail lines of the current document
477         */
478        protected Map<String, DetailLineGroup> refreshDetailLineGroupMap(CertificationReportForm certificationReportForm) {
479            LOG.info("refreshDetailLineGroupMap() started");
480    
481            List<EffortCertificationDetail> summarizedDetailLines = certificationReportForm.getSummarizedDetailLines();
482            if (summarizedDetailLines == null) {
483                EffortCertificationDocument effortCertificationDocument = (EffortCertificationDocument) certificationReportForm.getDocument();
484                effortCertificationDocument.setSummarizedDetailLines(new ArrayList<EffortCertificationDetail>());
485            }
486            summarizedDetailLines.clear();
487    
488            Map<String, DetailLineGroup> detailLineGroupMap = DetailLineGroup.groupDetailLines(certificationReportForm.getDetailLines());
489    
490            for (String key : detailLineGroupMap.keySet()) {
491                EffortCertificationDetail sumaryline = detailLineGroupMap.get(key).getSummaryDetailLine();
492    
493                summarizedDetailLines.add(sumaryline);
494            }
495    
496            return detailLineGroupMap;
497        }
498    
499        /**
500         * find the given detail line from the given collection of detail lines and revert it
501         */
502        protected void revertDetaiLine(List<EffortCertificationDetail> detailLines, EffortCertificationDetail lineToRevert) {
503            int lineToRevertIndex = detailLines.lastIndexOf(lineToRevert);
504    
505            this.revertDetaiLine(detailLines, lineToRevertIndex);
506        }
507    
508        /**
509         * revert the detail line in the specified position
510         */
511        protected void revertDetaiLine(List<EffortCertificationDetail> detailLines, int lineToRevertIndex) {
512            EffortCertificationDetail lineToRevert = detailLines.get(lineToRevertIndex);
513    
514            BusinessObjectService businessObjectService = SpringContext.getBean(BusinessObjectService.class);
515            EffortCertificationDetail revertedLine = (EffortCertificationDetail) businessObjectService.retrieve(lineToRevert);
516            this.resetPersistedFields(revertedLine);
517    
518            detailLines.remove(lineToRevertIndex);
519            detailLines.add(lineToRevertIndex, revertedLine);
520        }
521    
522        /**
523         * reset the persisted fields of the given detail line
524         */
525        protected void resetPersistedFields(EffortCertificationDetail detailLine) {
526            int persistedEffortPercent = detailLine.getEffortCertificationUpdatedOverallPercent();
527            detailLine.setPersistedEffortPercent(new Integer(persistedEffortPercent));
528    
529            BigDecimal persistedPayrollAmount = detailLine.getEffortCertificationPayrollAmount().bigDecimalValue();
530            detailLine.setPersistedPayrollAmount(new KualiDecimal(persistedPayrollAmount));
531        }
532    
533        /**
534         * @see org.kuali.rice.kns.web.struts.action.KualiDocumentActionBase#approve(org.apache.struts.action.ActionMapping,
535         *      org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
536         */
537        @Override
538        public ActionForward approve(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
539            KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form;
540            EffortCertificationDocument effortDocument = (EffortCertificationDocument) kualiDocumentFormBase.getDocument();
541    
542            EffortCertificationDocumentService effortCertificationDocumentService = SpringContext.getBean(EffortCertificationDocumentService.class);        
543            effortCertificationDocumentService.addRouteLooping(effortDocument);
544            
545            ActionForward actionForward = super.approve(mapping, kualiDocumentFormBase, request, response);
546    
547            return actionForward;
548        }
549    
550        /**
551         * @see org.kuali.rice.kns.web.struts.action.KualiDocumentActionBase#insertBONote(org.apache.struts.action.ActionMapping, org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
552         */
553        @Override
554        public ActionForward insertBONote(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
555            KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form;
556            kualiDocumentFormBase.getNewNote().setNewCollectionRecord(true);
557            
558            return super.insertBONote(mapping, form, request, response);
559        }
560    
561        /**
562         * Do one additional check on Use Transactional for Summary Tab
563         * @see org.kuali.rice.kns.web.struts.action.KualiTransactionalDocumentActionBase#populateAuthorizationFields(org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase)
564         */
565        @Override
566        protected void populateAuthorizationFields(KualiDocumentFormBase formBase) {        
567            
568            //call super for authorization fields
569            super.populateAuthorizationFields(formBase);
570    
571            CertificationReportForm certificationReportForm = (CertificationReportForm) formBase;
572            
573            //get document authorizer
574            EffortCertificationDocumentAuthorizer certReportDocAuthorizer = (EffortCertificationDocumentAuthorizer)SpringContext.getBean(DocumentHelperService.class).getDocumentAuthorizer(EffortConstants.EffortDocumentTypes.EFFORT_CERTIFICATION_DOCUMENT);
575    
576            //grab document
577            KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) formBase;
578            EffortCertificationDocument effortDocument = (EffortCertificationDocument) kualiDocumentFormBase.getDocument();                               
579                    
580            //get principal id
581            String currentPrincipalId = GlobalVariables.getUserSession().getPrincipalId();
582            
583            //set additional details for summary tab 
584            Map<String, String> additionalPermissionDetails = new HashMap<String, String>();
585            additionalPermissionDetails.put(KimAttributes.EDIT_MODE, EffortCertificationEditMode.SUMMARY_TAB_ENTRY);
586    
587            //get list of chart/account numbers
588            List<EffortCertificationDetail> summarizedDetailLines = createSummarizedDetailLines(certificationReportForm);
589            
590            //set additional role qualifiers
591            Map<String, String> additionalRoleQualifiers = new HashMap<String, String>();
592            String proposalNumber = getPropsalNumberForProjectDirector(currentPrincipalId, summarizedDetailLines);
593            
594            //set proposal number if found
595            if (StringUtils.isNotBlank(proposalNumber)){
596                additionalRoleQualifiers.put(KFSPropertyConstants.PROPOSAL, proposalNumber);
597                                            
598                //re-check summary permission
599                if (certReportDocAuthorizer.doPermissionExistsByTemplate(
600                        effortDocument,
601                        KNSConstants.KNS_NAMESPACE,
602                        KimConstants.PermissionTemplateNames.USE_TRANSACTIONAL_DOCUMENT,
603                        additionalPermissionDetails)
604                        && !certReportDocAuthorizer.isAuthorizedByTemplate(
605                                effortDocument,
606                                KNSConstants.KNS_NAMESPACE,
607                                KimConstants.PermissionTemplateNames.USE_TRANSACTIONAL_DOCUMENT,
608                                currentPrincipalId, 
609                                additionalPermissionDetails,
610                                additionalRoleQualifiers)) {
611                    
612                    formBase.getEditingMode().put(EffortCertificationEditMode.SUMMARY_TAB_ENTRY, KNSConstants.KUALI_DEFAULT_TRUE_VALUE);
613                }
614            }
615        }
616        
617        /**
618         * Retrieves a proposal number for a project director from a list of awards pulled by chart/account number. 
619         * 
620         * @param projectDirectorId
621         * @return
622         */
623        protected String getPropsalNumberForProjectDirector(String projectDirectorId, List<EffortCertificationDetail> summarizedDetailLines){
624            
625            String proposalNumber = null;
626            String chartOfAccountsCode = null;
627            String accountNumber = null;
628    
629            for (EffortCertificationDetail line : summarizedDetailLines) {
630                proposalNumber = SpringContext.getBean(ContractsAndGrantsModuleService.class).getProposalNumberForAccountAndProjectDirector(line.getChartOfAccountsCode(), line.getAccountNumber(), projectDirectorId);
631                
632                //if found a proposal number, break and return
633                if( StringUtils.isNotEmpty(proposalNumber)) break;
634            }
635                    
636            return proposalNumber;
637        }
638    
639        protected List<EffortCertificationDetail> createSummarizedDetailLines(CertificationReportForm certificationReportForm){
640    
641            List<EffortCertificationDetail> summarizedDetailLines = new ArrayList<EffortCertificationDetail>();
642            Map<String, DetailLineGroup> detailLineGroupMap = null; 
643                
644            summarizedDetailLines = certificationReportForm.getSummarizedDetailLines();
645            
646            if(ObjectUtils.isNull(summarizedDetailLines) || summarizedDetailLines.isEmpty()){
647                if(ObjectUtils.isNotNull(certificationReportForm.getDetailLines()) && !certificationReportForm.getDetailLines().isEmpty()){
648                    summarizedDetailLines = certificationReportForm.getDetailLines();                
649                }
650            }        
651            
652            return summarizedDetailLines;
653    
654        }
655        
656        /**
657         * Transforms the summarized effort detail lines into a chart/account number map
658         * 
659         * @param summaryDetail
660         * @return
661         */
662        protected Map<String,String> convertSummarizedDetailToChartAccountMap(List<EffortCertificationDetail> summaryDetail){
663            
664            Map<String,String> chartAccountMap = new HashMap<String,String>();
665            
666            for(EffortCertificationDetail line: summaryDetail){
667                chartAccountMap.put(line.getChartOfAccountsCode(), line.getAccountNumber());            
668            }
669            
670            return chartAccountMap;
671        }
672    }