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 static org.kuali.kfs.module.bc.BCConstants.AppointmentFundingDurationCodes.NONE;
019
020 import java.math.BigDecimal;
021 import java.util.ArrayList;
022 import java.util.HashMap;
023 import java.util.HashSet;
024 import java.util.List;
025 import java.util.Map;
026 import java.util.Set;
027
028 import org.apache.commons.lang.StringUtils;
029 import org.kuali.kfs.integration.ld.LaborLedgerObject;
030 import org.kuali.kfs.integration.ld.LaborModuleService;
031 import org.kuali.kfs.module.bc.BCConstants;
032 import org.kuali.kfs.module.bc.businessobject.BudgetConstructionAppointmentFundingReason;
033 import org.kuali.kfs.module.bc.businessobject.BudgetConstructionAppointmentFundingReasonCode;
034 import org.kuali.kfs.module.bc.businessobject.BudgetConstructionCalculatedSalaryFoundationTracker;
035 import org.kuali.kfs.module.bc.businessobject.BudgetConstructionHeader;
036 import org.kuali.kfs.module.bc.businessobject.BudgetConstructionPosition;
037 import org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding;
038 import org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionGeneralLedger;
039 import org.kuali.kfs.module.bc.businessobject.SalarySettingExpansion;
040 import org.kuali.kfs.module.bc.document.BudgetConstructionDocument;
041 import org.kuali.kfs.module.bc.document.service.BenefitsCalculationService;
042 import org.kuali.kfs.module.bc.document.service.BudgetConstructionProcessorService;
043 import org.kuali.kfs.module.bc.document.service.BudgetDocumentService;
044 import org.kuali.kfs.module.bc.document.service.LockService;
045 import org.kuali.kfs.module.bc.document.service.SalarySettingService;
046 import org.kuali.kfs.module.bc.util.BudgetParameterFinder;
047 import org.kuali.kfs.module.bc.util.SalarySettingCalculator;
048 import org.kuali.kfs.module.bc.util.SalarySettingFieldsHolder;
049 import org.kuali.kfs.sys.KFSConstants;
050 import org.kuali.kfs.sys.KFSPropertyConstants;
051 import org.kuali.kfs.sys.ObjectUtil;
052 import org.kuali.kfs.sys.context.SpringContext;
053 import org.kuali.kfs.sys.service.OptionsService;
054 import org.kuali.rice.kew.exception.WorkflowException;
055 import org.kuali.rice.kim.bo.Person;
056 import org.kuali.rice.kns.document.authorization.TransactionalDocumentAuthorizer;
057 import org.kuali.rice.kns.service.BusinessObjectService;
058 import org.kuali.rice.kns.service.DocumentHelperService;
059 import org.kuali.rice.kns.service.DocumentService;
060 import org.kuali.rice.kns.service.KualiConfigurationService;
061 import org.kuali.rice.kns.util.GlobalVariables;
062 import org.kuali.rice.kns.util.KualiDecimal;
063 import org.kuali.rice.kns.util.KualiInteger;
064 import org.kuali.rice.kns.util.ObjectUtils;
065 import org.springframework.transaction.annotation.Transactional;
066
067 /**
068 * implements the service methods defined in the SalarySettingService
069 *
070 * @see org.kuali.kfs.module.bc.document.service.SalarySettingService
071 */
072 @Transactional
073 public class SalarySettingServiceImpl implements SalarySettingService {
074 public static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(SalarySettingServiceImpl.class);
075
076 private KualiConfigurationService kualiConfigurationService;
077 private BusinessObjectService businessObjectService;
078 private LaborModuleService laborModuleService;
079 private BudgetDocumentService budgetDocumentService;
080 private BenefitsCalculationService benefitsCalculationService;
081 private OptionsService optionsService;
082 private LockService lockService;
083 private DocumentHelperService documentHelperService;
084 private DocumentService documentService;
085 private BudgetConstructionProcessorService budgetConstructionProcessorService;
086
087 /**
088 * for now just return false, implement application parameter if decision is made implement this functionality
089 *
090 * @see org.kuali.kfs.module.bc.document.service.SalarySettingService#isSalarySettingDisabled()
091 */
092 public boolean isSalarySettingDisabled() {
093 return false;
094 }
095
096 /**
097 * @see org.kuali.kfs.module.bc.document.service.SalarySettingService#calculateHourlyPayRate(org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding)
098 */
099 public BigDecimal calculateHourlyPayRate(PendingBudgetConstructionAppointmentFunding appointmentFunding) {
100 LOG.debug("calculateHourlyPayRate() start");
101
102 KualiInteger requestedAmount = appointmentFunding.getAppointmentRequestedAmount();
103 BigDecimal fteQuantity = this.calculateFteQuantityFromAppointmentFunding(appointmentFunding);
104
105 BigDecimal annualWorkingHours = BigDecimal.valueOf(BudgetParameterFinder.getAnnualWorkingHours());
106 BigDecimal totalPayHoursForYear = fteQuantity.multiply(annualWorkingHours);
107 BigDecimal hourlyPayRate = BigDecimal.ZERO;
108 if (totalPayHoursForYear.compareTo(BigDecimal.ZERO) != 0) {
109 hourlyPayRate = requestedAmount.divide(totalPayHoursForYear).setScale(2, BigDecimal.ROUND_HALF_UP);
110 }
111
112 return hourlyPayRate;
113 }
114
115 /**
116 * @see org.kuali.kfs.module.bc.document.service.SalarySettingService#calculateAnnualPayAmount(org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding)
117 */
118 public KualiInteger calculateAnnualPayAmount(PendingBudgetConstructionAppointmentFunding appointmentFunding) {
119 LOG.debug("calculateAnnualPayAmount() start");
120
121 BigDecimal hourlyPayRate = appointmentFunding.getAppointmentRequestedPayRate();
122 BigDecimal fteQuantity = this.calculateFteQuantityFromAppointmentFunding(appointmentFunding);
123 BigDecimal annualWorkingHours = BigDecimal.valueOf(BudgetParameterFinder.getAnnualWorkingHours());
124 BigDecimal totalPayHoursForYear = fteQuantity.multiply(annualWorkingHours);
125 KualiInteger annualPayAmount = new KualiInteger(hourlyPayRate.multiply(totalPayHoursForYear));
126
127 return annualPayAmount;
128 }
129
130 /**
131 * @see org.kuali.kfs.module.bc.document.service.SalarySettingService#normalizePayRateAndAmount(org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding)
132 */
133 public void normalizePayRateAndAmount(PendingBudgetConstructionAppointmentFunding appointmentFunding) {
134 LOG.debug("normalizePayRateAndAmount() start");
135
136 BigDecimal currentHourlyPayRate = appointmentFunding.getAppointmentRequestedPayRate();
137 if (currentHourlyPayRate != null && !currentHourlyPayRate.equals(BigDecimal.ZERO)) {
138 KualiInteger annualPayAmount = this.calculateAnnualPayAmount(appointmentFunding);
139 appointmentFunding.setAppointmentRequestedAmount(annualPayAmount);
140 } else {
141
142 KualiInteger currentAnnualPayAmount = appointmentFunding.getAppointmentRequestedAmount();
143 if (currentAnnualPayAmount != null && currentAnnualPayAmount.isNonZero()) {
144 BigDecimal hourlyPayRate = this.calculateHourlyPayRate(appointmentFunding);
145 appointmentFunding.setAppointmentRequestedPayRate(hourlyPayRate);
146 }
147
148 currentHourlyPayRate = appointmentFunding.getAppointmentRequestedPayRate();
149 if (currentHourlyPayRate != null) {
150 KualiInteger annualPayAmount = this.calculateAnnualPayAmount(appointmentFunding);
151 appointmentFunding.setAppointmentRequestedAmount(annualPayAmount);
152 }
153 }
154 }
155
156 /**
157 * @see org.kuali.kfs.module.bc.document.service.SalarySettingService#calculateFteQuantityForAppointmentFunding(org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding)
158 */
159 public BigDecimal calculateFteQuantityFromAppointmentFunding(PendingBudgetConstructionAppointmentFunding appointmentFunding) {
160 LOG.debug("calculateFteQuantity() start");
161
162 // appointmentFunding.refreshReferenceObject(BCPropertyConstants.BUDGET_CONSTRUCTION_POSITION);
163 BudgetConstructionPosition position = appointmentFunding.getBudgetConstructionPosition();
164 if (ObjectUtils.isNull(position)) {
165 return BigDecimal.ZERO;
166 }
167
168 Integer payMonth = position.getIuPayMonths();
169 Integer fundingMonth = appointmentFunding.getAppointmentFundingMonth();
170 BigDecimal requestedTimePercent = appointmentFunding.getAppointmentRequestedTimePercent();
171
172 return this.calculateFteQuantity(payMonth, fundingMonth, requestedTimePercent);
173 }
174
175 /**
176 * @see org.kuali.kfs.module.bc.document.service.SalarySettingService#calculateFteQuantity(java.lang.Integer, java.lang.Integer,
177 * java.math.BigDecimal)
178 */
179 public BigDecimal calculateFteQuantity(Integer payMonth, Integer fundingMonth, BigDecimal requestedTimePercent) {
180 LOG.debug("calculateFteQuantity() start");
181
182 if (payMonth == null || fundingMonth == null || requestedTimePercent == null) {
183 return BigDecimal.ZERO;
184 }
185
186 BigDecimal payMonthAsDecimal = BigDecimal.valueOf(payMonth);
187 BigDecimal fundingMonthAsDecimal = BigDecimal.valueOf(fundingMonth);
188 BigDecimal fundingMonthPercent = fundingMonthAsDecimal.divide(payMonthAsDecimal, 5, BigDecimal.ROUND_HALF_UP);
189
190 BigDecimal fteQuantity = requestedTimePercent.multiply(fundingMonthPercent).divide(KFSConstants.ONE_HUNDRED.bigDecimalValue());
191
192 return fteQuantity.setScale(5, BigDecimal.ROUND_HALF_UP);
193 }
194
195 /**
196 * @see org.kuali.kfs.module.bc.document.service.SalarySettingService#calculateCSFFteQuantityFromAppointmentFunding(org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding)
197 */
198 public BigDecimal calculateCSFFteQuantityFromAppointmentFunding(PendingBudgetConstructionAppointmentFunding appointmentFunding) {
199 LOG.debug("calculateCSFFteQuantity() start");
200
201 // appointmentFunding.refreshReferenceObject(BCPropertyConstants.BUDGET_CONSTRUCTION_POSITION);
202 BudgetConstructionPosition position = appointmentFunding.getBudgetConstructionPosition();
203 if (position == null) {
204 return BigDecimal.ZERO;
205 }
206
207 Integer payMonth = position.getIuPayMonths();
208 Integer normalWorkMonth = position.getIuNormalWorkMonths();
209 BigDecimal requestedCSFTimePercent = appointmentFunding.getAppointmentRequestedCsfTimePercent();
210
211 return this.calculateCSFFteQuantity(payMonth, normalWorkMonth, requestedCSFTimePercent);
212 }
213
214 /**
215 * @see org.kuali.kfs.module.bc.document.service.SalarySettingService#calculateCSFFteQuantity(java.lang.Integer,
216 * java.lang.Integer, java.math.BigDecimal)
217 */
218 public BigDecimal calculateCSFFteQuantity(Integer payMonth, Integer normalWorkMonth, BigDecimal requestedCSFTimePercent) {
219 LOG.debug("calculateCSFFteQuantity() start");
220
221 if (payMonth == null || normalWorkMonth == null || requestedCSFTimePercent == null) {
222 return BigDecimal.ZERO;
223 }
224
225 BigDecimal payMonthAsDecimal = BigDecimal.valueOf(payMonth);
226 BigDecimal normalMonthAsDecimal = BigDecimal.valueOf(normalWorkMonth);
227 BigDecimal fundingMonthPercent = normalMonthAsDecimal.divide(payMonthAsDecimal, 5, BigDecimal.ROUND_HALF_UP);
228
229 BigDecimal fteQuantity = requestedCSFTimePercent.multiply(fundingMonthPercent).divide(KFSConstants.ONE_HUNDRED.bigDecimalValue());
230
231 return fteQuantity.setScale(5, BigDecimal.ROUND_HALF_UP);
232 }
233
234 /**
235 * @see org.kuali.kfs.module.bc.document.service.SalarySettingService#isHourlyPaid(org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionGeneralLedger)
236 */
237 public boolean isHourlyPaid(PendingBudgetConstructionGeneralLedger pendingBudgetConstructionGeneralLedger) {
238 LOG.debug("isHourlyPaid() start");
239
240 Integer fiscalYear = pendingBudgetConstructionGeneralLedger.getUniversityFiscalYear();
241 String chartOfAccountsCode = pendingBudgetConstructionGeneralLedger.getChartOfAccountsCode();
242 String objectCode = pendingBudgetConstructionGeneralLedger.getFinancialObjectCode();
243
244 return this.isHourlyPaidObject(fiscalYear, chartOfAccountsCode, objectCode);
245 }
246
247 /**
248 * @see org.kuali.kfs.module.bc.document.service.SalarySettingService#isHourlyPaid(org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding)
249 */
250 public boolean isHourlyPaid(PendingBudgetConstructionAppointmentFunding appointmentFunding) {
251 LOG.debug("isHourlyPaid() start");
252
253 Integer fiscalYear = appointmentFunding.getUniversityFiscalYear();
254 String chartOfAccountsCode = appointmentFunding.getChartOfAccountsCode();
255 String objectCode = appointmentFunding.getFinancialObjectCode();
256
257 return this.isHourlyPaidObject(fiscalYear, chartOfAccountsCode, objectCode);
258 }
259
260 /**
261 * @see org.kuali.kfs.module.bc.document.service.SalarySettingService#isHourlyPaid(org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionGeneralLedger)
262 */
263 public boolean isHourlyPaidObject(Integer fiscalYear, String chartOfAccountsCode, String objectCode) {
264 LOG.debug("isHourlyPaid() start");
265
266 LaborLedgerObject laborLedgerObject = laborModuleService.retrieveLaborLedgerObject(fiscalYear, chartOfAccountsCode, objectCode);
267
268 if (laborLedgerObject == null) {
269 return false;
270 }
271
272 return BudgetParameterFinder.getBiweeklyPayTypeCodes().contains(laborLedgerObject.getFinancialObjectPayTypeCode());
273 }
274
275 /**
276 * @see org.kuali.kfs.module.bc.document.service.SalarySettingService#canBeVacant(java.util.List,
277 * org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding)
278 */
279 public boolean canBeVacant(List<PendingBudgetConstructionAppointmentFunding> appointmentFundings, PendingBudgetConstructionAppointmentFunding appointmentFunding) {
280 LOG.debug("canBeVacant(List, PendingBudgetConstructionAppointmentFunding) start");
281
282 if (!this.canBeVacant(appointmentFunding)) {
283 return false;
284 }
285
286 return this.findVacantAppointmentFunding(appointmentFundings, appointmentFunding) == null;
287 }
288
289 /**
290 * @see org.kuali.kfs.module.bc.document.service.SalarySettingService#findVacantAppointmentFunding(java.util.List,
291 * org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding)
292 */
293 public PendingBudgetConstructionAppointmentFunding findVacantAppointmentFunding(List<PendingBudgetConstructionAppointmentFunding> appointmentFundings, PendingBudgetConstructionAppointmentFunding appointmentFunding) {
294 LOG.debug("findVacantAppointmentFunding() start");
295
296 PendingBudgetConstructionAppointmentFunding vacantAppointmentFunding = this.createVacantAppointmentFunding(appointmentFunding);
297
298 return this.findAppointmentFunding(appointmentFundings, vacantAppointmentFunding);
299 }
300
301 /**
302 * @see org.kuali.kfs.module.bc.document.service.SalarySettingService#findAppointmentFunding(java.util.List,
303 * org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding)
304 */
305 public PendingBudgetConstructionAppointmentFunding findAppointmentFunding(List<PendingBudgetConstructionAppointmentFunding> appointmentFundings, PendingBudgetConstructionAppointmentFunding appointmentFunding) {
306 LOG.debug("findAppointmentFunding() start");
307
308 Map<String, Object> keyFieldValues = appointmentFunding.getValuesMap();
309 List<String> keyFields = new ArrayList<String>();
310 keyFields.addAll(keyFieldValues.keySet());
311
312 // determine whether there is vacant for the given appointment funding in its list
313 for (PendingBudgetConstructionAppointmentFunding fundingLine : appointmentFundings) {
314 if (ObjectUtil.equals(fundingLine, appointmentFunding, keyFields)) {
315 return fundingLine;
316 }
317 }
318
319 return null;
320 }
321
322 /**
323 * @see org.kuali.kfs.module.bc.document.service.SalarySettingService#canBeVacant(org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding)
324 */
325 public boolean canBeVacant(PendingBudgetConstructionAppointmentFunding appointmentFunding) {
326 LOG.debug("canBeVacant() start");
327
328 if (appointmentFunding.isNewLineIndicator()) {
329 return false;
330 }
331
332 // the given funding line has not been deleted
333 if (appointmentFunding.isAppointmentFundingDeleteIndicator()) {
334 return false;
335 }
336
337 // the given funding line cannot be a vacant line
338 String emplid = appointmentFunding.getEmplid();
339 if (BCConstants.VACANT_EMPLID.equals(emplid)) {
340 return false;
341 }
342
343 // check if the associated position is valid and active
344 BudgetConstructionPosition position = appointmentFunding.getBudgetConstructionPosition();
345 if (position == null || !position.isBudgetedPosition() || !position.isEffective()) {
346 return false;
347 }
348
349 // check if there is an existing vacant appintment funcding for the given funding line
350 boolean hasBeenVacated = this.hasBeenVacated(appointmentFunding);
351 if (hasBeenVacated) {
352 return false;
353 }
354
355 return true;
356 }
357
358 /**
359 * @see org.kuali.kfs.module.bc.document.service.SalarySettingService#vacateAppointmentFunding(org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding)
360 */
361 public PendingBudgetConstructionAppointmentFunding vacateAppointmentFunding(PendingBudgetConstructionAppointmentFunding appointmentFunding) {
362 LOG.debug("vacateAppointmentFunding() start");
363
364 PendingBudgetConstructionAppointmentFunding vacantAppointmentFunding = this.createVacantAppointmentFunding(appointmentFunding);
365 this.markAsDelete(appointmentFunding);
366
367 return vacantAppointmentFunding;
368 }
369
370 /**
371 * @see org.kuali.kfs.module.bc.document.service.SalarySettingService#vacateAppointmentFunding(java.util.List,
372 * org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding)
373 */
374 public PendingBudgetConstructionAppointmentFunding vacateAppointmentFunding(List<PendingBudgetConstructionAppointmentFunding> appointmentFundings, PendingBudgetConstructionAppointmentFunding appointmentFunding) {
375 PendingBudgetConstructionAppointmentFunding vacantAppointmentFunding = this.vacateAppointmentFunding(appointmentFunding);
376
377 if (vacantAppointmentFunding != null) {
378 appointmentFundings.add(vacantAppointmentFunding);
379 }
380
381 return vacantAppointmentFunding;
382 }
383
384 /**
385 * @see org.kuali.kfs.module.bc.document.service.SalarySettingService#purgeAppointmentFundings(java.util.List)
386 */
387 public void purgeAppointmentFundings(List<PendingBudgetConstructionAppointmentFunding> purgedAppointmentFundings) {
388 // remove the purged appointment funding lines and their referenced records
389 for (PendingBudgetConstructionAppointmentFunding appointmentFunding : purgedAppointmentFundings) {
390 if (!appointmentFunding.isNewLineIndicator()) {
391 businessObjectService.delete(appointmentFunding);
392 }
393 }
394 }
395
396 /**
397 * @see org.kuali.kfs.module.bc.document.service.SalarySettingService#adjustRequestedSalaryByAmount(org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding)
398 */
399 public void adjustRequestedSalaryByAmount(PendingBudgetConstructionAppointmentFunding appointmentFunding) {
400 LOG.debug("adjustRequestedSalaryByAmount() start");
401
402 int inputAdjustmentAmount = appointmentFunding.getAdjustmentAmount().intValue();
403
404 KualiInteger adjustmentAmount = new KualiInteger(inputAdjustmentAmount);
405 KualiInteger csfAmount = this.getCsfAmount(appointmentFunding);
406 KualiInteger appointmentRequestedAmount = csfAmount.add(adjustmentAmount);
407
408 appointmentFunding.setAppointmentRequestedAmount(appointmentRequestedAmount);
409
410 if (appointmentFunding.isHourlyPaid()) {
411 appointmentFunding.setAppointmentRequestedPayRate(BigDecimal.ZERO);
412 this.normalizePayRateAndAmount(appointmentFunding);
413 }
414 }
415
416 /**
417 * @see org.kuali.kfs.module.bc.document.service.SalarySettingService#adjustRequestedSalaryByPercent(org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding)
418 */
419 public void adjustRequestedSalaryByPercent(PendingBudgetConstructionAppointmentFunding appointmentFunding) {
420 LOG.debug("adjustRequestedSalaryByPercent() start");
421
422 KualiInteger csfAmount = this.getCsfAmount(appointmentFunding);
423
424 if (csfAmount.isNonZero()) {
425 KualiDecimal percent = appointmentFunding.getAdjustmentAmount();
426 BigDecimal adjustedAmount = csfAmount.multiply(percent).divide(KFSConstants.ONE_HUNDRED);
427
428 KualiInteger appointmentRequestedAmount = new KualiInteger(adjustedAmount).add(csfAmount);
429 appointmentFunding.setAppointmentRequestedAmount(appointmentRequestedAmount);
430 }
431
432 if (appointmentFunding.isHourlyPaid()) {
433 appointmentFunding.setAppointmentRequestedPayRate(BigDecimal.ZERO);
434 this.normalizePayRateAndAmount(appointmentFunding);
435 }
436 }
437
438 /**
439 * @see org.kuali.kfs.module.bc.document.service.SalarySettingService#saveSalarySetting(org.kuali.kfs.module.bc.businessobject.SalarySettingExpansion)
440 */
441 public void saveSalarySetting(SalarySettingExpansion salarySettingExpansion) {
442 LOG.debug("saveSalarySetting() start");
443
444 List<PendingBudgetConstructionAppointmentFunding> appointmentFundings = salarySettingExpansion.getPendingBudgetConstructionAppointmentFunding();
445 this.resetDeletedFundingLines(appointmentFundings);
446 this.updateAppointmentFundingsBeforeSaving(appointmentFundings);
447
448 KualiInteger requestedAmountTotal = SalarySettingCalculator.getAppointmentRequestedAmountTotal(appointmentFundings);
449 KualiInteger changes = KualiInteger.ZERO;
450
451 if (requestedAmountTotal != null) {
452 KualiInteger annualBalanceAmount = salarySettingExpansion.getAccountLineAnnualBalanceAmount();
453 changes = (annualBalanceAmount != null) ? requestedAmountTotal.subtract(annualBalanceAmount) : requestedAmountTotal;
454 }
455
456 salarySettingExpansion.setAccountLineAnnualBalanceAmount(requestedAmountTotal);
457 businessObjectService.save(salarySettingExpansion);
458
459 // now create a pseudo funding line if the BCAF list is empty so we can pass it to create 2PLG below
460 Boolean wasSalarySettingExpansionBCAFEmpty = salarySettingExpansion.getPendingBudgetConstructionAppointmentFunding().isEmpty();
461 if (wasSalarySettingExpansionBCAFEmpty) {
462 appointmentFundings.add(this.createPseudoAppointmentFundingLine(salarySettingExpansion));
463 }
464
465 // update or create plug line if the total amount has been changed
466 if (changes.isNonZero()) {
467
468 budgetDocumentService.updatePendingBudgetGeneralLedgerPlug(appointmentFundings.get(0), changes.negated());
469 }
470 }
471
472 public void savePBGLSalarySetting(SalarySettingExpansion salarySettingExpansion) {
473 LOG.debug("savePBGLSalarySetting() start");
474
475 // gwp - added this method to handle detail salary setting PBGL updates
476 // instead of using saveSalarySetting(SalarySettingExpansion salarySettingExpansion)
477
478 List<PendingBudgetConstructionAppointmentFunding> appointmentFundings = salarySettingExpansion.getPendingBudgetConstructionAppointmentFunding();
479
480 // this is already done in saveSalarySetting by a call to saveAppointmentFundings
481 // this.resetDeletedFundingLines(appointmentFundings);
482 // this.updateAppointmentFundingsBeforeSaving(appointmentFundings);
483
484 KualiInteger requestedAmountTotal = SalarySettingCalculator.getAppointmentRequestedAmountTotal(appointmentFundings);
485 KualiInteger changes = KualiInteger.ZERO;
486
487 if (requestedAmountTotal != null) {
488 KualiInteger annualBalanceAmount = salarySettingExpansion.getAccountLineAnnualBalanceAmount();
489 changes = (annualBalanceAmount != null) ? requestedAmountTotal.subtract(annualBalanceAmount) : requestedAmountTotal;
490 }
491
492 // salarySettingExpansion.setAccountLineAnnualBalanceAmount(requestedAmountTotal);
493 // businessObjectService.save(salarySettingExpansion);
494
495 // now create a pseudo funding line if the BCAF list is empty so we can pass it to create 2PLG below
496 Boolean wasSalarySettingExpansionBCAFEmpty = salarySettingExpansion.getPendingBudgetConstructionAppointmentFunding().isEmpty();
497 if (wasSalarySettingExpansionBCAFEmpty) {
498 appointmentFundings.add(this.createPseudoAppointmentFundingLine(salarySettingExpansion));
499 }
500
501 // For detail salary setting, we need to update existing or create new PBGL row for the BCAF set
502 // We can't save salarySettingExpansion here since it will also save the associated BCAF rows
503 // which in this case would be rows we might not have worked on.
504 // Also, don't save PBGL if it is not in DB already and the BCAF set was empty (no doo doo).
505 // that is, save if PBGL exists in DB or BCAF was not empty
506 if (salarySettingExpansion.getVersionNumber() != null || !wasSalarySettingExpansionBCAFEmpty) {
507
508 budgetDocumentService.updatePendingBudgetGeneralLedger(appointmentFundings.get(0), changes);
509 }
510
511 // update or create plug line if the total amount has been changed
512 if (changes.isNonZero()) {
513
514 budgetDocumentService.updatePendingBudgetGeneralLedgerPlug(appointmentFundings.get(0), changes.negated());
515 }
516 }
517
518 /**
519 * @see org.kuali.kfs.module.bc.document.service.SalarySettingService#saveSalarySetting(java.util.List)
520 */
521 public void saveSalarySetting(List<PendingBudgetConstructionAppointmentFunding> appointmentFundings, Boolean isSalarySettingByIncumbent) {
522
523 // Do the save/delete of BCAF rows from the salary setting detail screen first
524 this.saveAppointmentFundings(appointmentFundings);
525
526 // From the DB get the current unique set of SalarySettingExpansions (PBGL)
527 // associated with our Incumbent or Position BCAF rows.
528 // Each PBGL row from the DB will, in turn, have all the BCAF rows associated, including the
529 // ones we just stored as part of this save operation (see above)
530 // No one else would be updating these since we have a transaction lock on the account
531 Set<SalarySettingExpansion> salarySettingExpansionSet = new HashSet<SalarySettingExpansion>();
532
533 // these keep track of purged/unpurged used to unlock funding
534 // when the last line for that account is purged
535 Set<SalarySettingExpansion> purgedSseSet = new HashSet<SalarySettingExpansion>();
536 Set<SalarySettingExpansion> unpurgedSseSet = new HashSet<SalarySettingExpansion>();
537
538 // these keep track of purged/unpurged used to unlock positions
539 // when the last line for that position is purged
540 Set<BudgetConstructionPosition> purgedBPOSNSet = new HashSet<BudgetConstructionPosition>();
541 Set<BudgetConstructionPosition> unpurgedBPOSNSet = new HashSet<BudgetConstructionPosition>();
542
543 for (PendingBudgetConstructionAppointmentFunding fundingLine : appointmentFundings) {
544 SalarySettingExpansion salarySettingExpansion = this.retriveSalarySalarySettingExpansion(fundingLine);
545
546 if (salarySettingExpansion != null) {
547 salarySettingExpansionSet.add(salarySettingExpansion);
548 }
549 else {
550 // No PBGL row yet, create one to work with in memory only for now.
551 // Don't set versionNumber, this will indicate this is in memory only,
552 // so we can check for the case where there are no BCAF rows in the DB
553 // and no PBGL row either. We don't want to create a new zero request PBGL row in this case.
554 salarySettingExpansion = new SalarySettingExpansion();
555 salarySettingExpansion.setUniversityFiscalYear(fundingLine.getUniversityFiscalYear());
556 salarySettingExpansion.setChartOfAccountsCode(fundingLine.getChartOfAccountsCode());
557 salarySettingExpansion.setAccountNumber(fundingLine.getAccountNumber());
558 salarySettingExpansion.setSubAccountNumber(fundingLine.getSubAccountNumber());
559 salarySettingExpansion.setFinancialObjectCode(fundingLine.getFinancialObjectCode());
560 salarySettingExpansion.setFinancialSubObjectCode(fundingLine.getFinancialSubObjectCode());
561 salarySettingExpansion.setFinancialBalanceTypeCode(optionsService.getOptions(fundingLine.getUniversityFiscalYear()).getBaseBudgetFinancialBalanceTypeCd());
562 salarySettingExpansion.setFinancialObjectTypeCode(optionsService.getOptions(fundingLine.getUniversityFiscalYear()).getFinObjTypeExpenditureexpCd());
563 salarySettingExpansion.setAccountLineAnnualBalanceAmount(KualiInteger.ZERO);
564 salarySettingExpansion.setFinancialBeginningBalanceLineAmount(KualiInteger.ZERO);
565
566 // If this has been created in memory already, the list should already be attached
567 // and be in the current salarySettingExpansionSet.
568 // This handles the case where at least 2 new BCAF rows for the same non-existent PBGL row
569 // were saved earlier as part of this save operation.
570 if (!salarySettingExpansionSet.contains(salarySettingExpansion)) {
571
572 // Get the BCAF rows from the DB that are associated with the
573 // newly created salarySettingExpansion and attach so the
574 // method savePBGLSalarySetting() called below can get the total.
575 List<PendingBudgetConstructionAppointmentFunding> bcafRows = this.retrievePendingBudgetConstructionAppointmentFundings(salarySettingExpansion);
576 salarySettingExpansion.getPendingBudgetConstructionAppointmentFunding().addAll(bcafRows);
577 salarySettingExpansionSet.add(salarySettingExpansion);
578 }
579 }
580
581 // collect the set of purge/notpurged SalarySettingExpansions here
582 if (fundingLine.isPurged()) {
583 purgedSseSet.add(salarySettingExpansion);
584 }
585 else {
586 unpurgedSseSet.add(salarySettingExpansion);
587 }
588
589 // if SS by incumbent collect the set of purged/notpurged BudgetConstructionPositions here
590 if (isSalarySettingByIncumbent) {
591 BudgetConstructionPosition budgetConstructionPosition = fundingLine.getBudgetConstructionPosition();
592 if (fundingLine.isPurged()) {
593 purgedBPOSNSet.add(budgetConstructionPosition);
594 }
595 else {
596 unpurgedBPOSNSet.add(budgetConstructionPosition);
597 }
598 }
599 }
600
601 // remove from set of purged SSEs the set of notpurged SSEs
602 // leftover are those SSEs to release funding locks for after successful save
603 purgedSseSet.removeAll(unpurgedSseSet);
604
605 // if SS by incumbent, remove from set of purged BPOSNs the set of nonpurged BPOSNs
606 if (isSalarySettingByIncumbent) {
607 purgedBPOSNSet.removeAll(unpurgedBPOSNSet);
608 }
609
610 // Use the salarySettingExpansionSet to drive the update of PBGL rows (including any 2PLGs)
611 for (SalarySettingExpansion salarySettingExpansion : salarySettingExpansionSet) {
612
613 this.savePBGLSalarySetting(salarySettingExpansion);
614 }
615
616 // iterate leftover purged SSEs and release funding lock for each
617 for (SalarySettingExpansion salarySettingExpansion : purgedSseSet) {
618 String chartOfAccountsCode = salarySettingExpansion.getChartOfAccountsCode();
619 String accountNumber = salarySettingExpansion.getAccountNumber();
620 String subAccountNumber = salarySettingExpansion.getSubAccountNumber();
621 Integer fiscalYear = salarySettingExpansion.getUniversityFiscalYear();
622 String principalId = GlobalVariables.getUserSession().getPerson().getPrincipalId();
623
624 // release the associated funding lock
625 lockService.unlockFunding(chartOfAccountsCode, accountNumber, subAccountNumber, fiscalYear, principalId);
626
627 }
628
629 // if SS by incumbent iterate leftover purged BPOSNs and release position lock for each
630 for (BudgetConstructionPosition budgetConstructionPosition : purgedBPOSNSet) {
631 Person person = GlobalVariables.getUserSession().getPerson();
632 lockService.unlockPostion(budgetConstructionPosition, person);
633 }
634 }
635
636 /**
637 * @see org.kuali.kfs.module.bc.document.service.SalarySettingService#saveAppointmentFundings(java.util.List)
638 */
639 public void saveAppointmentFundings(List<PendingBudgetConstructionAppointmentFunding> appointmentFundings) {
640 LOG.debug("saveAppointmentFundings() start");
641
642 // remove the appointment funding lines being purged
643 List<PendingBudgetConstructionAppointmentFunding> purgedAppointmentFundings = new ArrayList<PendingBudgetConstructionAppointmentFunding>();
644 for (PendingBudgetConstructionAppointmentFunding appointmentFunding : appointmentFundings) {
645 if (appointmentFunding.isPurged()) {
646 purgedAppointmentFundings.add(appointmentFunding);
647 }
648 }
649 this.purgeAppointmentFundings(purgedAppointmentFundings);
650
651 // save the appointment funding lines that have been updated or newly created
652 List<PendingBudgetConstructionAppointmentFunding> savableAppointmentFundings = new ArrayList<PendingBudgetConstructionAppointmentFunding>(appointmentFundings);
653 savableAppointmentFundings.removeAll(purgedAppointmentFundings);
654
655 // gwp - added this as part of double save optimistic exception fix
656 // since savePBGLSalarySetting does not call this like saveSalarySetting does
657 this.resetDeletedFundingLines(appointmentFundings);
658
659 this.updateAppointmentFundingsBeforeSaving(savableAppointmentFundings);
660
661 // save each line so deletion aware reasons get removed when needed
662 for (PendingBudgetConstructionAppointmentFunding savableAppointmentFunding : savableAppointmentFundings){
663 businessObjectService.save(savableAppointmentFunding);
664 }
665 }
666
667 /**
668 * @see org.kuali.kfs.module.bc.document.service.SalarySettingService#retriveSalarySalarySettingExpansion(org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding)
669 */
670 public SalarySettingExpansion retriveSalarySalarySettingExpansion(PendingBudgetConstructionAppointmentFunding appointmentFunding) {
671 BudgetConstructionHeader budgetDocument = budgetDocumentService.getBudgetConstructionHeader(appointmentFunding);
672
673 Map<String, Object> fieldValues = ObjectUtil.buildPropertyMap(appointmentFunding, SalarySettingExpansion.getPrimaryKeyFields());
674 fieldValues.put(KFSPropertyConstants.DOCUMENT_NUMBER, budgetDocument.getDocumentNumber());
675
676 return (SalarySettingExpansion) businessObjectService.findByPrimaryKey(SalarySettingExpansion.class, fieldValues);
677 }
678
679 /**
680 * @see org.kuali.kfs.module.bc.document.service.SalarySettingService#retrievePendingBudgetConstructionAppointmentFundings(org.kuali.kfs.module.bc.businessobject.SalarySettingExpansion)
681 */
682 public List<PendingBudgetConstructionAppointmentFunding> retrievePendingBudgetConstructionAppointmentFundings(SalarySettingExpansion salarySettingExpansion) {
683
684 Map<String, Object> fieldValues = new HashMap<String, Object>();
685 fieldValues.put(KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR, salarySettingExpansion.getUniversityFiscalYear());
686 fieldValues.put(KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE, salarySettingExpansion.getChartOfAccountsCode());
687 fieldValues.put(KFSPropertyConstants.ACCOUNT_NUMBER, salarySettingExpansion.getAccountNumber());
688 fieldValues.put(KFSPropertyConstants.SUB_ACCOUNT_NUMBER, salarySettingExpansion.getSubAccountNumber());
689 fieldValues.put(KFSPropertyConstants.FINANCIAL_OBJECT_CODE, salarySettingExpansion.getFinancialObjectCode());
690 fieldValues.put(KFSPropertyConstants.FINANCIAL_SUB_OBJECT_CODE, salarySettingExpansion.getFinancialSubObjectCode());
691
692 return (List<PendingBudgetConstructionAppointmentFunding>) businessObjectService.findMatching(PendingBudgetConstructionAppointmentFunding.class, fieldValues);
693 }
694
695 /**
696 * @see org.kuali.kfs.module.bc.document.service.SalarySettingService#resetAppointmentFunding(org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding)
697 */
698 public void resetAppointmentFunding(PendingBudgetConstructionAppointmentFunding appointmentFunding) {
699 appointmentFunding.setAppointmentRequestedAmount(KualiInteger.ZERO);
700 appointmentFunding.setAppointmentRequestedTimePercent(BigDecimal.ZERO);
701 appointmentFunding.setAppointmentRequestedPayRate(BigDecimal.ZERO);
702 appointmentFunding.setAppointmentRequestedFteQuantity(BigDecimal.ZERO);
703
704 appointmentFunding.setAppointmentRequestedCsfAmount(KualiInteger.ZERO);
705 appointmentFunding.setAppointmentRequestedCsfFteQuantity(BigDecimal.ZERO);
706 appointmentFunding.setAppointmentRequestedCsfTimePercent(BigDecimal.ZERO);
707
708 appointmentFunding.setAppointmentTotalIntendedAmount(KualiInteger.ZERO);
709 appointmentFunding.setAppointmentTotalIntendedFteQuantity(BigDecimal.ZERO);
710
711 appointmentFunding.setAppointmentFundingDurationCode(BCConstants.AppointmentFundingDurationCodes.NONE.durationCode);
712
713 appointmentFunding.setPositionObjectChangeIndicator(false);
714 appointmentFunding.setPositionSalaryChangeIndicator(false);
715 }
716
717 /**
718 * @see org.kuali.kfs.module.bc.document.service.SalarySettingService#markAsDelete(org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding)
719 */
720 public void markAsDelete(PendingBudgetConstructionAppointmentFunding appointmentFunding) {
721 this.resetAppointmentFunding(appointmentFunding);
722
723 appointmentFunding.setAppointmentFundingDeleteIndicator(true);
724 }
725
726 /**
727 * @see org.kuali.kfs.module.bc.document.service.SalarySettingService#revert(java.util.List,
728 * org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding)
729 */
730 public void revert(List<PendingBudgetConstructionAppointmentFunding> appointmentFundings, PendingBudgetConstructionAppointmentFunding appointmentFunding) {
731 PendingBudgetConstructionAppointmentFunding vacantFunding = this.findVacantAppointmentFunding(appointmentFundings, appointmentFunding);
732
733 if (vacantFunding != null) {
734 appointmentFundings.remove(vacantFunding);
735 }
736
737 PendingBudgetConstructionAppointmentFunding newAppointmentFunding = (PendingBudgetConstructionAppointmentFunding) businessObjectService.retrieve(appointmentFunding);
738 appointmentFundings.add(newAppointmentFunding);
739 appointmentFundings.remove(appointmentFunding);
740 }
741
742 /**
743 * @see org.kuali.kfs.module.bc.document.service.SalarySettingService#updateAccessOfAppointmentFunding(org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding,
744 * org.kuali.kfs.module.bc.util.SalarySettingFieldsHolder, boolean, java.util.Map, org.kuali.rice.kim.bo.Person)
745 */
746 public boolean updateAccessOfAppointmentFunding(PendingBudgetConstructionAppointmentFunding appointmentFunding, SalarySettingFieldsHolder salarySettingFieldsHolder, boolean budgetByObjectMode, boolean hasDocumentEditAccess, Person person) {
747 String budgetChartOfAccountsCode = salarySettingFieldsHolder.getChartOfAccountsCode();
748 String budgetAccountNumber = salarySettingFieldsHolder.getAccountNumber();
749 String budgetSubAccountNumber = salarySettingFieldsHolder.getSubAccountNumber();
750 String budgetObjectCode = salarySettingFieldsHolder.getFinancialObjectCode();
751 String budgetSubObjectCode = salarySettingFieldsHolder.getFinancialSubObjectCode();
752
753 String chartOfAccountsCode = appointmentFunding.getChartOfAccountsCode();
754 String accountNumber = appointmentFunding.getAccountNumber();
755 String subAccountNumber = appointmentFunding.getSubAccountNumber();
756 String objectCode = appointmentFunding.getFinancialObjectCode();
757 String subObjectCode = appointmentFunding.getFinancialSubObjectCode();
758
759 // just allow edit if budget by object mode (general case of single account mode)
760 if (budgetByObjectMode && StringUtils.equals(chartOfAccountsCode, budgetChartOfAccountsCode) && StringUtils.equals(accountNumber, budgetAccountNumber) && StringUtils.equals(subAccountNumber, budgetSubAccountNumber)) {
761 // use the edit permission already calculated for the home account during document open
762 appointmentFunding.setDisplayOnlyMode(!hasDocumentEditAccess);
763
764 return true;
765 }
766
767 boolean isUpdatedByUserLevel = this.updateAccessOfAppointmentFundingByUserLevel(appointmentFunding, person);
768 if (isUpdatedByUserLevel) {
769 return true;
770 }
771
772 return false;
773 }
774
775 /**
776 * @see org.kuali.kfs.module.bc.document.service.SalarySettingService#updateAccessOfAppointmentFundingByUserLevel(org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding,
777 * org.kuali.rice.kim.bo.Person)
778 */
779 public boolean updateAccessOfAppointmentFundingByUserLevel(PendingBudgetConstructionAppointmentFunding appointmentFunding, Person user) {
780 BudgetConstructionHeader budgetConstructionHeader = budgetDocumentService.getBudgetConstructionHeader(appointmentFunding);
781 if (budgetConstructionHeader == null) {
782 return false;
783 }
784
785 BudgetConstructionDocument document;
786 try {
787 document = (BudgetConstructionDocument) documentService.getByDocumentHeaderId(budgetConstructionHeader.getDocumentNumber());
788 }
789 catch (WorkflowException e) {
790 throw new RuntimeException("Fail to retrieve budget document for doc id " + budgetConstructionHeader.getDocumentNumber());
791 }
792
793 TransactionalDocumentAuthorizer documentAuthorizer = (TransactionalDocumentAuthorizer) getDocumentHelperService().getDocumentAuthorizer(document);
794
795 boolean hasEditAccess = documentAuthorizer.isAuthorized(document, BCConstants.BUDGET_CONSTRUCTION_NAMESPACE, BCConstants.KimConstants.EDIT_BCAF_PERMISSION_NAME, user.getPrincipalId());
796 appointmentFunding.setDisplayOnlyMode(!hasEditAccess);
797
798 boolean hasViewAmountsAccess = documentAuthorizer.isAuthorized(document, BCConstants.BUDGET_CONSTRUCTION_NAMESPACE, BCConstants.KimConstants.VIEW_BCAF_AMOUNTS_PERMISSION_NAME, user.getPrincipalId());
799 appointmentFunding.setExcludedFromTotal(!hasViewAmountsAccess);
800
801 return true;
802 }
803
804 /**
805 * @see org.kuali.kfs.module.bc.document.service.SalarySettingService#updateDerivedInformationForAppointmentFundings(java.util.List)
806 */
807 public void updateAppointmentFundingsBeforeSaving(List<PendingBudgetConstructionAppointmentFunding> appointmentFundings) {
808 LOG.debug("updateDerivedInformationForAppointmentFundings() start");
809
810 for (PendingBudgetConstructionAppointmentFunding appointmentFunding : appointmentFundings) {
811 this.recalculateDerivedInformation(appointmentFunding);
812
813 appointmentFunding.setNewLineIndicator(false);
814 }
815 }
816
817 /**
818 * @see org.kuali.kfs.module.bc.document.service.SalarySettingService#updateDerivedInformationForAppointmentFunding(org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding)
819 */
820 public void recalculateDerivedInformation(PendingBudgetConstructionAppointmentFunding appointmentFunding) {
821 this.preprocessFundingReason(appointmentFunding);
822 this.preprocessLeaveRequest(appointmentFunding);
823
824 boolean isHourlyPaid = this.isHourlyPaid(appointmentFunding);
825 appointmentFunding.setHourlyPaid(isHourlyPaid);
826
827 if (appointmentFunding.isHourlyPaid()) {
828 this.normalizePayRateAndAmount(appointmentFunding);
829 }
830 else {
831 appointmentFunding.setAppointmentRequestedPayRate(BigDecimal.ZERO);
832 }
833
834 BigDecimal requestedFteQuantity = this.calculateFteQuantityFromAppointmentFunding(appointmentFunding);
835 appointmentFunding.setAppointmentRequestedFteQuantity(requestedFteQuantity);
836
837 if (!appointmentFunding.getAppointmentFundingDurationCode().equals(NONE.durationCode)) {
838 BigDecimal requestedCSFFteQuantity = this.calculateCSFFteQuantityFromAppointmentFunding(appointmentFunding);
839 appointmentFunding.setAppointmentRequestedCsfFteQuantity(requestedCSFFteQuantity);
840 }
841 }
842
843 /**
844 * reset the amount values of each line in the given appointment fundings as zeros and remove the reason annotations if the line
845 * is marked as deleted
846 *
847 * @param pendingBudgetConstructionAppointmentFunding the given appointment fundings
848 */
849 protected void resetDeletedFundingLines(List<PendingBudgetConstructionAppointmentFunding> pendingBudgetConstructionAppointmentFunding) {
850 for (PendingBudgetConstructionAppointmentFunding appointmentFunding : pendingBudgetConstructionAppointmentFunding) {
851 if (!appointmentFunding.isAppointmentFundingDeleteIndicator() || appointmentFunding.isPersistedDeleteIndicator()) {
852 continue;
853 }
854
855 this.markAsDelete(appointmentFunding);
856 List<BudgetConstructionAppointmentFundingReason> reasons = appointmentFunding.getBudgetConstructionAppointmentFundingReason();
857 if (reasons != null) {
858 reasons.clear();
859 }
860
861 appointmentFunding.setPersistedDeleteIndicator(true);
862 }
863 }
864
865 /**
866 * get the csf tracker amount of the given appointment funding
867 *
868 * @param appointmentFunding the given appointment funding
869 * @return the csf tracker amount of the given appointment funding if any; otherwise, return zero
870 */
871 protected KualiInteger getCsfAmount(PendingBudgetConstructionAppointmentFunding appointmentFunding) {
872 if (appointmentFunding == null) {
873 return KualiInteger.ZERO;
874 }
875
876 BudgetConstructionCalculatedSalaryFoundationTracker csfTracker = appointmentFunding.getEffectiveCSFTracker();
877 if (csfTracker == null) {
878 return KualiInteger.ZERO;
879 }
880
881 return csfTracker.getCsfAmount();
882 }
883
884 /**
885 * determine whether there exists at lease one vacant funding line for the given appointment funding
886 *
887 * @param appointmentFunding the given appointment funding
888 * @return true if there exists at lease one vacant funding line for the given appointment funding; otherwise, return false
889 */
890 protected boolean hasBeenVacated(PendingBudgetConstructionAppointmentFunding appointmentFunding) {
891 Map<String, Object> keyFieldValues = appointmentFunding.getValuesMap();
892 keyFieldValues.put(KFSPropertyConstants.EMPLID, BCConstants.VACANT_EMPLID);
893
894 return businessObjectService.countMatching(PendingBudgetConstructionAppointmentFunding.class, keyFieldValues) > 0;
895 }
896
897 /**
898 * create a vacant appointment funding based on the given budget funding
899 *
900 * @param appointmentFunding the given appointment funding
901 * @return a vacant appointment funding
902 */
903 protected PendingBudgetConstructionAppointmentFunding createVacantAppointmentFunding(PendingBudgetConstructionAppointmentFunding appointmentFunding) {
904 PendingBudgetConstructionAppointmentFunding vacantAppointmentFunding = new PendingBudgetConstructionAppointmentFunding();
905
906 ObjectUtil.buildObjectWithoutReferenceFields(vacantAppointmentFunding, appointmentFunding);
907 vacantAppointmentFunding.setEmplid(BCConstants.VACANT_EMPLID);
908 vacantAppointmentFunding.setAppointmentFundingDeleteIndicator(false);
909 vacantAppointmentFunding.setPersistedDeleteIndicator(false);
910 vacantAppointmentFunding.setVersionNumber(null);
911
912 return vacantAppointmentFunding;
913 }
914
915 /**
916 * create a pseudo appointment funding for the salary setting expansion this is used when there are no funding lines for the
917 * salary setting expansion to get a funding line to be used to pass primary key info
918 *
919 * @param salarySettingExpansion
920 * @return a pseudo appointment funding
921 */
922 protected PendingBudgetConstructionAppointmentFunding createPseudoAppointmentFundingLine(SalarySettingExpansion salarySettingExpansion) {
923 PendingBudgetConstructionAppointmentFunding pseudoAppointmentFunding = new PendingBudgetConstructionAppointmentFunding();
924
925 pseudoAppointmentFunding.setUniversityFiscalYear(salarySettingExpansion.getUniversityFiscalYear());
926 pseudoAppointmentFunding.setChartOfAccountsCode(salarySettingExpansion.getChartOfAccountsCode());
927 pseudoAppointmentFunding.setAccountNumber(salarySettingExpansion.getAccountNumber());
928 pseudoAppointmentFunding.setSubAccountNumber(salarySettingExpansion.getSubAccountNumber());
929 pseudoAppointmentFunding.setFinancialObjectCode(salarySettingExpansion.getFinancialObjectCode());
930 pseudoAppointmentFunding.setFinancialSubObjectCode(salarySettingExpansion.getFinancialSubObjectCode());
931 pseudoAppointmentFunding.setAppointmentFundingDeleteIndicator(false);
932 pseudoAppointmentFunding.setAppointmentRequestedAmount(KualiInteger.ZERO);
933 pseudoAppointmentFunding.refreshReferenceObject(KFSPropertyConstants.ACCOUNT);
934
935 return pseudoAppointmentFunding;
936 }
937
938 /**
939 * preprocess the funding reason of the given appointment funding before the funding is saved
940 *
941 * @param appointmentFunding the given appointment funding
942 */
943 public void preprocessFundingReason(PendingBudgetConstructionAppointmentFunding appointmentFunding) {
944
945 List<BudgetConstructionAppointmentFundingReason> fundingReasons = appointmentFunding.getBudgetConstructionAppointmentFundingReason();
946
947 // do special removal of any reason rows where the reason code is blank
948 if (!fundingReasons.isEmpty() && StringUtils.isBlank(fundingReasons.get(0).getAppointmentFundingReasonCode())) {
949 fundingReasons.clear();
950 }
951 }
952
953 /**
954 * preprocess the leave request of the given appointment funding before the funding is saved
955 *
956 * @param appointmentFunding the given appointment funding
957 */
958 public void preprocessLeaveRequest(PendingBudgetConstructionAppointmentFunding appointmentFunding) {
959 String durationCode = appointmentFunding.getAppointmentFundingDurationCode();
960
961 if (StringUtils.isEmpty(durationCode) || StringUtils.equals(durationCode, BCConstants.AppointmentFundingDurationCodes.NONE.durationCode)) {
962 appointmentFunding.setAppointmentRequestedCsfAmount(KualiInteger.ZERO);
963 appointmentFunding.setAppointmentRequestedCsfFteQuantity(BigDecimal.ZERO);
964 appointmentFunding.setAppointmentRequestedCsfTimePercent(BigDecimal.ZERO);
965 }
966 }
967
968 /**
969 * @see org.kuali.kfs.module.bc.document.service.SalarySettingService#hasExistingFundingReason(org.kuali.kfs.module.bc.businessobject.BudgetConstructionAppointmentFundingReasonCode)
970 */
971 public boolean hasExistingFundingReason(BudgetConstructionAppointmentFundingReasonCode budgetConstructionAppointmentFundingReasonCode) {
972
973 Map<String, Object> queryMap = new HashMap<String, Object>();
974 queryMap.put("appointmentFundingReasonCode", budgetConstructionAppointmentFundingReasonCode.getAppointmentFundingReasonCode());
975
976 return (businessObjectService.countMatching(BudgetConstructionAppointmentFundingReason.class, queryMap) > 0);
977 }
978
979 /**
980 * Sets the kualiConfigurationService attribute value.
981 *
982 * @param kualiConfigurationService The kualiConfigurationService to set.
983 */
984 public void setKualiConfigurationService(KualiConfigurationService kualiConfigurationService) {
985 this.kualiConfigurationService = kualiConfigurationService;
986 }
987
988 /**
989 * Sets the businessObjectService attribute value.
990 *
991 * @param businessObjectService The businessObjectService to set.
992 */
993 public void setBusinessObjectService(BusinessObjectService businessObjectService) {
994 this.businessObjectService = businessObjectService;
995 }
996
997 /**
998 * Sets the laborModuleService attribute value.
999 *
1000 * @param laborModuleService The laborModuleService to set.
1001 */
1002 public void setLaborModuleService(LaborModuleService laborModuleService) {
1003 this.laborModuleService = laborModuleService;
1004 }
1005
1006 /**
1007 * Sets the budgetDocumentService attribute value.
1008 *
1009 * @param budgetDocumentService The budgetDocumentService to set.
1010 */
1011 public void setBudgetDocumentService(BudgetDocumentService budgetDocumentService) {
1012 this.budgetDocumentService = budgetDocumentService;
1013 }
1014
1015 /**
1016 * Sets the benefitsCalculationService attribute value.
1017 *
1018 * @param benefitsCalculationService The benefitsCalculationService to set.
1019 */
1020 public void setBenefitsCalculationService(BenefitsCalculationService benefitsCalculationService) {
1021 this.benefitsCalculationService = benefitsCalculationService;
1022 }
1023
1024 /**
1025 * Sets the optionsService attribute value.
1026 *
1027 * @param optionsService The optionsService to set.
1028 */
1029 public void setOptionsService(OptionsService optionsService) {
1030 this.optionsService = optionsService;
1031 }
1032
1033 /**
1034 * Sets the lockService attribute value.
1035 *
1036 * @param lockService The lockService to set.
1037 */
1038 public void setLockService(LockService lockService) {
1039 this.lockService = lockService;
1040 }
1041
1042 /**
1043 * Gets the documentHelperService attribute.
1044 *
1045 * @return Returns the documentHelperService.
1046 */
1047 public DocumentHelperService getDocumentHelperService() {
1048 if (documentHelperService == null) {
1049 documentHelperService = SpringContext.getBean(DocumentHelperService.class);
1050 }
1051 return documentHelperService;
1052 }
1053
1054 /**
1055 * Sets the documentHelperService attribute value.
1056 *
1057 * @param documentHelperService The documentHelperService to set.
1058 */
1059 public void setDocumentHelperService(DocumentHelperService documentHelperService) {
1060 this.documentHelperService = documentHelperService;
1061 }
1062
1063 /**
1064 * Sets the documentService attribute value.
1065 *
1066 * @param documentService The documentService to set.
1067 */
1068 public void setDocumentService(DocumentService documentService) {
1069 this.documentService = documentService;
1070 }
1071
1072 /**
1073 * Sets the budgetConstructionProcessorService attribute value.
1074 *
1075 * @param budgetConstructionProcessorService The budgetConstructionProcessorService to set.
1076 */
1077 public void setBudgetConstructionProcessorService(BudgetConstructionProcessorService budgetConstructionProcessorService) {
1078 this.budgetConstructionProcessorService = budgetConstructionProcessorService;
1079 }
1080 }