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