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 }