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 }