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 }