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 }