001 /* 002 * Copyright 2011 The Kuali Foundation. 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package org.kuali.kfs.module.bc.document.service.impl; 017 018 import java.util.ArrayList; 019 import java.util.Arrays; 020 import java.util.Calendar; 021 import java.util.Collection; 022 import java.util.Collections; 023 import java.util.HashMap; 024 import java.util.List; 025 import java.util.ListIterator; 026 import java.util.Map; 027 028 import org.apache.commons.lang.StringUtils; 029 import org.kuali.kfs.coa.businessobject.A21SubAccount; 030 import org.kuali.kfs.coa.businessobject.Account; 031 import org.kuali.kfs.coa.businessobject.SubAccount; 032 import org.kuali.kfs.coa.businessobject.SubFundGroup; 033 import org.kuali.kfs.coa.service.OrganizationService; 034 import org.kuali.kfs.fp.service.FiscalYearFunctionControlService; 035 import org.kuali.kfs.integration.ld.LaborLedgerBenefitsCalculation; 036 import org.kuali.kfs.module.bc.BCConstants; 037 import org.kuali.kfs.module.bc.BCKeyConstants; 038 import org.kuali.kfs.module.bc.BCPropertyConstants; 039 import org.kuali.kfs.module.bc.BCConstants.MonthSpreadDeleteType; 040 import org.kuali.kfs.module.bc.businessobject.BudgetConstructionAccountOrganizationHierarchy; 041 import org.kuali.kfs.module.bc.businessobject.BudgetConstructionAccountReports; 042 import org.kuali.kfs.module.bc.businessobject.BudgetConstructionHeader; 043 import org.kuali.kfs.module.bc.businessobject.BudgetConstructionMonthly; 044 import org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding; 045 import org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionGeneralLedger; 046 import org.kuali.kfs.module.bc.businessobject.SalarySettingExpansion; 047 import org.kuali.kfs.module.bc.document.BudgetConstructionDocument; 048 import org.kuali.kfs.module.bc.document.dataaccess.BudgetConstructionDao; 049 import org.kuali.kfs.module.bc.document.service.BenefitsCalculationService; 050 import org.kuali.kfs.module.bc.document.service.BudgetDocumentService; 051 import org.kuali.kfs.module.bc.document.service.BudgetParameterService; 052 import org.kuali.kfs.module.bc.document.validation.event.DeleteMonthlySpreadEvent; 053 import org.kuali.kfs.module.bc.document.validation.impl.BudgetConstructionRuleUtil; 054 import org.kuali.kfs.module.bc.document.web.struts.BudgetConstructionForm; 055 import org.kuali.kfs.module.bc.document.web.struts.MonthlyBudgetForm; 056 import org.kuali.kfs.module.bc.util.BudgetParameterFinder; 057 import org.kuali.kfs.sys.KFSConstants; 058 import org.kuali.kfs.sys.KFSPropertyConstants; 059 import org.kuali.kfs.sys.businessobject.FinancialSystemDocumentHeader; 060 import org.kuali.kfs.sys.service.NonTransactional; 061 import org.kuali.kfs.sys.service.OptionsService; 062 import org.kuali.rice.kew.exception.WorkflowException; 063 import org.kuali.rice.kew.routeheader.service.RouteHeaderService; 064 import org.kuali.rice.kim.bo.Person; 065 import org.kuali.rice.kns.dao.DocumentDao; 066 import org.kuali.rice.kns.document.Document; 067 import org.kuali.rice.kns.exception.ValidationException; 068 import org.kuali.rice.kns.rule.event.KualiDocumentEvent; 069 import org.kuali.rice.kns.rule.event.SaveDocumentEvent; 070 import org.kuali.rice.kns.service.BusinessObjectService; 071 import org.kuali.rice.kns.service.DocumentService; 072 import org.kuali.rice.kns.service.KualiModuleService; 073 import org.kuali.rice.kns.service.ParameterService; 074 import org.kuali.rice.kns.service.PersistenceService; 075 import org.kuali.rice.kns.util.GlobalVariables; 076 import org.kuali.rice.kns.util.KualiDecimal; 077 import org.kuali.rice.kns.util.KualiInteger; 078 import org.kuali.rice.kns.util.MessageList; 079 import org.kuali.rice.kns.util.ObjectUtils; 080 import org.kuali.rice.kns.workflow.service.WorkflowDocumentService; 081 import org.springframework.dao.OptimisticLockingFailureException; 082 import org.springframework.transaction.annotation.Transactional; 083 084 /** 085 * Implements the BudgetDocumentService interface. Methods here operate on objects associated with the Budget Construction document 086 * such as BudgetConstructionHeader 087 */ 088 public class BudgetDocumentServiceImpl implements BudgetDocumentService { 089 private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(BudgetDocumentServiceImpl.class); 090 091 private BudgetConstructionDao budgetConstructionDao; 092 private DocumentDao documentDao; 093 private DocumentService documentService; 094 private WorkflowDocumentService workflowDocumentService; 095 private BenefitsCalculationService benefitsCalculationService; 096 private BusinessObjectService businessObjectService; 097 private KualiModuleService kualiModuleService; 098 private ParameterService parameterService; 099 private BudgetParameterService budgetParameterService; 100 private FiscalYearFunctionControlService fiscalYearFunctionControlService; 101 private OptionsService optionsService; 102 private PersistenceService persistenceService; 103 private OrganizationService organizationService; 104 105 /** 106 * @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#getByCandidateKey(java.lang.String, java.lang.String, 107 * java.lang.String, java.lang.Integer) 108 */ 109 @Transactional 110 public BudgetConstructionHeader getByCandidateKey(String chartOfAccountsCode, String accountNumber, String subAccountNumber, Integer fiscalYear) { 111 return budgetConstructionDao.getByCandidateKey(chartOfAccountsCode, accountNumber, subAccountNumber, fiscalYear); 112 } 113 114 /** 115 * @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#saveDocument(org.kuali.rice.kns.document.Document) 116 * similar to DocumentService.saveDocument() 117 */ 118 @Transactional 119 public Document saveDocument(BudgetConstructionDocument budgetConstructionDocument) throws WorkflowException, ValidationException { 120 121 // user did explicit save here so mark as touched 122 budgetConstructionDocument.getDocumentHeader().setFinancialDocumentStatusCode(KFSConstants.DocumentStatusCodes.ENROUTE); 123 124 this.saveDocumentNoWorkflow(budgetConstructionDocument); 125 126 GlobalVariables.getUserSession().setWorkflowDocument(budgetConstructionDocument.getDocumentHeader().getWorkflowDocument()); 127 128 // save any messages up to this point and put them back in after logDocumentAction() 129 // this is a hack to get around the problem where messageLists gets cleared 130 // that is PostProcessorServiceImpl.doActionTaken(ActionTakenEventDTO), establishGlobalVariables(), which does 131 // GlobalVariables.clear() 132 // not sure why this doesn't trash the GlobalVariables.getMessageMap() 133 MessageList messagesSoFar = GlobalVariables.getMessageList(); 134 135 budgetConstructionDocument.getDocumentHeader().getWorkflowDocument().logDocumentAction("Document Updated"); 136 137 // putting messages back in 138 GlobalVariables.getMessageList().addAll(messagesSoFar); 139 140 return budgetConstructionDocument; 141 } 142 143 /** 144 * @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#saveDocumentNoWorkflow(org.kuali.rice.kns.document.Document) 145 */ 146 @Transactional 147 public Document saveDocumentNoWorkflow(BudgetConstructionDocument bcDoc) throws ValidationException { 148 return this.saveDocumentNoWorkFlow(bcDoc, MonthSpreadDeleteType.NONE, true); 149 } 150 151 /** 152 * @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#saveDocumentNoWorkFlow(org.kuali.kfs.module.bc.document.BudgetConstructionDocument, 153 * org.kuali.kfs.module.bc.BCConstants.MonthSpreadDeleteType, boolean) 154 */ 155 @Transactional 156 public Document saveDocumentNoWorkFlow(BudgetConstructionDocument bcDoc, MonthSpreadDeleteType monthSpreadDeleteType, boolean doMonthRICheck) throws ValidationException { 157 158 checkForNulls(bcDoc); 159 160 bcDoc.prepareForSave(); 161 162 // validate and save the local objects not workflow objects 163 // this eventually calls BudgetConstructionRules.processSaveDocument() which overrides the method in DocumentRuleBase 164 if (doMonthRICheck) { 165 validateAndPersistDocument(bcDoc, new SaveDocumentEvent(bcDoc)); 166 } 167 else { 168 validateAndPersistDocument(bcDoc, new DeleteMonthlySpreadEvent(bcDoc, monthSpreadDeleteType)); 169 } 170 return bcDoc; 171 } 172 173 @Transactional 174 public void saveMonthlyBudget(MonthlyBudgetForm monthlyBudgetForm, BudgetConstructionMonthly budgetConstructionMonthly) { 175 176 BudgetConstructionForm budgetConstructionForm = (BudgetConstructionForm) GlobalVariables.getUserSession().retrieveObject(monthlyBudgetForm.getReturnFormKey()); 177 BudgetConstructionDocument bcDoc = budgetConstructionForm.getBudgetConstructionDocument(); 178 179 // handle any override situation 180 // getting here assumes that the line is not a salary detail line and that overrides are allowed 181 KualiInteger changeAmount = KualiInteger.ZERO; 182 KualiInteger monthTotalAmount = budgetConstructionMonthly.getFinancialDocumentMonthTotalLineAmount(); 183 KualiInteger pbglRequestAmount = budgetConstructionMonthly.getPendingBudgetConstructionGeneralLedger().getAccountLineAnnualBalanceAmount(); 184 if (!monthTotalAmount.equals(pbglRequestAmount)) { 185 186 changeAmount = monthTotalAmount.subtract(pbglRequestAmount); 187 188 // change the pbgl request amount store it and sync the object in session 189 budgetConstructionMonthly.refreshReferenceObject("pendingBudgetConstructionGeneralLedger"); 190 191 PendingBudgetConstructionGeneralLedger sourceRow = (PendingBudgetConstructionGeneralLedger) businessObjectService.retrieve(budgetConstructionMonthly.getPendingBudgetConstructionGeneralLedger()); 192 sourceRow.setAccountLineAnnualBalanceAmount(monthTotalAmount); 193 businessObjectService.save(sourceRow); 194 195 this.addOrUpdatePBGLRow(bcDoc, sourceRow); 196 bcDoc.setExpenditureAccountLineAnnualBalanceAmountTotal(bcDoc.getExpenditureAccountLineAnnualBalanceAmountTotal().add(changeAmount)); 197 198 } 199 200 businessObjectService.save(budgetConstructionMonthly); 201 this.callForBenefitsCalcIfNeeded(bcDoc, budgetConstructionMonthly, changeAmount); 202 } 203 204 /** 205 * @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#callForBenefitsCalcIfNeeded(org.kuali.kfs.module.bc.document.BudgetConstructionDocument, 206 * org.kuali.kfs.module.bc.businessobject.BudgetConstructionMonthly, org.kuali.rice.kns.util.KualiInteger) 207 */ 208 @Transactional 209 public void callForBenefitsCalcIfNeeded(BudgetConstructionDocument bcDoc, BudgetConstructionMonthly budgetConstructionMonthly, KualiInteger pbglChangeAmount) { 210 211 if (!benefitsCalculationService.isBenefitsCalculationDisabled()) { 212 if (budgetConstructionMonthly.getPendingBudgetConstructionGeneralLedger().getPositionObjectBenefit() != null && !budgetConstructionMonthly.getPendingBudgetConstructionGeneralLedger().getPositionObjectBenefit().isEmpty()) { 213 214 bcDoc.setMonthlyBenefitsCalcNeeded(true); 215 if (pbglChangeAmount.isNonZero()) { 216 bcDoc.setBenefitsCalcNeeded(true); 217 } 218 } 219 } 220 } 221 222 /** 223 * @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#calculateBenefitsIfNeeded(org.kuali.kfs.module.bc.document.BudgetConstructionDocument) 224 */ 225 @Transactional 226 public void calculateBenefitsIfNeeded(BudgetConstructionDocument bcDoc) { 227 228 if (bcDoc.isBenefitsCalcNeeded() || bcDoc.isMonthlyBenefitsCalcNeeded()) { 229 230 if (bcDoc.isBenefitsCalcNeeded()) { 231 this.calculateAnnualBenefits(bcDoc); 232 } 233 234 if (bcDoc.isMonthlyBenefitsCalcNeeded()) { 235 this.calculateMonthlyBenefits(bcDoc); 236 } 237 238 // reload from the DB and refresh refs 239 this.reloadBenefitsLines(bcDoc); 240 } 241 } 242 243 /** 244 * @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#calculateBenefits(org.kuali.kfs.module.bc.document.BudgetConstructionDocument) 245 */ 246 @Transactional 247 public void calculateBenefits(BudgetConstructionDocument bcDoc) { 248 249 this.calculateAnnualBenefits(bcDoc); 250 this.calculateMonthlyBenefits(bcDoc); 251 252 // reload from the DB and refresh refs 253 this.reloadBenefitsLines(bcDoc); 254 } 255 256 /** 257 * @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#calculateAnnualBenefits(org.kuali.kfs.module.bc.document.BudgetConstructionDocument) 258 */ 259 @Transactional 260 protected void calculateAnnualBenefits(BudgetConstructionDocument bcDoc) { 261 262 // allow benefits calculation if document's account is not salary setting only lines 263 bcDoc.setBenefitsCalcNeeded(false); 264 if (!bcDoc.isSalarySettingOnly()) { 265 266 // pbgl lines are saved at this point, calc benefits 267 benefitsCalculationService.calculateAnnualBudgetConstructionGeneralLedgerBenefits(bcDoc.getDocumentNumber(), bcDoc.getUniversityFiscalYear(), bcDoc.getChartOfAccountsCode(), bcDoc.getAccountNumber(), bcDoc.getSubAccountNumber()); 268 269 // write global message on calc success 270 GlobalVariables.getMessageList().add(BCKeyConstants.MESSAGE_BENEFITS_CALCULATED); 271 } 272 } 273 274 /** 275 * @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#calculateMonthlyBenefits(org.kuali.kfs.module.bc.document.BudgetConstructionDocument) 276 */ 277 @Transactional 278 protected void calculateMonthlyBenefits(BudgetConstructionDocument bcDoc) { 279 280 // allow benefits calculation if document's account is not salary setting only lines 281 bcDoc.setMonthlyBenefitsCalcNeeded(false); 282 if (!bcDoc.isSalarySettingOnly()) { 283 284 // pbgl lines are saved at this point, calc benefits 285 benefitsCalculationService.calculateMonthlyBudgetConstructionGeneralLedgerBenefits(bcDoc.getDocumentNumber(), bcDoc.getUniversityFiscalYear(), bcDoc.getChartOfAccountsCode(), bcDoc.getAccountNumber(), bcDoc.getSubAccountNumber()); 286 287 // write global message on calc success 288 GlobalVariables.getMessageList().add(BCKeyConstants.MESSAGE_BENEFITS_MONTHLY_CALCULATED); 289 } 290 } 291 292 /** 293 * Does sanity checks for null document object and null documentNumber 294 * 295 * @param document 296 */ 297 @NonTransactional 298 protected void checkForNulls(Document document) { 299 if (document == null) { 300 throw new IllegalArgumentException("invalid (null) document"); 301 } 302 else if (document.getDocumentNumber() == null) { 303 throw new IllegalStateException("invalid (null) documentHeaderId"); 304 } 305 } 306 307 /** 308 * Runs validation and persists a document to the database. 309 * 310 * @param document 311 * @param event 312 * @throws WorkflowException 313 * @throws ValidationException 314 */ 315 @Transactional 316 public void validateAndPersistDocument(Document document, KualiDocumentEvent event) throws ValidationException { 317 if (document == null) { 318 LOG.error("document passed to validateAndPersist was null"); 319 throw new IllegalArgumentException("invalid (null) document"); 320 } 321 LOG.info("validating and preparing to persist document " + document.getDocumentNumber()); 322 323 // runs business rules event.validate() and creates rule instance and runs rule method recursively 324 document.validateBusinessRules(event); 325 326 // calls overriden method for specific document for anything that needs to happen before the save 327 // currently nothing for BC document 328 document.prepareForSave(event); 329 330 // save the document to the database 331 try { 332 LOG.info("storing document " + document.getDocumentNumber()); 333 documentDao.save(document); 334 } 335 catch (OptimisticLockingFailureException e) { 336 LOG.error("exception encountered on store of document " + e.getMessage()); 337 throw e; 338 } 339 340 document.postProcessSave(event); 341 342 343 } 344 345 /** 346 * @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#validateDocument(org.kuali.rice.kns.document.Document) 347 */ 348 @Transactional 349 public void validateDocument(Document document) throws ValidationException { 350 if (document == null) { 351 LOG.error("document passed to validateDocument was null"); 352 throw new IllegalArgumentException("invalid (null) document"); 353 } 354 LOG.info("validating document " + document.getDocumentNumber()); 355 document.validateBusinessRules(new SaveDocumentEvent(document)); 356 357 } 358 359 /** 360 * @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#getPBGLSalarySettingRows(org.kuali.kfs.module.bc.document.BudgetConstructionDocument) 361 */ 362 @Transactional 363 public List<PendingBudgetConstructionGeneralLedger> getPBGLSalarySettingRows(BudgetConstructionDocument bcDocument) { 364 365 List<String> ssObjects = budgetConstructionDao.getDetailSalarySettingLaborObjects(bcDocument.getUniversityFiscalYear(), bcDocument.getChartOfAccountsCode()); 366 ssObjects.add(KFSConstants.BudgetConstructionConstants.OBJECT_CODE_2PLG); 367 List<PendingBudgetConstructionGeneralLedger> pbglSalarySettingRows = budgetConstructionDao.getPBGLSalarySettingRows(bcDocument.getDocumentNumber(), ssObjects); 368 369 return pbglSalarySettingRows; 370 } 371 372 /** 373 * @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#addOrUpdatePBGLRow(org.kuali.kfs.module.bc.document.BudgetConstructionDocument, 374 * org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionGeneralLedger) 375 */ 376 @NonTransactional 377 public BudgetConstructionDocument addOrUpdatePBGLRow(BudgetConstructionDocument bcDoc, PendingBudgetConstructionGeneralLedger sourceRow) { 378 379 List<PendingBudgetConstructionGeneralLedger> expenditureRows = bcDoc.getPendingBudgetConstructionGeneralLedgerExpenditureLines(); 380 381 // add or update salary setting row to set in memory - this assumes at least one row in the set 382 // we can't even do salary setting without at least one salary detail row 383 int index = 0; 384 boolean insertNeeded = true; 385 for (PendingBudgetConstructionGeneralLedger expRow : expenditureRows) { 386 String expRowKey = expRow.getFinancialObjectCode() + expRow.getFinancialSubObjectCode(); 387 String sourceRowKey = sourceRow.getFinancialObjectCode() + sourceRow.getFinancialSubObjectCode(); 388 if (expRowKey.compareToIgnoreCase(sourceRowKey) == 0) { 389 // update 390 insertNeeded = false; 391 expRow.setAccountLineAnnualBalanceAmount(sourceRow.getAccountLineAnnualBalanceAmount()); 392 expRow.setPersistedAccountLineAnnualBalanceAmount(sourceRow.getAccountLineAnnualBalanceAmount()); 393 expRow.setVersionNumber(sourceRow.getVersionNumber()); 394 break; 395 } 396 else { 397 if (expRowKey.compareToIgnoreCase(sourceRowKey) > 0) { 398 // insert here - drop out 399 break; 400 } 401 } 402 index++; 403 } 404 if (insertNeeded) { 405 // insert the row 406 sourceRow.setPersistedAccountLineAnnualBalanceAmount(sourceRow.getAccountLineAnnualBalanceAmount()); 407 expenditureRows.add(index, sourceRow); 408 } 409 410 return bcDoc; 411 } 412 413 /** 414 * Reloads benefits target accounting lines. Usually called right after an annual benefits calculation and the display needs 415 * updated with a fresh copy from the database. All old row versions are removed and database row versions are inserted in the 416 * list in the correct order. 417 * 418 * @param bcDoc 419 */ 420 @Transactional 421 protected void reloadBenefitsLines(BudgetConstructionDocument bcDoc) { 422 423 // get list of potential fringe objects to use as an in query param 424 Map<String, Object> fieldValues = new HashMap<String, Object>(); 425 fieldValues.put(KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR, bcDoc.getUniversityFiscalYear()); 426 fieldValues.put(KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE, bcDoc.getChartOfAccountsCode()); 427 428 List<LaborLedgerBenefitsCalculation> benefitsCalculation = kualiModuleService.getResponsibleModuleService(LaborLedgerBenefitsCalculation.class).getExternalizableBusinessObjectsList(LaborLedgerBenefitsCalculation.class, fieldValues); 429 430 List<String> fringeObjects = new ArrayList<String>(); 431 for (LaborLedgerBenefitsCalculation element : benefitsCalculation) { 432 fringeObjects.add(element.getPositionFringeBenefitObjectCode()); 433 } 434 435 List<PendingBudgetConstructionGeneralLedger> dbPBGLFringeLines = budgetConstructionDao.getDocumentPBGLFringeLines(bcDoc.getDocumentNumber(), fringeObjects); 436 List<PendingBudgetConstructionGeneralLedger> docPBGLExpLines = bcDoc.getPendingBudgetConstructionGeneralLedgerExpenditureLines(); 437 438 // holds the request sums of removed, added records and used to adjust the document expenditure request total 439 KualiInteger docRequestTotals = KualiInteger.ZERO; 440 KualiInteger dbRequestTotals = KualiInteger.ZERO; 441 442 // remove the current set of fringe lines 443 ListIterator docLines = docPBGLExpLines.listIterator(); 444 while (docLines.hasNext()) { 445 PendingBudgetConstructionGeneralLedger docLine = (PendingBudgetConstructionGeneralLedger) docLines.next(); 446 if (fringeObjects.contains(docLine.getFinancialObjectCode())) { 447 docRequestTotals = docRequestTotals.add(docLine.getAccountLineAnnualBalanceAmount()); 448 docLines.remove(); 449 } 450 } 451 452 // add the dbset of fringe lines, if any 453 if (dbPBGLFringeLines != null && !dbPBGLFringeLines.isEmpty()) { 454 455 if (docPBGLExpLines == null || docPBGLExpLines.isEmpty()) { 456 docPBGLExpLines.addAll(dbPBGLFringeLines); 457 } 458 else { 459 ListIterator dbLines = dbPBGLFringeLines.listIterator(); 460 docLines = docPBGLExpLines.listIterator(); 461 PendingBudgetConstructionGeneralLedger dbLine = (PendingBudgetConstructionGeneralLedger) dbLines.next(); 462 PendingBudgetConstructionGeneralLedger docLine = (PendingBudgetConstructionGeneralLedger) docLines.next(); 463 boolean dbDone = false; 464 boolean docDone = false; 465 while (!dbDone) { 466 if (docDone || docLine.getFinancialObjectCode().compareToIgnoreCase(dbLine.getFinancialObjectCode()) > 0) { 467 if (!docDone) { 468 docLine = (PendingBudgetConstructionGeneralLedger) docLines.previous(); 469 } 470 dbRequestTotals = dbRequestTotals.add(dbLine.getAccountLineAnnualBalanceAmount()); 471 dbLine.setPersistedAccountLineAnnualBalanceAmount(dbLine.getAccountLineAnnualBalanceAmount()); 472 this.populatePBGLLine(dbLine); 473 docLines.add(dbLine); 474 if (!docDone) { 475 docLine = (PendingBudgetConstructionGeneralLedger) docLines.next(); 476 } 477 if (dbLines.hasNext()) { 478 dbLine = (PendingBudgetConstructionGeneralLedger) dbLines.next(); 479 } 480 else { 481 dbDone = true; 482 } 483 } 484 else { 485 if (docLines.hasNext()) { 486 docLine = (PendingBudgetConstructionGeneralLedger) docLines.next(); 487 } 488 else { 489 docDone = true; 490 } 491 } 492 } 493 } 494 } 495 496 // adjust the request total for the removed and added recs 497 bcDoc.setExpenditureAccountLineAnnualBalanceAmountTotal(bcDoc.getExpenditureAccountLineAnnualBalanceAmountTotal().add(dbRequestTotals.subtract(docRequestTotals))); 498 499 } 500 501 /** 502 * @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#populatePBGLLine(org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionGeneralLedger) 503 */ 504 @Transactional 505 public void populatePBGLLine(PendingBudgetConstructionGeneralLedger line) { 506 507 final List REFRESH_FIELDS; 508 if (StringUtils.isNotBlank(line.getFinancialSubObjectCode())) { 509 REFRESH_FIELDS = Collections.unmodifiableList(Arrays.asList(new String[] { KFSPropertyConstants.FINANCIAL_OBJECT, KFSPropertyConstants.FINANCIAL_SUB_OBJECT, BCPropertyConstants.BUDGET_CONSTRUCTION_MONTHLY })); 510 } 511 else { 512 REFRESH_FIELDS = Collections.unmodifiableList(Arrays.asList(new String[] { KFSPropertyConstants.FINANCIAL_OBJECT, BCPropertyConstants.BUDGET_CONSTRUCTION_MONTHLY })); 513 } 514 persistenceService.retrieveReferenceObjects(line, REFRESH_FIELDS); 515 516 } 517 518 /** 519 * @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#getPendingBudgetConstructionAppointmentFundingRequestSum(org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionGeneralLedger) 520 */ 521 @Transactional 522 public KualiInteger getPendingBudgetConstructionAppointmentFundingRequestSum(PendingBudgetConstructionGeneralLedger salaryDetailLine) { 523 return budgetConstructionDao.getPendingBudgetConstructionAppointmentFundingRequestSum(salaryDetailLine); 524 } 525 526 /** 527 * @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#isBudgetableDocument(org.kuali.kfs.module.bc.businessobject.BudgetConstructionHeader) 528 */ 529 @NonTransactional 530 public boolean isBudgetableDocument(BudgetConstructionHeader bcHeader) { 531 if (bcHeader == null) { 532 return false; 533 } 534 535 Integer budgetYear = bcHeader.getUniversityFiscalYear(); 536 Account account = bcHeader.getAccount(); 537 boolean isBudgetableAccount = this.isBudgetableAccount(budgetYear, account, true); 538 539 if (isBudgetableAccount) { 540 SubAccount subAccount = bcHeader.getSubAccount(); 541 String subAccountNumber = bcHeader.getSubAccountNumber(); 542 543 return this.isBudgetableSubAccount(subAccount, subAccountNumber); 544 } 545 546 return false; 547 } 548 549 @NonTransactional 550 public boolean isBudgetableDocumentNoWagesCheck(BudgetConstructionHeader bcHeader) { 551 if (bcHeader == null) { 552 return false; 553 } 554 555 Integer budgetYear = bcHeader.getUniversityFiscalYear(); 556 Account account = bcHeader.getAccount(); 557 boolean isBudgetableAccount = this.isBudgetableAccount(budgetYear, account, false); 558 559 if (isBudgetableAccount) { 560 SubAccount subAccount = bcHeader.getSubAccount(); 561 String subAccountNumber = bcHeader.getSubAccountNumber(); 562 563 return this.isBudgetableSubAccount(subAccount, subAccountNumber); 564 } 565 566 return false; 567 } 568 569 /** 570 * @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#isBudgetableDocument(org.kuali.kfs.module.bc.document.BudgetConstructionDocument) 571 */ 572 @NonTransactional 573 public boolean isBudgetableDocument(BudgetConstructionDocument document) { 574 if (document == null) { 575 return false; 576 } 577 578 Integer budgetYear = document.getUniversityFiscalYear(); 579 Account account = document.getAccount(); 580 boolean isBudgetableAccount = this.isBudgetableAccount(budgetYear, account, true); 581 582 if (isBudgetableAccount) { 583 SubAccount subAccount = document.getSubAccount(); 584 String subAccountNumber = document.getSubAccountNumber(); 585 586 return this.isBudgetableSubAccount(subAccount, subAccountNumber); 587 } 588 589 return false; 590 } 591 592 /** 593 * @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#isBudgetableDocumentNoWagesCheck(org.kuali.kfs.module.bc.document.BudgetConstructionDocument) 594 */ 595 @NonTransactional 596 public boolean isBudgetableDocumentNoWagesCheck(BudgetConstructionDocument document) { 597 if (document == null) { 598 return false; 599 } 600 601 Integer budgetYear = document.getUniversityFiscalYear(); 602 Account account = document.getAccount(); 603 boolean isBudgetableAccount = this.isBudgetableAccount(budgetYear, account, false); 604 605 if (isBudgetableAccount) { 606 SubAccount subAccount = document.getSubAccount(); 607 String subAccountNumber = document.getSubAccountNumber(); 608 609 return this.isBudgetableSubAccount(subAccount, subAccountNumber); 610 } 611 612 return false; 613 } 614 615 /** 616 * @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#isAssociatedWithBudgetableDocument(org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding) 617 */ 618 @NonTransactional 619 public boolean isAssociatedWithBudgetableDocument(PendingBudgetConstructionAppointmentFunding appointmentFunding) { 620 BudgetConstructionHeader bcHeader = this.getBudgetConstructionHeader(appointmentFunding); 621 return this.isBudgetableDocument(bcHeader); 622 } 623 624 /** 625 * @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#isBudgetableAccount(java.lang.Integer, 626 * org.kuali.kfs.coa.businessobject.Account) 627 */ 628 @NonTransactional 629 public boolean isBudgetableAccount(Integer budgetYear, Account account, boolean isWagesCheck) { 630 if (budgetYear == null || account == null) { 631 return false; 632 } 633 634 // account cannot be closed. 635 if (!account.isActive()) { 636 return false; 637 } 638 639 // account cannot be expired before beginning of 6th accounting period, 2 years before budget construction fiscal year. 640 Calendar expDate = BudgetConstructionRuleUtil.getNoBudgetAllowedExpireDate(budgetYear); 641 if (account.isExpired(expDate)) { 642 return false; 643 } 644 645 // account cannot be a cash control account 646 if (StringUtils.equals(account.getBudgetRecordingLevelCode(), BCConstants.BUDGET_RECORDING_LEVEL_N)) { 647 return false; 648 } 649 650 // this check is needed for salary setting 651 if (isWagesCheck) { 652 653 // account must be flagged as wages allowed 654 SubFundGroup subFundGroup = account.getSubFundGroup(); 655 if (subFundGroup == null || !subFundGroup.isSubFundGroupWagesIndicator()) { 656 return false; 657 } 658 } 659 660 return true; 661 } 662 663 /** 664 * @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#isBudgetableSubAccount(org.kuali.kfs.coa.businessobject.SubAccount, 665 * java.lang.String) 666 */ 667 @NonTransactional 668 public boolean isBudgetableSubAccount(SubAccount subAccount, String subAccountNumber) { 669 if (StringUtils.isNotEmpty(subAccountNumber) || StringUtils.equals(subAccountNumber, KFSConstants.getDashSubAccountNumber())) { 670 return true; 671 } 672 673 // sub account must exist and be active. 674 if (subAccount == null || !subAccount.isActive()) { 675 return false; 676 } 677 678 // sub account must not be flagged cost share 679 A21SubAccount a21SubAccount = subAccount.getA21SubAccount(); 680 if (ObjectUtils.isNotNull(a21SubAccount) && StringUtils.equals(a21SubAccount.getSubAccountTypeCode(), KFSConstants.SubAccountType.COST_SHARE)) { 681 return false; 682 } 683 684 return true; 685 } 686 687 /** 688 * @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#isAccountReportsExist(java.lang.String, java.lang.String) 689 */ 690 @Transactional 691 public boolean isAccountReportsExist(String chartOfAccountsCode, String accountNumber) { 692 693 BudgetConstructionAccountReports accountReports = (BudgetConstructionAccountReports) budgetConstructionDao.getAccountReports(chartOfAccountsCode, accountNumber); 694 if (accountReports == null) { 695 return false; 696 } 697 else { 698 return true; 699 } 700 } 701 702 /** 703 * @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#updatePendingBudgetGeneralLedger(org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding, 704 * org.kuali.rice.kns.util.KualiInteger) 705 */ 706 @Transactional 707 public void updatePendingBudgetGeneralLedger(PendingBudgetConstructionAppointmentFunding appointmentFunding, KualiInteger updateAmount) { 708 BudgetConstructionHeader budgetConstructionHeader = this.getBudgetConstructionHeader(appointmentFunding); 709 if (budgetConstructionHeader == null) { 710 return; 711 } 712 713 PendingBudgetConstructionGeneralLedger pendingRecord = this.getPendingBudgetConstructionGeneralLedger(budgetConstructionHeader, appointmentFunding, updateAmount, false); 714 businessObjectService.save(pendingRecord); 715 } 716 717 /** 718 * @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#updatePendingBudgetGeneralLedgerPlug(org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding, 719 * org.kuali.rice.kns.util.KualiInteger) 720 */ 721 @Transactional 722 public void updatePendingBudgetGeneralLedgerPlug(PendingBudgetConstructionAppointmentFunding appointmentFunding, KualiInteger updateAmount) { 723 if (updateAmount == null) { 724 throw new IllegalArgumentException("The update amount cannot be null"); 725 } 726 727 BudgetConstructionHeader budgetConstructionHeader = this.getBudgetConstructionHeader(appointmentFunding); 728 if (budgetConstructionHeader == null) { 729 return; 730 } 731 732 if (this.canUpdatePlugRecord(appointmentFunding)) { 733 PendingBudgetConstructionGeneralLedger plugRecord = this.getPendingBudgetConstructionGeneralLedger(budgetConstructionHeader, appointmentFunding, updateAmount, true); 734 735 KualiInteger annualBalanceAmount = plugRecord.getAccountLineAnnualBalanceAmount(); 736 KualiInteger beginningBalanceAmount = plugRecord.getFinancialBeginningBalanceLineAmount(); 737 738 if ((annualBalanceAmount == null || annualBalanceAmount.isZero()) && (beginningBalanceAmount == null || beginningBalanceAmount.isZero())) { 739 businessObjectService.delete(plugRecord); 740 } 741 else { 742 businessObjectService.save(plugRecord); 743 } 744 } 745 } 746 747 /** 748 * @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#updatePendingBudgetGeneralLedgerPlug(org.kuali.kfs.module.bc.document.BudgetConstructionDocument, 749 * org.kuali.rice.kns.util.KualiInteger) 750 */ 751 @Transactional 752 public PendingBudgetConstructionGeneralLedger updatePendingBudgetGeneralLedgerPlug(BudgetConstructionDocument bcDoc, KualiInteger updateAmount) { 753 754 String twoPlugKey = KFSConstants.BudgetConstructionConstants.OBJECT_CODE_2PLG + KFSConstants.getDashFinancialSubObjectCode(); 755 List<PendingBudgetConstructionGeneralLedger> expenditureRows = bcDoc.getPendingBudgetConstructionGeneralLedgerExpenditureLines(); 756 PendingBudgetConstructionGeneralLedger twoPlugRow = null; 757 758 // update or insert the 2plg row - this assumes at least one row in the set 759 // we can't even do salary setting without at least one detail line 760 int index = 0; 761 boolean insertNeeded = true; 762 for (PendingBudgetConstructionGeneralLedger expRow : expenditureRows) { 763 String expRowKey = expRow.getFinancialObjectCode() + expRow.getFinancialSubObjectCode(); 764 if (expRowKey.compareToIgnoreCase(twoPlugKey) == 0) { 765 766 // update the existing row 767 insertNeeded = false; 768 expRow.setAccountLineAnnualBalanceAmount(expRow.getAccountLineAnnualBalanceAmount().add(updateAmount.negated())); 769 expRow.setPersistedAccountLineAnnualBalanceAmount(expRow.getAccountLineAnnualBalanceAmount()); 770 businessObjectService.save(expRow); 771 expRow.refresh(); 772 twoPlugRow = expRow; 773 break; 774 } 775 else { 776 if (expRowKey.compareToIgnoreCase(twoPlugKey) > 0) { 777 778 // case where offsetting salary setting updates under different object codes - insert a new row here 779 break; 780 } 781 } 782 index++; 783 } 784 if (insertNeeded) { 785 786 // do insert in the middle or at end of list 787 String objectCode = KFSConstants.BudgetConstructionConstants.OBJECT_CODE_2PLG; 788 String subObjectCode = KFSConstants.getDashFinancialSubObjectCode(); 789 String objectTypeCode = optionsService.getOptions(bcDoc.getUniversityFiscalYear()).getFinObjTypeExpenditureexpCd(); 790 791 PendingBudgetConstructionGeneralLedger pendingRecord = new PendingBudgetConstructionGeneralLedger(); 792 793 pendingRecord.setDocumentNumber(bcDoc.getDocumentNumber()); 794 pendingRecord.setUniversityFiscalYear(bcDoc.getUniversityFiscalYear()); 795 pendingRecord.setChartOfAccountsCode(bcDoc.getChartOfAccountsCode()); 796 pendingRecord.setAccountNumber(bcDoc.getAccountNumber()); 797 pendingRecord.setSubAccountNumber(bcDoc.getSubAccountNumber()); 798 799 pendingRecord.setFinancialObjectCode(objectCode); 800 pendingRecord.setFinancialSubObjectCode(subObjectCode); 801 pendingRecord.setFinancialBalanceTypeCode(KFSConstants.BALANCE_TYPE_BASE_BUDGET); 802 pendingRecord.setFinancialObjectTypeCode(objectTypeCode); 803 804 pendingRecord.setFinancialBeginningBalanceLineAmount(KualiInteger.ZERO); 805 pendingRecord.setAccountLineAnnualBalanceAmount(updateAmount); 806 807 // store and add to memory set 808 pendingRecord.setPersistedAccountLineAnnualBalanceAmount(pendingRecord.getAccountLineAnnualBalanceAmount()); 809 businessObjectService.save(pendingRecord); 810 expenditureRows.add(index, pendingRecord); 811 twoPlugRow = pendingRecord; 812 bcDoc.setContainsTwoPlug(true); 813 } 814 815 bcDoc.setExpenditureAccountLineAnnualBalanceAmountTotal(bcDoc.getExpenditureAccountLineAnnualBalanceAmountTotal().add(updateAmount.negated())); 816 return twoPlugRow; 817 } 818 819 /** 820 * @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#getBudgetConstructionHeader(org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding) 821 */ 822 @NonTransactional 823 public BudgetConstructionHeader getBudgetConstructionHeader(PendingBudgetConstructionAppointmentFunding appointmentFunding) { 824 String chartOfAccountsCode = appointmentFunding.getChartOfAccountsCode(); 825 String accountNumber = appointmentFunding.getAccountNumber(); 826 String subAccountNumber = appointmentFunding.getSubAccountNumber(); 827 Integer fiscalYear = appointmentFunding.getUniversityFiscalYear(); 828 829 return this.getByCandidateKey(chartOfAccountsCode, accountNumber, subAccountNumber, fiscalYear); 830 } 831 832 /** 833 * @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#getBudgetConstructionDocument(org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding) 834 */ 835 @NonTransactional 836 public BudgetConstructionDocument getBudgetConstructionDocument(PendingBudgetConstructionAppointmentFunding appointmentFunding) { 837 Map<String, Object> fieldValues = new HashMap<String, Object>(); 838 fieldValues.put(KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR, appointmentFunding.getUniversityFiscalYear()); 839 fieldValues.put(KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE, appointmentFunding.getChartOfAccountsCode()); 840 fieldValues.put(KFSPropertyConstants.ACCOUNT_NUMBER, appointmentFunding.getAccountNumber()); 841 fieldValues.put(KFSPropertyConstants.SUB_ACCOUNT_NUMBER, appointmentFunding.getSubAccountNumber()); 842 843 // fiscalyear, chart, account, subaccount is a candidate key for BC document 844 // This should not need the special handling and just return the first (only document) in the collection 845 Collection<BudgetConstructionDocument> documents = businessObjectService.findMatching(BudgetConstructionDocument.class, fieldValues); 846 for (BudgetConstructionDocument document : documents) { 847 try { 848 return (BudgetConstructionDocument) documentService.getByDocumentHeaderId(document.getDocumentHeader().getDocumentNumber()); 849 } 850 catch (WorkflowException e) { 851 throw new RuntimeException("Fail to retrieve the document for appointment funding" + appointmentFunding, e); 852 } 853 } 854 855 return null; 856 } 857 858 /** 859 * @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#getBudgetConstructionDocument(org.kuali.kfs.module.bc.businessobject.SalarySettingExpansion) 860 */ 861 @NonTransactional 862 public BudgetConstructionDocument getBudgetConstructionDocument(SalarySettingExpansion salarySettingExpansion) { 863 try { 864 return (BudgetConstructionDocument) documentService.getByDocumentHeaderId(salarySettingExpansion.getDocumentNumber()); 865 } 866 catch (WorkflowException e) { 867 throw new RuntimeException("Fail to retrieve the document for salary expansion" + salarySettingExpansion, e); 868 } 869 } 870 871 /** 872 * determine whether the plug line can be updated or created. If the given appointment funding is in the plug override mode or 873 * it associates with a contract and grant account, then no plug can be updated or created 874 * 875 * @param appointmentFunding the given appointment funding 876 * @return true if the plug line can be updated or created; otherwise, false 877 */ 878 @Transactional 879 protected boolean canUpdatePlugRecord(PendingBudgetConstructionAppointmentFunding appointmentFunding) { 880 // no plug if the override mode is enabled 881 if (appointmentFunding.isOverride2PlugMode()) { 882 return false; 883 } 884 885 Account account = appointmentFunding.getAccount(); 886 887 // no plug for the account with the sub groups setup as a system parameter 888 if (BudgetParameterFinder.getNotGenerate2PlgSubFundGroupCodes().contains(account.getSubFundGroupCode())) { 889 return false; 890 } 891 892 // no plug for the contract and grant account 893 if (account.isForContractsAndGrants()) { 894 return false; 895 } 896 897 return true; 898 } 899 900 901 /** 902 * get a pending budget construction GL record, and set its to the given update amount if it exists in database; otherwise, 903 * create it with the given information 904 * 905 * @param budgetConstructionHeader the budget construction header of the pending budget construction GL record 906 * @param appointmentFunding the appointment funding associated with the pending budget construction GL record 907 * @param updateAmount the amount being used to update the retrieved pending budget construction GL record 908 * @param is2PLG the flag used to instrcut to retrieve a pending budget construction GL plug record 909 * @return a pending budget construction GL record if any; otherwise, create one with the given information 910 */ 911 @Transactional 912 protected PendingBudgetConstructionGeneralLedger getPendingBudgetConstructionGeneralLedger(BudgetConstructionHeader budgetConstructionHeader, PendingBudgetConstructionAppointmentFunding appointmentFunding, KualiInteger updateAmount, boolean is2PLG) { 913 if (budgetConstructionHeader == null) { 914 throw new IllegalArgumentException("The given budget construction document header cannot be null"); 915 } 916 917 if (appointmentFunding == null) { 918 throw new IllegalArgumentException("The given pending budget appointment funding cannot be null"); 919 } 920 921 if (updateAmount == null) { 922 throw new IllegalArgumentException("The update amount cannot be null"); 923 } 924 925 PendingBudgetConstructionGeneralLedger pendingRecord = this.retrievePendingBudgetConstructionGeneralLedger(budgetConstructionHeader, appointmentFunding, is2PLG); 926 927 if (pendingRecord != null) { 928 KualiInteger newAnnaulBalanceAmount = pendingRecord.getAccountLineAnnualBalanceAmount().add(updateAmount); 929 pendingRecord.setAccountLineAnnualBalanceAmount(newAnnaulBalanceAmount); 930 } 931 else if (!is2PLG || (is2PLG && updateAmount.isNonZero())) { 932 // initialize a new pending record if not plug line or plug line not zero 933 934 Integer budgetYear = appointmentFunding.getUniversityFiscalYear(); 935 String objectCode = is2PLG ? KFSConstants.BudgetConstructionConstants.OBJECT_CODE_2PLG : appointmentFunding.getFinancialObjectCode(); 936 String subObjectCode = is2PLG ? KFSConstants.getDashFinancialSubObjectCode() : appointmentFunding.getFinancialSubObjectCode(); 937 String objectTypeCode = optionsService.getOptions(budgetYear).getFinObjTypeExpenditureexpCd(); 938 939 pendingRecord = new PendingBudgetConstructionGeneralLedger(); 940 941 pendingRecord.setDocumentNumber(budgetConstructionHeader.getDocumentNumber()); 942 pendingRecord.setUniversityFiscalYear(appointmentFunding.getUniversityFiscalYear()); 943 pendingRecord.setChartOfAccountsCode(appointmentFunding.getChartOfAccountsCode()); 944 pendingRecord.setAccountNumber(appointmentFunding.getAccountNumber()); 945 pendingRecord.setSubAccountNumber(appointmentFunding.getSubAccountNumber()); 946 947 pendingRecord.setFinancialObjectCode(objectCode); 948 pendingRecord.setFinancialSubObjectCode(subObjectCode); 949 pendingRecord.setFinancialBalanceTypeCode(KFSConstants.BALANCE_TYPE_BASE_BUDGET); 950 pendingRecord.setFinancialObjectTypeCode(objectTypeCode); 951 952 pendingRecord.setFinancialBeginningBalanceLineAmount(KualiInteger.ZERO); 953 pendingRecord.setAccountLineAnnualBalanceAmount(updateAmount); 954 } 955 956 return pendingRecord; 957 } 958 959 /** 960 * retrieve a pending budget construction GL record based on the given infromation 961 * 962 * @param budgetConstructionHeader the budget construction header of the pending budget construction GL record to be retrieved 963 * @param appointmentFunding the appointment funding associated with the pending budget construction GL record to be retrieved 964 * @param is2PLG the flag used to instrcut to retrieve a pending budget construction GL plug record 965 * @return a pending budget construction GL record if any; otherwise, null 966 */ 967 @NonTransactional 968 protected PendingBudgetConstructionGeneralLedger retrievePendingBudgetConstructionGeneralLedger(BudgetConstructionHeader budgetConstructionHeader, PendingBudgetConstructionAppointmentFunding appointmentFunding, boolean is2PLG) { 969 String objectCode = is2PLG ? KFSConstants.BudgetConstructionConstants.OBJECT_CODE_2PLG : appointmentFunding.getFinancialObjectCode(); 970 String subObjectCode = is2PLG ? KFSConstants.getDashFinancialSubObjectCode() : appointmentFunding.getFinancialSubObjectCode(); 971 972 Map<String, Object> searchCriteria = new HashMap<String, Object>(); 973 974 searchCriteria.put(KFSPropertyConstants.DOCUMENT_NUMBER, budgetConstructionHeader.getDocumentNumber()); 975 searchCriteria.put(KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR, budgetConstructionHeader.getUniversityFiscalYear()); 976 searchCriteria.put(KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE, budgetConstructionHeader.getChartOfAccountsCode()); 977 searchCriteria.put(KFSPropertyConstants.ACCOUNT_NUMBER, budgetConstructionHeader.getAccountNumber()); 978 searchCriteria.put(KFSPropertyConstants.SUB_ACCOUNT_NUMBER, budgetConstructionHeader.getSubAccountNumber()); 979 searchCriteria.put(KFSPropertyConstants.FINANCIAL_BALANCE_TYPE_CODE, KFSConstants.BALANCE_TYPE_BASE_BUDGET); 980 searchCriteria.put(KFSPropertyConstants.FINANCIAL_OBJECT_TYPE_CODE, optionsService.getOptions(appointmentFunding.getUniversityFiscalYear()).getFinObjTypeExpenditureexpCd()); 981 982 searchCriteria.put(KFSPropertyConstants.FINANCIAL_OBJECT_CODE, objectCode); 983 searchCriteria.put(KFSPropertyConstants.FINANCIAL_SUB_OBJECT_CODE, subObjectCode); 984 985 return (PendingBudgetConstructionGeneralLedger) businessObjectService.findByPrimaryKey(PendingBudgetConstructionGeneralLedger.class, searchCriteria); 986 } 987 988 /** 989 * @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#retrievePendingBudgetConstructionGeneralLedger(org.kuali.kfs.module.bc.businessobject.BudgetConstructionHeader) 990 */ 991 @NonTransactional 992 public List<PendingBudgetConstructionGeneralLedger> retrievePendingBudgetConstructionGeneralLedger(BudgetConstructionHeader budgetConstructionHeader) { 993 Map<String, Object> searchCriteria = new HashMap<String, Object>(); 994 995 searchCriteria.put(KFSPropertyConstants.DOCUMENT_NUMBER, budgetConstructionHeader.getDocumentNumber()); 996 searchCriteria.put(KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR, budgetConstructionHeader.getUniversityFiscalYear()); 997 searchCriteria.put(KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE, budgetConstructionHeader.getChartOfAccountsCode()); 998 searchCriteria.put(KFSPropertyConstants.ACCOUNT_NUMBER, budgetConstructionHeader.getAccountNumber()); 999 searchCriteria.put(KFSPropertyConstants.SUB_ACCOUNT_NUMBER, budgetConstructionHeader.getSubAccountNumber()); 1000 1001 return (List<PendingBudgetConstructionGeneralLedger>) businessObjectService.findMatching(PendingBudgetConstructionGeneralLedger.class, searchCriteria); 1002 } 1003 1004 /** 1005 * @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#retrieveOrBuildAccountOrganizationHierarchy(java.lang.Integer, 1006 * java.lang.String, java.lang.String) 1007 */ 1008 @Transactional 1009 public List<BudgetConstructionAccountOrganizationHierarchy> retrieveOrBuildAccountOrganizationHierarchy(Integer universityFiscalYear, String chartOfAccountsCode, String accountNumber) { 1010 1011 List<BudgetConstructionAccountOrganizationHierarchy> accountOrgHier = new ArrayList<BudgetConstructionAccountOrganizationHierarchy>(); 1012 BudgetConstructionAccountReports accountReports = (BudgetConstructionAccountReports) budgetConstructionDao.getAccountReports(chartOfAccountsCode, accountNumber); 1013 if (accountReports != null) { 1014 accountOrgHier = budgetConstructionDao.getAccountOrgHierForAccount(chartOfAccountsCode, accountNumber, universityFiscalYear); 1015 if (accountOrgHier == null || accountOrgHier.isEmpty()) { 1016 1017 // attempt to build it 1018 String[] rootNode = organizationService.getRootOrganizationCode(); 1019 String rootChart = rootNode[0]; 1020 String rootOrganization = rootNode[1]; 1021 Integer currentLevel = new Integer(1); 1022 String organizationChartOfAccountsCode = accountReports.getReportsToChartOfAccountsCode(); 1023 String organizationCode = accountReports.getReportsToOrganizationCode(); 1024 boolean overFlow = budgetConstructionDao.insertAccountIntoAccountOrganizationHierarchy(rootChart, rootOrganization, universityFiscalYear, chartOfAccountsCode, accountNumber, currentLevel, organizationChartOfAccountsCode, organizationCode); 1025 if (!overFlow) { 1026 accountOrgHier = budgetConstructionDao.getAccountOrgHierForAccount(chartOfAccountsCode, accountNumber, universityFiscalYear); 1027 } 1028 } 1029 } 1030 return accountOrgHier; 1031 } 1032 1033 /** 1034 * @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#instantiateNewBudgetConstructionDocument(org.kuali.kfs.module.bc.document.BudgetConstructionDocument) 1035 */ 1036 @Transactional 1037 public BudgetConstructionDocument instantiateNewBudgetConstructionDocument(BudgetConstructionDocument budgetConstructionDocument) throws WorkflowException { 1038 1039 budgetConstructionDocument.setOrganizationLevelChartOfAccountsCode(BCConstants.INITIAL_ORGANIZATION_LEVEL_CHART_OF_ACCOUNTS_CODE); 1040 budgetConstructionDocument.setOrganizationLevelOrganizationCode(BCConstants.INITIAL_ORGANIZATION_LEVEL_ORGANIZATION_CODE); 1041 budgetConstructionDocument.setOrganizationLevelCode(BCConstants.INITIAL_ORGANIZATION_LEVEL_CODE); 1042 budgetConstructionDocument.setBudgetTransactionLockUserIdentifier(BCConstants.DEFAULT_BUDGET_HEADER_LOCK_IDS); 1043 budgetConstructionDocument.setBudgetLockUserIdentifier(BCConstants.DEFAULT_BUDGET_HEADER_LOCK_IDS); 1044 1045 FinancialSystemDocumentHeader kualiDocumentHeader = budgetConstructionDocument.getDocumentHeader(); 1046 budgetConstructionDocument.setDocumentNumber(budgetConstructionDocument.getDocumentHeader().getDocumentNumber()); 1047 kualiDocumentHeader.setOrganizationDocumentNumber(budgetConstructionDocument.getUniversityFiscalYear().toString()); 1048 kualiDocumentHeader.setFinancialDocumentStatusCode(KFSConstants.INITIAL_KUALI_DOCUMENT_STATUS_CD); 1049 kualiDocumentHeader.setFinancialDocumentTotalAmount(KualiDecimal.ZERO); 1050 kualiDocumentHeader.setDocumentDescription(String.format("%s %d %s %s", BCConstants.BUDGET_CONSTRUCTION_DOCUMENT_DESCRIPTION, budgetConstructionDocument.getUniversityFiscalYear(), budgetConstructionDocument.getChartOfAccountsCode(), budgetConstructionDocument.getAccountNumber())); 1051 kualiDocumentHeader.setExplanation(BCConstants.BUDGET_CONSTRUCTION_DOCUMENT_DESCRIPTION); 1052 1053 budgetConstructionDao.saveBudgetConstructionDocument(budgetConstructionDocument); 1054 List<String> emptyAdHocList = new ArrayList<String>(); 1055 1056 // call route with document type configured for no route paths or post processor 1057 documentService.routeDocument(budgetConstructionDocument, "created by application UI", emptyAdHocList); 1058 1059 return budgetConstructionDocument; 1060 } 1061 1062 /** 1063 * @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#getPushPullLevelList(org.kuali.kfs.module.bc.document.BudgetConstructionDocument, 1064 * org.kuali.rice.kim.bo.Person) 1065 */ 1066 @Transactional 1067 public List<BudgetConstructionAccountOrganizationHierarchy> getPushPullLevelList(BudgetConstructionDocument bcDoc, Person person) { 1068 List<BudgetConstructionAccountOrganizationHierarchy> pushOrPullList = new ArrayList<BudgetConstructionAccountOrganizationHierarchy>(); 1069 1070 pushOrPullList.addAll(budgetConstructionDao.getAccountOrgHierForAccount(bcDoc.getChartOfAccountsCode(), bcDoc.getAccountNumber(), bcDoc.getUniversityFiscalYear())); 1071 1072 if (pushOrPullList.size() >= 1) { 1073 BudgetConstructionAccountOrganizationHierarchy levelZero = new BudgetConstructionAccountOrganizationHierarchy(); 1074 levelZero.setUniversityFiscalYear(bcDoc.getUniversityFiscalYear()); 1075 levelZero.setChartOfAccountsCode(bcDoc.getChartOfAccountsCode()); 1076 levelZero.setAccountNumber(bcDoc.getAccountNumber()); 1077 levelZero.setOrganizationLevelCode(0); 1078 levelZero.setOrganizationChartOfAccountsCode(pushOrPullList.get(0).getOrganizationChartOfAccountsCode()); 1079 levelZero.setOrganizationCode(pushOrPullList.get(0).getOrganizationCode()); 1080 pushOrPullList.add(0, levelZero); 1081 } 1082 1083 return pushOrPullList; 1084 } 1085 1086 /** 1087 * Sets the budgetConstructionDao attribute value. 1088 * 1089 * @param budgetConstructionDao The budgetConstructionDao to set. 1090 */ 1091 @NonTransactional 1092 public void setBudgetConstructionDao(BudgetConstructionDao budgetConstructionDao) { 1093 this.budgetConstructionDao = budgetConstructionDao; 1094 } 1095 1096 /** 1097 * Sets the documentService attribute value. 1098 * 1099 * @param documentService The documentService to set. 1100 */ 1101 @NonTransactional 1102 public void setDocumentService(DocumentService documentService) { 1103 this.documentService = documentService; 1104 } 1105 1106 /** 1107 * Sets the workflowDocumentService attribute value. 1108 * 1109 * @param workflowDocumentService The workflowDocumentService to set. 1110 */ 1111 @NonTransactional 1112 public void setWorkflowDocumentService(WorkflowDocumentService workflowDocumentService) { 1113 this.workflowDocumentService = workflowDocumentService; 1114 } 1115 1116 /** 1117 * Sets the documentDao attribute value. 1118 * 1119 * @param documentDao The documentDao to set. 1120 */ 1121 @NonTransactional 1122 public void setDocumentDao(DocumentDao documentDao) { 1123 this.documentDao = documentDao; 1124 } 1125 1126 1127 /** 1128 * Sets the benefitsCalculationService attribute value. 1129 * 1130 * @param benefitsCalculationService The benefitsCalculationService to set. 1131 */ 1132 @NonTransactional 1133 public void setBenefitsCalculationService(BenefitsCalculationService benefitsCalculationService) { 1134 this.benefitsCalculationService = benefitsCalculationService; 1135 } 1136 1137 1138 /** 1139 * Sets the businessObjectService attribute value. 1140 * 1141 * @param businessObjectService The businessObjectService to set. 1142 */ 1143 @NonTransactional 1144 public void setBusinessObjectService(BusinessObjectService businessObjectService) { 1145 this.businessObjectService = businessObjectService; 1146 } 1147 1148 /** 1149 * Sets the budgetParameterService attribute value. 1150 * 1151 * @param budgetParameterService The budgetParameterService to set. 1152 */ 1153 @NonTransactional 1154 public void setBudgetParameterService(BudgetParameterService budgetParameterService) { 1155 this.budgetParameterService = budgetParameterService; 1156 } 1157 1158 /** 1159 * Sets the parameterService attribute value. 1160 * 1161 * @param parameterService The parameterService to set. 1162 */ 1163 @NonTransactional 1164 public void setParameterService(ParameterService parameterService) { 1165 this.parameterService = parameterService; 1166 } 1167 1168 /** 1169 * Sets the fiscalYearFunctionControlService attribute value. 1170 * 1171 * @param fiscalYearFunctionControlService The fiscalYearFunctionControlService to set. 1172 */ 1173 @NonTransactional 1174 public void setFiscalYearFunctionControlService(FiscalYearFunctionControlService fiscalYearFunctionControlService) { 1175 this.fiscalYearFunctionControlService = fiscalYearFunctionControlService; 1176 } 1177 1178 1179 /** 1180 * Sets the optionsService attribute value. 1181 * 1182 * @param optionsService The optionsService to set. 1183 */ 1184 @NonTransactional 1185 public void setOptionsService(OptionsService optionsService) { 1186 this.optionsService = optionsService; 1187 } 1188 1189 /** 1190 * Gets the persistenceService attribute. 1191 * 1192 * @return Returns the persistenceService. 1193 */ 1194 @NonTransactional 1195 public PersistenceService getPersistenceService() { 1196 return persistenceService; 1197 } 1198 1199 /** 1200 * Sets the persistenceService attribute value. 1201 * 1202 * @param persistenceService The persistenceService to set. 1203 */ 1204 @NonTransactional 1205 public void setPersistenceService(PersistenceService persistenceService) { 1206 this.persistenceService = persistenceService; 1207 } 1208 1209 /** 1210 * Sets the organizationService attribute value. 1211 * 1212 * @param organizationService The organizationService to set. 1213 */ 1214 @NonTransactional 1215 public void setOrganizationService(OrganizationService organizationService) { 1216 this.organizationService = organizationService; 1217 } 1218 1219 /** 1220 * Sets the kualiModuleService attribute value. 1221 * 1222 * @param kualiModuleService The kualiModuleService to set. 1223 */ 1224 @NonTransactional 1225 public void setKualiModuleService(KualiModuleService kualiModuleService) { 1226 this.kualiModuleService = kualiModuleService; 1227 } 1228 1229 }