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.bc.document.service.impl;
017    
018    import java.math.BigDecimal;
019    import java.util.ArrayList;
020    import java.util.Collection;
021    import java.util.HashMap;
022    import java.util.List;
023    import java.util.Map;
024    
025    import org.apache.commons.lang.StringUtils;
026    import org.kuali.kfs.module.bc.BCConstants;
027    import org.kuali.kfs.module.bc.BCKeyConstants;
028    import org.kuali.kfs.module.bc.businessobject.BudgetConstructionAdministrativePost;
029    import org.kuali.kfs.module.bc.businessobject.BudgetConstructionAppointmentFundingReason;
030    import org.kuali.kfs.module.bc.businessobject.BudgetConstructionAppointmentFundingReasonCode;
031    import org.kuali.kfs.module.bc.businessobject.BudgetConstructionCalculatedSalaryFoundationTracker;
032    import org.kuali.kfs.module.bc.businessobject.BudgetConstructionIntendedIncumbent;
033    import org.kuali.kfs.module.bc.businessobject.BudgetConstructionOrgReasonSummaryReport;
034    import org.kuali.kfs.module.bc.businessobject.BudgetConstructionOrgReasonSummaryReportTotal;
035    import org.kuali.kfs.module.bc.businessobject.BudgetConstructionOrgSalarySummaryReportTotal;
036    import org.kuali.kfs.module.bc.businessobject.BudgetConstructionPosition;
037    import org.kuali.kfs.module.bc.businessobject.BudgetConstructionReportThresholdSettings;
038    import org.kuali.kfs.module.bc.businessobject.BudgetConstructionSalaryFunding;
039    import org.kuali.kfs.module.bc.businessobject.BudgetConstructionSalarySocialSecurityNumber;
040    import org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding;
041    import org.kuali.kfs.module.bc.document.dataaccess.BudgetConstructionSalarySummaryReportDao;
042    import org.kuali.kfs.module.bc.document.service.BudgetConstructionOrganizationReportsService;
043    import org.kuali.kfs.module.bc.document.service.BudgetConstructionReasonSummaryReportService;
044    import org.kuali.kfs.module.bc.document.service.BudgetConstructionReportsServiceHelper;
045    import org.kuali.kfs.module.bc.document.service.impl.BudgetConstructionSalarySummaryReportServiceImpl.OrganizationTotalHolder;
046    import org.kuali.kfs.module.bc.report.BudgetConstructionReportHelper;
047    import org.kuali.kfs.sys.KFSPropertyConstants;
048    import org.kuali.rice.kns.service.BusinessObjectService;
049    import org.kuali.rice.kns.service.KualiConfigurationService;
050    import org.kuali.rice.kns.util.KualiDecimal;
051    import org.kuali.rice.kns.util.KualiInteger;
052    import org.kuali.rice.kns.util.ObjectUtils;
053    import org.springframework.transaction.annotation.Transactional;
054    
055    /**
056     * Service implementation of BudgetConstructionReasonSummaryReportService.
057     */
058    @Transactional
059    public class BudgetConstructionReasonSummaryReportServiceImpl implements BudgetConstructionReasonSummaryReportService {
060        private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(BudgetConstructionReasonSummaryReportServiceImpl.class);
061    
062        BudgetConstructionSalarySummaryReportDao budgetConstructionSalarySummaryReportDao;
063        BudgetConstructionOrganizationReportsService budgetConstructionOrganizationReportsService;
064        private BudgetConstructionReportsServiceHelper budgetConstructionReportsServiceHelper;
065        KualiConfigurationService kualiConfigurationService;
066        BusinessObjectService businessObjectService;
067    
068        /**
069         * @see org.kuali.kfs.module.bc.document.service.BudgetConstructionReasonSummaryReportService#updateReasonSummaryReport(java.lang.String,
070         *      java.lang.Integer, org.kuali.kfs.module.bc.businessobject.BudgetConstructionReportThresholdSettings)
071         */
072        public void updateReasonSummaryReport(String principalId, Integer universityFiscalYear, BudgetConstructionReportThresholdSettings budgetConstructionReportThresholdSettings) {
073            boolean selectOnlyGreaterThanOrEqualToThreshold = budgetConstructionReportThresholdSettings.isUseGreaterThanOperator();
074            KualiDecimal thresholdPercent = budgetConstructionReportThresholdSettings.getThresholdPercent();
075    
076            boolean applyAThreshold = budgetConstructionReportThresholdSettings.isUseThreshold();
077            if (applyAThreshold) {
078                budgetConstructionSalarySummaryReportDao.updateSalaryAndReasonSummaryReportsWithThreshold(principalId, universityFiscalYear - 1, selectOnlyGreaterThanOrEqualToThreshold, thresholdPercent);
079            }
080            else {
081                budgetConstructionSalarySummaryReportDao.updateSalaryAndReasonSummaryReportsWithoutThreshold(principalId, true);
082            }
083        }
084    
085        /**
086         * @see org.kuali.kfs.module.bc.document.service.BudgetConstructionReasonSummaryReportService#buildReports(java.lang.Integer,
087         *      java.lang.String, org.kuali.kfs.module.bc.businessobject.BudgetConstructionReportThresholdSettings)
088         */
089        public Collection<BudgetConstructionOrgReasonSummaryReport> buildReports(Integer universityFiscalYear, String principalId, BudgetConstructionReportThresholdSettings reportThresholdSettings) {
090            Collection<BudgetConstructionOrgReasonSummaryReport> reportSet = new ArrayList<BudgetConstructionOrgReasonSummaryReport>();
091    
092            BudgetConstructionOrgReasonSummaryReport reasonSummaryReport;
093            Collection<BudgetConstructionSalarySocialSecurityNumber> bcSalarySsnList = budgetConstructionReportsServiceHelper.getDataForBuildingReports(BudgetConstructionSalarySocialSecurityNumber.class, principalId, buildOrderByList());
094    
095            Map salaryFundingMap = new HashMap();
096            for (BudgetConstructionSalarySocialSecurityNumber ssnEntry : bcSalarySsnList) {
097                Collection<BudgetConstructionSalaryFunding> salaryFundingList = budgetConstructionReportsServiceHelper.getSalaryFunding(principalId, ssnEntry.getEmplid());
098                salaryFundingMap.put(ssnEntry, salaryFundingList);
099            }
100    
101            List<BudgetConstructionSalarySocialSecurityNumber> listForCalculateTotalPerson = deleteDuplicated((List) bcSalarySsnList, 1);
102            List<BudgetConstructionSalarySocialSecurityNumber> listForCalculateTotalOrg = deleteDuplicated((List) bcSalarySsnList, 2);
103    
104            // Calculate Total Section
105            Collection<BudgetConstructionOrgReasonSummaryReportTotal> reasonSummaryTotalPerson = calculatePersonTotal(universityFiscalYear, bcSalarySsnList, listForCalculateTotalPerson, salaryFundingMap);
106            Collection<BudgetConstructionOrgReasonSummaryReportTotal> reasonSummaryTotalOrg = calculateOrgTotal(reasonSummaryTotalPerson, listForCalculateTotalOrg, salaryFundingMap);
107    
108            // get object codes
109            String objectCodes = budgetConstructionReportsServiceHelper.getSelectedObjectCodes(principalId);
110    
111            // get reason codes
112            String reasonCodes = budgetConstructionReportsServiceHelper.getSelectedReasonCodes(principalId);
113    
114            for (BudgetConstructionSalarySocialSecurityNumber ssnEntry : bcSalarySsnList) {
115                Collection<BudgetConstructionSalaryFunding> salaryFundingList = (Collection) salaryFundingMap.get(ssnEntry);
116                
117                for (BudgetConstructionSalaryFunding salaryFundingEntry : salaryFundingList) {
118                    reasonSummaryReport = new BudgetConstructionOrgReasonSummaryReport();
119                    buildReportsHeader(universityFiscalYear, objectCodes, reasonCodes, reasonSummaryReport, salaryFundingEntry, ssnEntry, reportThresholdSettings);
120                    buildReportsBody(universityFiscalYear, reasonSummaryReport, salaryFundingEntry, ssnEntry);
121                    buildReportsTotal(reasonSummaryReport, ssnEntry, reasonSummaryTotalPerson, reasonSummaryTotalOrg);
122                    reportSet.add(reasonSummaryReport);
123                }
124            }
125    
126            return reportSet;
127        }
128    
129        /**
130         * builds report Header
131         */
132        public void buildReportsHeader(Integer universityFiscalYear, String objectCodes, String reasonCodes, BudgetConstructionOrgReasonSummaryReport reasonSummaryReport, BudgetConstructionSalaryFunding salaryFundingEntry, BudgetConstructionSalarySocialSecurityNumber bcSSN, BudgetConstructionReportThresholdSettings budgetConstructionReportThresholdSettings) {
133            Integer prevFiscalyear = universityFiscalYear - 1;
134            reasonSummaryReport.setFiscalYear(prevFiscalyear + "-" + universityFiscalYear.toString().substring(2, 4));
135    
136            reasonSummaryReport.setOrganizationCode(bcSSN.getOrganizationCode());
137            String organizationName = bcSSN.getOrganization().getOrganizationName();
138            if (organizationName == null) {
139                String wrongOrganizationName = kualiConfigurationService.getPropertyString(BCKeyConstants.ERROR_REPORT_GETTING_ORGANIZATION_NAME);
140                reasonSummaryReport.setOrganizationName(wrongOrganizationName);
141            }
142            else {
143                reasonSummaryReport.setOrganizationName(organizationName);
144            }
145    
146            reasonSummaryReport.setOrgChartOfAccountsCode(bcSSN.getOrganizationChartOfAccountsCode());
147            String chartDescription = bcSSN.getOrganizationChartOfAccounts().getFinChartOfAccountDescription();
148            if (chartDescription == null) {
149                String wrongChartDescription = kualiConfigurationService.getPropertyString(BCKeyConstants.ERROR_REPORT_GETTING_CHART_DESCRIPTION);
150                reasonSummaryReport.setOrgChartOfAccountDescription(wrongChartDescription);
151            }
152            else {
153                reasonSummaryReport.setOrgChartOfAccountDescription(chartDescription);
154            }
155    
156            Integer prevPrevFiscalyear = prevFiscalyear - 1;
157            reasonSummaryReport.setReqFy(prevFiscalyear + "-" + universityFiscalYear.toString().substring(2, 4));
158            reasonSummaryReport.setFinancialObjectCode(salaryFundingEntry.getFinancialObjectCode());
159    
160            reasonSummaryReport.setObjectCodes(objectCodes);
161    
162            if (budgetConstructionReportThresholdSettings.isUseThreshold()) {
163                if (budgetConstructionReportThresholdSettings.isUseGreaterThanOperator()) {
164                    reasonSummaryReport.setThresholdOrReason(BCConstants.Report.THRESHOLD + BCConstants.Report.THRESHOLD_GREATER + budgetConstructionReportThresholdSettings.getThresholdPercent().toString() + BCConstants.Report.PERCENT);
165                }
166                else {
167                    reasonSummaryReport.setThresholdOrReason(BCConstants.Report.THRESHOLD + BCConstants.Report.THRESHOLD_LESS + budgetConstructionReportThresholdSettings.getThresholdPercent().toString() + BCConstants.Report.PERCENT);
168                }
169            }
170            else {
171                reasonSummaryReport.setThresholdOrReason(BCConstants.Report.SELECTED_REASONS + reasonCodes);
172            }
173    
174            // reason, amt, desc
175            List<BudgetConstructionAppointmentFundingReason> appointmentFundingReasonList = salaryFundingEntry.getPendingAppointmentFunding().getBudgetConstructionAppointmentFundingReason();
176            if (ObjectUtils.isNotNull(appointmentFundingReasonList) && !appointmentFundingReasonList.isEmpty()) {
177                BudgetConstructionAppointmentFundingReason appointmentFundingReason = appointmentFundingReasonList.get(0);
178    
179                Integer reasonAmount = BudgetConstructionReportHelper.convertKualiInteger(appointmentFundingReason.getAppointmentFundingReasonAmount());
180                reasonSummaryReport.setAppointmentFundingReasonAmount(reasonAmount);
181    
182                BudgetConstructionAppointmentFundingReasonCode reasonCode = appointmentFundingReason.getAppointmentFundingReason();
183                if (ObjectUtils.isNotNull(reasonCode)) {
184                    reasonSummaryReport.setAppointmentFundingReasonDescription(reasonCode.getAppointmentFundingReasonDescription());
185                }
186            }
187        }
188    
189        /**
190         * builds report body
191         */
192        public void buildReportsBody(Integer fiscalYear, BudgetConstructionOrgReasonSummaryReport reasonSummaryReport, BudgetConstructionSalaryFunding salaryFundingEntry, BudgetConstructionSalarySocialSecurityNumber salarySSN) {
193            int curToInt = -1;
194            double curFteInt = -1.00;
195    
196            PendingBudgetConstructionAppointmentFunding appointmentFunding = salaryFundingEntry.getPendingAppointmentFunding();
197            BudgetConstructionIntendedIncumbent intendedIncumbent = budgetConstructionReportsServiceHelper.getBudgetConstructionIntendedIncumbent(appointmentFunding);
198            if (intendedIncumbent != null) {
199                reasonSummaryReport.setIuClassificationLevel(intendedIncumbent.getIuClassificationLevel());
200            }
201    
202            int nameLength = salarySSN.getName().length();
203            reasonSummaryReport.setName(salarySSN.getName().substring(0, (nameLength > 35) ? 35 : nameLength));
204    
205            BudgetConstructionAdministrativePost administrativePost = budgetConstructionReportsServiceHelper.getBudgetConstructionAdministrativePost(appointmentFunding);
206            BudgetConstructionPosition budgetConstructionPosition = budgetConstructionReportsServiceHelper.getBudgetConstructionPosition(fiscalYear, appointmentFunding);
207    
208            // set report body
209            reasonSummaryReport.setChartOfAccountsCode(salaryFundingEntry.getChartOfAccountsCode());
210            reasonSummaryReport.setAccountNumber(salaryFundingEntry.getAccountNumber());
211            reasonSummaryReport.setSubAccountNumber(salaryFundingEntry.getSubAccountNumber());
212            reasonSummaryReport.setFinancialSubObjectCode(salaryFundingEntry.getFinancialSubObjectCode());
213    
214            if (administrativePost != null) {
215                reasonSummaryReport.setAdministrativePost(administrativePost.getAdministrativePost());
216            }
217    
218            if (budgetConstructionPosition != null) {
219                reasonSummaryReport.setPositionNumber(budgetConstructionPosition.getPositionNumber());
220                reasonSummaryReport.setNormalWorkMonthsAndiuPayMonths(budgetConstructionPosition.getIuNormalWorkMonths() + "/" + budgetConstructionPosition.getIuPayMonths());
221                reasonSummaryReport.setPositionFte(BudgetConstructionReportHelper.setDecimalDigit(budgetConstructionPosition.getPositionFullTimeEquivalency(), 5, false));
222                reasonSummaryReport.setPositionSalaryPlanDefault(budgetConstructionPosition.getPositionSalaryPlanDefault());
223                reasonSummaryReport.setPositionGradeDefault(budgetConstructionPosition.getPositionGradeDefault());
224            }
225    
226            BudgetConstructionCalculatedSalaryFoundationTracker csfTracker = appointmentFunding.getEffectiveCSFTracker();
227            if (ObjectUtils.isNotNull(csfTracker)) {
228                reasonSummaryReport.setCsfTimePercent(BudgetConstructionReportHelper.setDecimalDigit(csfTracker.getCsfTimePercent(), 2, false));
229                reasonSummaryReport.setCsfAmount(csfTracker.getCsfAmount().intValue());
230    
231                // calculate amountChange and percentChange
232                if (appointmentFunding.getAppointmentRequestedFteQuantity().equals(csfTracker.getCsfFullTimeEmploymentQuantity())) {
233                    Integer amountChange = appointmentFunding.getAppointmentRequestedAmount().subtract(csfTracker.getCsfAmount()).intValue();
234                    reasonSummaryReport.setAmountChange(amountChange);
235    
236                    BigDecimal percentChange = BudgetConstructionReportHelper.calculatePercent(new BigDecimal(amountChange), csfTracker.getCsfAmount().bigDecimalValue());
237                    reasonSummaryReport.setPercentChange(percentChange);
238                }
239            }
240    
241            if (StringUtils.equals(appointmentFunding.getFinancialSubObjectCode(), BCConstants.Report.BLANK_SUB_OBJECT_CODE)) {
242                reasonSummaryReport.setFinancialSubObjectCode(BCConstants.Report.BLANK);
243            }
244            else {
245                reasonSummaryReport.setFinancialSubObjectCode(appointmentFunding.getFinancialSubObjectCode());
246            }
247    
248            reasonSummaryReport.setEmplid(appointmentFunding.getEmplid());
249            reasonSummaryReport.setAppointmentFundingDurationCode(appointmentFunding.getAppointmentFundingDurationCode());
250            reasonSummaryReport.setAppointmentTotalIntendedAmount(appointmentFunding.getAppointmentTotalIntendedAmount().intValue());
251    
252            BigDecimal totalIntendedFteQuantity = BudgetConstructionReportHelper.setDecimalDigit(appointmentFunding.getAppointmentTotalIntendedFteQuantity(), 5, false);
253            reasonSummaryReport.setAppointmentTotalIntendedFteQuantity(totalIntendedFteQuantity);
254    
255            if (StringUtils.equals(appointmentFunding.getAppointmentFundingDurationCode(), BCConstants.Report.NONE)) {
256                reasonSummaryReport.setSalaryAmount(appointmentFunding.getAppointmentRequestedAmount().intValue());
257                reasonSummaryReport.setPercentAmount(appointmentFunding.getAppointmentRequestedTimePercent());
258                reasonSummaryReport.setSalaryMonths(appointmentFunding.getAppointmentFundingMonth());
259            }
260            else {
261                reasonSummaryReport.setSalaryAmount(appointmentFunding.getAppointmentRequestedCsfAmount().intValue());
262                reasonSummaryReport.setPercentAmount(appointmentFunding.getAppointmentRequestedCsfTimePercent());
263    
264                if (budgetConstructionPosition != null) {
265                    reasonSummaryReport.setSalaryMonths(budgetConstructionPosition.getIuNormalWorkMonths());
266                }
267            }
268    
269            if (appointmentFunding.isAppointmentFundingDeleteIndicator()) {
270                reasonSummaryReport.setDeleteBox(BCConstants.Report.DELETE_MARK);
271            }
272            else {
273                reasonSummaryReport.setDeleteBox(BCConstants.Report.BLANK);
274            }
275    
276            // set tiFlag
277            if (appointmentFunding.isAppointmentFundingDeleteIndicator()) {
278                if (curToInt == -1) {
279                    curToInt = appointmentFunding.getAppointmentTotalIntendedAmount().intValue();
280                }
281                else if (curToInt != appointmentFunding.getAppointmentTotalIntendedAmount().intValue()) {
282                    reasonSummaryReport.setTiFlag(BCConstants.Report.PLUS);
283                }
284    
285                if (curFteInt == -1.00) {
286                    curFteInt = appointmentFunding.getAppointmentTotalIntendedFteQuantity().doubleValue();
287                }
288                else if (curFteInt != appointmentFunding.getAppointmentTotalIntendedFteQuantity().doubleValue()) {
289                    reasonSummaryReport.setTiFlag(BCConstants.Report.PLUS);
290                }
291            }
292        }
293    
294        /**
295         * build the total sections for the report 
296         */
297        public void buildReportsTotal(BudgetConstructionOrgReasonSummaryReport reasonSummaryReportEntry, BudgetConstructionSalarySocialSecurityNumber ssnEntry, Collection<BudgetConstructionOrgReasonSummaryReportTotal> reasonSummaryTotalPerson, Collection<BudgetConstructionOrgReasonSummaryReportTotal> reasonSummaryTotalOrg) {
298    
299            for (BudgetConstructionOrgReasonSummaryReportTotal totalPersonEntry : reasonSummaryTotalPerson) {
300                if (isSameSsnEntryForTotalPerson(totalPersonEntry.getBudgetConstructionSalarySocialSecurityNumber(), ssnEntry)) {
301                    reasonSummaryReportEntry.setPersonPositionNumber(totalPersonEntry.getPersonPositionNumber());
302                    reasonSummaryReportEntry.setPersonFiscalYearTag(totalPersonEntry.getPersonFiscalYearTag());
303                    reasonSummaryReportEntry.setPersonNormalMonthsAndPayMonths(totalPersonEntry.getPersonCsfNormalMonths().toString() + "/" + totalPersonEntry.getPersonCsfPayMonths().toString());
304                    reasonSummaryReportEntry.setPersonCsfAmount(totalPersonEntry.getPersonCsfAmount());
305                    reasonSummaryReportEntry.setPersonCsfPercent(totalPersonEntry.getPersonCsfPercent());
306                    reasonSummaryReportEntry.setPersonSalaryNormalMonths(totalPersonEntry.getPersonSalaryNormalMonths());
307                    reasonSummaryReportEntry.setPersonSalaryAmount(totalPersonEntry.getPersonSalaryAmount());
308                    reasonSummaryReportEntry.setPersonSalaryPercent(totalPersonEntry.getPersonSalaryPercent());
309                    reasonSummaryReportEntry.setPersonSalaryFte(totalPersonEntry.getPersonSalaryFte());
310                    reasonSummaryReportEntry.setPersonTiFlag(totalPersonEntry.getPersonTiFlag());
311                    reasonSummaryReportEntry.setPersonAmountChange(totalPersonEntry.getPersonAmountChange());
312                    reasonSummaryReportEntry.setPersonPercentChange(totalPersonEntry.getPersonPercentChange());
313                }
314                for (BudgetConstructionOrgReasonSummaryReportTotal totalOrgEntry : reasonSummaryTotalOrg) {
315                    if (isSameSsnEntryForTotalOrg(totalOrgEntry.getBudgetConstructionSalarySocialSecurityNumber(), ssnEntry)) {
316                        reasonSummaryReportEntry.setNewFte(totalOrgEntry.getNewFte());
317                        reasonSummaryReportEntry.setNewTotalAmount(totalOrgEntry.getNewTotalAmount());
318                        reasonSummaryReportEntry.setConTotalBaseAmount(totalOrgEntry.getConTotalBaseAmount());
319                        reasonSummaryReportEntry.setConFte(totalOrgEntry.getConFte());
320                        reasonSummaryReportEntry.setConTotalRequestAmount(totalOrgEntry.getConTotalRequestAmount());
321                        reasonSummaryReportEntry.setNewAverageAmount(totalOrgEntry.getNewAverageAmount());
322                        reasonSummaryReportEntry.setConAverageBaseAmount(totalOrgEntry.getConAverageBaseAmount());
323                        reasonSummaryReportEntry.setConAverageRequestAmount(totalOrgEntry.getConAverageRequestAmount());
324                        reasonSummaryReportEntry.setConAveragechange(totalOrgEntry.getConAveragechange());
325                        reasonSummaryReportEntry.setConPercentChange(totalOrgEntry.getConPercentChange());
326                    }
327                }
328            }
329        }
330    
331        // calculate the totals for the given person
332        protected Collection<BudgetConstructionOrgReasonSummaryReportTotal> calculatePersonTotal(Integer universityFiscalYear, Collection<BudgetConstructionSalarySocialSecurityNumber> bcSalarySsnList, List<BudgetConstructionSalarySocialSecurityNumber> listForCalculateTotalPerson, Map salaryFundingMap) {
333            Collection<BudgetConstructionOrgReasonSummaryReportTotal> returnCollection = new ArrayList<BudgetConstructionOrgReasonSummaryReportTotal>();
334    
335            for (BudgetConstructionSalarySocialSecurityNumber personEntry : listForCalculateTotalPerson) {
336                PersonTotalHolder totalsHolder = new PersonTotalHolder();
337                totalsHolder.emplid = personEntry.getEmplid();
338    
339                for (BudgetConstructionSalarySocialSecurityNumber salaryFundingEntry : bcSalarySsnList) {
340                    if (isSameSsnEntryForTotalPerson(personEntry, salaryFundingEntry)) {
341                        Collection<BudgetConstructionSalaryFunding> salaryFundings = (Collection<BudgetConstructionSalaryFunding>) salaryFundingMap.get(personEntry);
342                        this.collectPersonTotal(universityFiscalYear, salaryFundings, totalsHolder);
343                    }
344                }
345    
346                this.adjustPersonTotal(totalsHolder);
347                returnCollection.add(this.createReportTotal(personEntry, totalsHolder));
348            }
349    
350            return returnCollection;
351        }
352    
353        // adjust the total amount that just is held by the given holder
354        protected void adjustPersonTotal(PersonTotalHolder totalsHolder) {
355            Integer restatementCsfAmount = 0;
356            if (totalsHolder.salaryPayMonth == 0 || totalsHolder.csfPayMonths == 0 || BigDecimal.ZERO.compareTo(totalsHolder.csfPercent) == 0 || totalsHolder.csfNormalMonths == 0) {
357                restatementCsfAmount = 0;
358            }
359            else {
360                BigDecimal salaryMonthPercent = new BigDecimal(totalsHolder.salaryNormalMonths * 1.0 / totalsHolder.salaryPayMonth);
361                BigDecimal salaryFteQuantity = totalsHolder.salaryPercent.multiply(salaryMonthPercent);
362                
363                BigDecimal csfMonthpercent = new BigDecimal(totalsHolder.csfNormalMonths * 1.0 / totalsHolder.csfPayMonths);
364                BigDecimal csfFteQuantity = totalsHolder.csfPercent.multiply(csfMonthpercent);
365    
366                BigDecimal restatementCsfPercent = salaryFteQuantity.divide(csfFteQuantity);
367                BigDecimal csfAmount = new BigDecimal(totalsHolder.csfAmount);
368                restatementCsfAmount = csfAmount.multiply(restatementCsfPercent).intValue();
369            }
370    
371            if (totalsHolder.salaryPayMonth == 0) {
372                totalsHolder.salaryFte = BigDecimal.ZERO;
373            }
374            else {
375                BigDecimal salaryFte = totalsHolder.salaryPercent.multiply(new BigDecimal(totalsHolder.salaryNormalMonths * 1.0 / (totalsHolder.salaryPayMonth * 100.0)));
376                totalsHolder.salaryFte = BudgetConstructionReportHelper.setDecimalDigit(salaryFte, 5, false);
377            }
378    
379            if (totalsHolder.salaryPayMonth != totalsHolder.csfPayMonths) {
380                if (totalsHolder.csfPayMonths == 0) {
381                    restatementCsfAmount = 0;
382                }
383                else {
384                    BigDecimal amount = new BigDecimal(restatementCsfAmount * totalsHolder.salaryPayMonth * 1.0 / totalsHolder.csfPayMonths);
385                    restatementCsfAmount = BudgetConstructionReportHelper.setDecimalDigit(amount, 0, false).intValue();
386                }
387            }
388            
389            totalsHolder.csfAmount = restatementCsfAmount;
390            totalsHolder.amountChange = totalsHolder.salaryAmount - totalsHolder.csfAmount;
391    
392            if (totalsHolder.csfAmount != 0) {
393                totalsHolder.percentChange = BudgetConstructionReportHelper.calculatePercent(totalsHolder.amountChange, totalsHolder.csfAmount);
394            }
395            else {
396                totalsHolder.percentChange = BigDecimal.ZERO;
397            }
398    
399            if (totalsHolder.curToInt != 0 && totalsHolder.curToInt != -1 && totalsHolder.curToInt != totalsHolder.salaryAmount.intValue() || totalsHolder.curFteInt != 0 && totalsHolder.curFteInt != -1.00 && totalsHolder.curFteInt != totalsHolder.salaryFte.doubleValue()) {
400                totalsHolder.tiFlag = BCConstants.Report.PLUS;
401            }
402            else {
403                totalsHolder.tiFlag = BCConstants.Report.BLANK;
404            }
405        }
406    
407        // collect the total amounts for a single person and save the totals in the given holder
408        protected void collectPersonTotal(Integer universityFiscalYear, Collection<BudgetConstructionSalaryFunding> salaryFundings, PersonTotalHolder totalsHolder) {
409            int maxSalaryAmount = 0;
410            int maxCsfAmount = 0;
411    
412            for (BudgetConstructionSalaryFunding salaryFunding : salaryFundings) {
413                PendingBudgetConstructionAppointmentFunding appointmentFunding = salaryFunding.getPendingAppointmentFunding();
414                BudgetConstructionPosition budgetConstructionPosition = budgetConstructionReportsServiceHelper.getBudgetConstructionPosition(universityFiscalYear, appointmentFunding);
415                
416                int salaryAmount = 0;
417                BigDecimal salaryPercent = BigDecimal.ZERO;
418                String durationCode = appointmentFunding.getAppointmentFundingDurationCode();
419                
420                if (StringUtils.equals(durationCode, BCConstants.Report.NONE)) {
421                    salaryAmount = appointmentFunding.getAppointmentRequestedAmount().intValue();
422                    totalsHolder.salaryNormalMonths = appointmentFunding.getAppointmentFundingMonth();
423                    salaryPercent = appointmentFunding.getAppointmentRequestedTimePercent();
424                }
425                else {
426                    salaryAmount = appointmentFunding.getAppointmentRequestedCsfAmount().intValue();
427                    totalsHolder.salaryNormalMonths = budgetConstructionPosition.getIuNormalWorkMonths();
428                    
429                    boolean hasRequestedCsfTimePercent = appointmentFunding.getAppointmentRequestedCsfTimePercent() != null;
430                    salaryPercent = hasRequestedCsfTimePercent ? appointmentFunding.getAppointmentRequestedCsfTimePercent() : BigDecimal.ZERO;
431                }
432    
433                if (salaryAmount > maxSalaryAmount) {
434                    maxSalaryAmount = totalsHolder.salaryAmount;
435                    totalsHolder.salaryPayMonth = budgetConstructionPosition.getIuPayMonths();
436                    totalsHolder.salaryNormalMonths = appointmentFunding.getAppointmentFundingMonth();
437                }
438                
439                totalsHolder.salaryAmount += salaryAmount;
440                totalsHolder.salaryPercent = totalsHolder.salaryPercent.add(salaryPercent);
441    
442                BudgetConstructionCalculatedSalaryFoundationTracker csfTracker = appointmentFunding.getEffectiveCSFTracker();
443                if (csfTracker == null) {
444                    continue;
445                }
446    
447                KualiInteger effectiveCsfAmount = csfTracker.getCsfAmount();
448                if (effectiveCsfAmount == null || effectiveCsfAmount.isZero()) {
449                    continue;
450                }
451    
452                if (effectiveCsfAmount.intValue() > maxCsfAmount) {
453                    maxCsfAmount = effectiveCsfAmount.intValue();
454                }
455    
456                totalsHolder.csfAmount += effectiveCsfAmount.intValue();
457                totalsHolder.csfPercent = totalsHolder.csfPercent.add(csfTracker.getCsfTimePercent());
458    
459                // data for previous year, position table has two data, one is for current year and another is for previous year.
460                Integer previousFiscalYear = universityFiscalYear - 1;
461                BudgetConstructionPosition previousYearBudgetConstructionPosition = budgetConstructionReportsServiceHelper.getBudgetConstructionPosition(previousFiscalYear, appointmentFunding);
462                
463                totalsHolder.csfPayMonths = previousYearBudgetConstructionPosition.getIuPayMonths();
464                totalsHolder.csfNormalMonths = previousYearBudgetConstructionPosition.getIuNormalWorkMonths();
465                
466                totalsHolder.positionNumber = budgetConstructionPosition.getPositionNumber();
467                totalsHolder.fiscalYearTag = previousFiscalYear.toString() + ":";
468    
469                if (!appointmentFunding.isAppointmentFundingDeleteIndicator()) {
470                    if (totalsHolder.curToInt <= -1) {
471                        totalsHolder.curToInt = appointmentFunding.getAppointmentTotalIntendedAmount().intValue();
472                    }
473    
474                    if (totalsHolder.curFteInt <= -1.00) {
475                        totalsHolder.curFteInt = appointmentFunding.getAppointmentTotalIntendedFteQuantity().doubleValue();
476                    }
477                }
478            }
479        }
480    
481        // calculate the totals for the given organization
482        protected Collection<BudgetConstructionOrgReasonSummaryReportTotal> calculateOrgTotal(Collection<BudgetConstructionOrgReasonSummaryReportTotal> reasonSummaryTotalPerson, List<BudgetConstructionSalarySocialSecurityNumber> listForCalculateTotalOrg, Map salaryFundingMap) {
483            Collection<BudgetConstructionOrgReasonSummaryReportTotal> returnCollection = new ArrayList<BudgetConstructionOrgReasonSummaryReportTotal>();
484    
485            for (BudgetConstructionSalarySocialSecurityNumber totalOrgEntry : listForCalculateTotalOrg) {
486                OrganizationTotalHolder totalsHolder = new OrganizationTotalHolder();
487    
488                for (BudgetConstructionOrgReasonSummaryReportTotal reportTotalPersonEntry : reasonSummaryTotalPerson) {
489                    if (isSameSsnEntryForTotalOrg(totalOrgEntry, reportTotalPersonEntry.getBudgetConstructionSalarySocialSecurityNumber())) {
490                        if (reportTotalPersonEntry.getPersonCsfAmount() == 0) {
491                            totalsHolder.newFte = totalsHolder.newFte.add(reportTotalPersonEntry.getPersonSalaryFte());
492                            totalsHolder.newTotalAmount += reportTotalPersonEntry.getPersonSalaryAmount();
493                        }
494                        else {
495                            totalsHolder.conTotalBaseAmount += reportTotalPersonEntry.getPersonCsfAmount();
496                            totalsHolder.conFte = totalsHolder.conFte.add(reportTotalPersonEntry.getPersonSalaryFte());
497                            totalsHolder.conTotalRequestAmount += reportTotalPersonEntry.getPersonSalaryAmount();
498                        }
499                    }
500                }
501    
502                // calculate average and change
503                if (BigDecimal.ZERO.compareTo(totalsHolder.newFte) != 0) {
504                    BigDecimal averageAmount = BudgetConstructionReportHelper.calculateDivide(new BigDecimal(totalsHolder.newTotalAmount), totalsHolder.newFte);
505                    totalsHolder.newAverageAmount = BudgetConstructionReportHelper.setDecimalDigit(averageAmount, 0, false).intValue();
506                }
507    
508                if (BigDecimal.ZERO.compareTo(totalsHolder.conFte) != 0) {
509                    BigDecimal averageAmount = BudgetConstructionReportHelper.calculateDivide(new BigDecimal(totalsHolder.conTotalBaseAmount), totalsHolder.conFte);
510                    totalsHolder.conAverageBaseAmount = BudgetConstructionReportHelper.setDecimalDigit(averageAmount, 0, false).intValue();
511    
512                    BigDecimal averageRequestAmount = BudgetConstructionReportHelper.calculateDivide(new BigDecimal(totalsHolder.conTotalRequestAmount), totalsHolder.conFte);
513                    totalsHolder.conAverageRequestAmount = BudgetConstructionReportHelper.setDecimalDigit(averageRequestAmount, 0, false).intValue();
514                }
515    
516                totalsHolder.conAveragechange = totalsHolder.conAverageRequestAmount - totalsHolder.conAverageBaseAmount;
517    
518                if (totalsHolder.conAverageBaseAmount != 0) {
519                    totalsHolder.conPercentChange = BudgetConstructionReportHelper.calculatePercent(totalsHolder.conAveragechange, totalsHolder.conAverageBaseAmount);
520                }
521    
522                returnCollection.add(this.createReportTotal(totalOrgEntry, totalsHolder));
523            }
524    
525            return returnCollection;
526        }
527    
528        // create a report total for the given organization with the values in the given total holder
529        protected BudgetConstructionOrgReasonSummaryReportTotal createReportTotal(BudgetConstructionSalarySocialSecurityNumber totalOrgEntry, OrganizationTotalHolder totalsHolder) {
530            BudgetConstructionOrgReasonSummaryReportTotal reportTotal = new BudgetConstructionOrgReasonSummaryReportTotal();
531    
532            reportTotal.setBudgetConstructionSalarySocialSecurityNumber(totalOrgEntry);
533            reportTotal.setNewFte(totalsHolder.newFte);
534            reportTotal.setNewTotalAmount(totalsHolder.newTotalAmount);
535            reportTotal.setConTotalBaseAmount(totalsHolder.conTotalBaseAmount);
536            reportTotal.setConFte(totalsHolder.conFte);
537            reportTotal.setConTotalRequestAmount(totalsHolder.conTotalRequestAmount);
538            reportTotal.setNewAverageAmount(totalsHolder.newAverageAmount);
539            reportTotal.setConAverageBaseAmount(totalsHolder.conAverageBaseAmount);
540            reportTotal.setConAverageRequestAmount(totalsHolder.conAverageRequestAmount);
541            reportTotal.setConAveragechange(totalsHolder.conAveragechange);
542            reportTotal.setConPercentChange(totalsHolder.conPercentChange);
543    
544            return reportTotal;
545        }
546    
547    
548        // create a report total for the given person with the values in the given total holder
549        protected BudgetConstructionOrgReasonSummaryReportTotal createReportTotal(BudgetConstructionSalarySocialSecurityNumber totalPersonEntry, PersonTotalHolder totalsHolder) {
550            BudgetConstructionOrgReasonSummaryReportTotal reportTotal = new BudgetConstructionOrgReasonSummaryReportTotal();
551    
552            reportTotal.setBudgetConstructionSalarySocialSecurityNumber(totalPersonEntry);
553            reportTotal.setPersonPositionNumber(totalsHolder.positionNumber);
554            reportTotal.setPersonFiscalYearTag(totalsHolder.fiscalYearTag);
555            reportTotal.setPersonCsfNormalMonths(totalsHolder.csfNormalMonths);
556            reportTotal.setPersonCsfPayMonths(totalsHolder.csfPayMonths);
557            reportTotal.setPersonCsfAmount(totalsHolder.csfAmount);
558            reportTotal.setPersonCsfPercent(totalsHolder.csfPercent);
559            reportTotal.setPersonSalaryNormalMonths(totalsHolder.salaryNormalMonths);
560            reportTotal.setPersonSalaryAmount(totalsHolder.salaryAmount);
561            reportTotal.setPersonSalaryPercent(totalsHolder.salaryPercent);
562            reportTotal.setPersonSalaryFte(BudgetConstructionReportHelper.setDecimalDigit(totalsHolder.salaryFte, 5, false));
563            reportTotal.setPersonTiFlag(totalsHolder.tiFlag);
564            reportTotal.setPersonAmountChange(totalsHolder.amountChange);
565            reportTotal.setPersonPercentChange(totalsHolder.percentChange);
566    
567            return reportTotal;
568        }
569    
570    
571        // a total holder that contains the totals for a single person
572        protected class PersonTotalHolder {
573            String emplid = StringUtils.EMPTY;
574            String positionNumber = StringUtils.EMPTY;
575            String fiscalYearTag = StringUtils.EMPTY;
576            String tiFlag = StringUtils.EMPTY;
577    
578            Integer csfNormalMonths = 0;
579            Integer csfPayMonths = 0;
580            Integer csfAmount = 0;
581            BigDecimal csfPercent = BigDecimal.ZERO;
582    
583            Integer salaryNormalMonths = 0;
584            Integer salaryPayMonth = 0;
585            Integer salaryAmount = 0;
586            BigDecimal salaryPercent = BigDecimal.ZERO;
587            BigDecimal salaryFte = BigDecimal.ZERO;
588    
589            Integer amountChange = 0;
590            BigDecimal percentChange = BigDecimal.ZERO;
591    
592            int curToInt = -1;
593            double curFteInt = -1.00;
594        }
595    
596        // a total holder that contains the totals for an organization
597        protected class OrganizationTotalHolder {
598            BigDecimal newFte = BigDecimal.ZERO;
599            Integer newTotalAmount = 0;
600            Integer newAverageAmount = 0;
601            BigDecimal conFte = BigDecimal.ZERO;
602            Integer conTotalBaseAmount = 0;
603            Integer conTotalRequestAmount = 0;
604            Integer conAverageBaseAmount = 0;
605            Integer conAverageRequestAmount = 0;
606            Integer conAveragechange = 0;
607            BigDecimal conPercentChange = BigDecimal.ZERO;
608        }
609    
610        /**
611         * builds orderByList for sort order.
612         * 
613         * @return returnList
614         */
615        public List<String> buildOrderByList() {
616            List<String> returnList = new ArrayList<String>();
617            returnList.add(KFSPropertyConstants.ORGANIZATION_CHART_OF_ACCOUNTS_CODE);
618            returnList.add(KFSPropertyConstants.ORGANIZATION_CODE);
619            returnList.add(KFSPropertyConstants.PERSON_NAME);
620            returnList.add(KFSPropertyConstants.EMPLID);
621    
622            return returnList;
623        }
624    
625        /**
626         * Deletes duplicated entry from list
627         * 
628         * @param List list
629         * @return a list that all duplicated entries were deleted
630         */
631        protected List deleteDuplicated(List list, int mode) {
632            // mode 1 is for getting a list of total object
633            // mode 2 is for getting a list of total account
634            int count = 0;
635            BudgetConstructionSalarySocialSecurityNumber ssnEntry = null;
636            BudgetConstructionSalarySocialSecurityNumber ssnEntryAux = null;
637            List returnList = new ArrayList();
638            if ((list != null) && (list.size() > 0)) {
639                ssnEntry = (BudgetConstructionSalarySocialSecurityNumber) list.get(count);
640                ssnEntryAux = (BudgetConstructionSalarySocialSecurityNumber) list.get(count);
641                returnList.add(ssnEntry);
642                count++;
643                while (count < list.size()) {
644                    ssnEntry = (BudgetConstructionSalarySocialSecurityNumber) list.get(count);
645                    switch (mode) {
646                        case 1: {
647                            if (!isSameSsnEntryForTotalPerson(ssnEntry, ssnEntryAux)) {
648                                returnList.add(ssnEntry);
649                                ssnEntryAux = ssnEntry;
650                            }
651                        }
652                        case 2: {
653                            if (!isSameSsnEntryForTotalOrg(ssnEntry, ssnEntryAux)) {
654                                returnList.add(ssnEntry);
655                                ssnEntryAux = ssnEntry;
656                            }
657                        }
658                    }
659                    count++;
660                }
661            }
662            return returnList;
663        }
664    
665        protected boolean isSameSsnEntryForTotalPerson(BudgetConstructionSalarySocialSecurityNumber firstSsn, BudgetConstructionSalarySocialSecurityNumber secondSsn) {
666            if (firstSsn.getOrganizationChartOfAccountsCode().equals(secondSsn.getOrganizationChartOfAccountsCode()) && firstSsn.getOrganizationCode().equals(secondSsn.getOrganizationCode()) && firstSsn.getEmplid().equals(secondSsn.getEmplid())) {
667                return true;
668            }
669            else
670                return false;
671        }
672    
673        protected boolean isSameSsnEntryForTotalOrg(BudgetConstructionSalarySocialSecurityNumber firstSsn, BudgetConstructionSalarySocialSecurityNumber secondSsn) {
674            if (firstSsn.getOrganizationChartOfAccountsCode().equals(secondSsn.getOrganizationChartOfAccountsCode()) && firstSsn.getOrganizationCode().equals(secondSsn.getOrganizationCode())) {
675                return true;
676            }
677            else
678                return false;
679        }
680    
681        /**
682         * Sets the budgetConstructionSalarySummaryReportDao attribute value.
683         * 
684         * @param budgetConstructionSalarySummaryReportDao The budgetConstructionSalarySummaryReportDao to set.
685         */
686        public void setBudgetConstructionSalarySummaryReportDao(BudgetConstructionSalarySummaryReportDao budgetConstructionSalarySummaryReportDao) {
687            this.budgetConstructionSalarySummaryReportDao = budgetConstructionSalarySummaryReportDao;
688        }
689    
690        /**
691         * Sets the budgetConstructionOrganizationReportsService attribute value.
692         * 
693         * @param budgetConstructionOrganizationReportsService The budgetConstructionOrganizationReportsService to set.
694         */
695        public void setBudgetConstructionOrganizationReportsService(BudgetConstructionOrganizationReportsService budgetConstructionOrganizationReportsService) {
696            this.budgetConstructionOrganizationReportsService = budgetConstructionOrganizationReportsService;
697        }
698    
699        /**
700         * Sets the budgetConstructionReportsServiceHelper attribute value.
701         * 
702         * @param budgetConstructionReportsServiceHelper The budgetConstructionReportsServiceHelper to set.
703         */
704        public void setBudgetConstructionReportsServiceHelper(BudgetConstructionReportsServiceHelper budgetConstructionReportsServiceHelper) {
705            this.budgetConstructionReportsServiceHelper = budgetConstructionReportsServiceHelper;
706        }
707    
708        /**
709         * Sets the kualiConfigurationService attribute value.
710         * 
711         * @param kualiConfigurationService The kualiConfigurationService to set.
712         */
713        public void setKualiConfigurationService(KualiConfigurationService kualiConfigurationService) {
714            this.kualiConfigurationService = kualiConfigurationService;
715        }
716    
717        /**
718         * Sets the businessObjectService attribute value.
719         * 
720         * @param businessObjectService The businessObjectService to set.
721         */
722        public void setBusinessObjectService(BusinessObjectService businessObjectService) {
723            this.businessObjectService = businessObjectService;
724        }
725    }