001: /*
002: * Copyright 2006-2007 The Kuali Foundation.
003: *
004: * Licensed under the Educational Community License, Version 1.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/ecl1.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.module.kra.budget.service.impl;
017:
018: import java.util.ArrayList;
019: import java.util.Collections;
020: import java.util.HashMap;
021: import java.util.Iterator;
022: import java.util.List;
023: import java.util.Map;
024:
025: import org.kuali.core.service.BusinessObjectService;
026: import org.kuali.core.util.KualiDecimal;
027: import org.kuali.core.util.KualiInteger;
028: import org.kuali.core.util.ObjectUtils;
029: import org.kuali.kfs.KFSConstants;
030: import org.kuali.kfs.KFSPropertyConstants;
031: import org.kuali.kfs.service.ParameterService;
032: import org.kuali.kfs.service.impl.ParameterConstants;
033: import org.kuali.module.kra.KraConstants;
034: import org.kuali.module.kra.budget.bo.Budget;
035: import org.kuali.module.kra.budget.bo.BudgetBaseCode;
036: import org.kuali.module.kra.budget.bo.BudgetIndirectCost;
037: import org.kuali.module.kra.budget.bo.BudgetIndirectCostLookup;
038: import org.kuali.module.kra.budget.bo.BudgetModularPeriod;
039: import org.kuali.module.kra.budget.bo.BudgetNonpersonnel;
040: import org.kuali.module.kra.budget.bo.BudgetPeriod;
041: import org.kuali.module.kra.budget.bo.BudgetTask;
042: import org.kuali.module.kra.budget.bo.BudgetTaskPeriodIndirectCost;
043: import org.kuali.module.kra.budget.bo.IndirectCostLookup;
044: import org.kuali.module.kra.budget.bo.UserAppointmentTaskPeriod;
045: import org.kuali.module.kra.budget.document.BudgetDocument;
046: import org.kuali.module.kra.budget.service.BudgetIndirectCostService;
047: import org.kuali.module.kra.budget.service.BudgetModularService;
048:
049: public class BudgetIndirectCostServiceImpl implements
050: BudgetIndirectCostService {
051:
052: private BudgetModularService budgetModularService;
053: private ParameterService parameterService;
054: private BusinessObjectService businessObjectService;
055:
056: /**
057: * Generate our task/period list items based on idc data. Each task/period list item is basically a mapping between one task and
058: * one period ordered by sequence number. This should not be called for existing budgets, because we do not want to overwrite
059: * existing data.
060: *
061: * @param BudgetDocument budgetDocument
062: */
063: private void createTaskPeriodIdcList(BudgetDocument budgetDocument) {
064: // Get our tasks and periods lists from our budget.
065: List tasks = budgetDocument.getBudget().getTasks();
066: List periods = budgetDocument.getBudget().getPeriods();
067:
068: // Get our idc object from the budget as well.
069: BudgetIndirectCost idc = budgetDocument.getBudget()
070: .getIndirectCost();
071: if (idc == null) {
072: idc = new BudgetIndirectCost(budgetDocument.getBudget()
073: .getDocumentNumber());
074: budgetDocument.getBudget().setIndirectCost(idc);
075: }
076:
077: // Now we want to loop through our tasks, and for each task we want to add
078: // multiple taskPeriod items to our idc's collection.
079: for (Iterator taskIterator = tasks.iterator(); taskIterator
080: .hasNext();) {
081: BudgetTask task = (BudgetTask) taskIterator.next();
082:
083: for (Iterator periodIterator = periods.iterator(); periodIterator
084: .hasNext();) {
085: BudgetPeriod period = (BudgetPeriod) periodIterator
086: .next();
087:
088: // Create our new taskPeriod.
089: BudgetTaskPeriodIndirectCost taskPeriod = new BudgetTaskPeriodIndirectCost();
090:
091: // Set our parameters.
092: taskPeriod
093: .setDocumentNumber(idc.getDocumentNumber() != null ? idc
094: .getDocumentNumber()
095: : null);
096: taskPeriod.setBudgetTaskSequenceNumber(task
097: .getBudgetTaskSequenceNumber());
098: taskPeriod.setBudgetPeriodSequenceNumber(period
099: .getBudgetPeriodSequenceNumber());
100: taskPeriod.setTask(task);
101: taskPeriod.setPeriod(period);
102:
103: // Make sure that this item doesn't already exist in the list.
104: if (!ObjectUtils
105: .collectionContainsObjectWithIdentitcalKey(
106: idc
107: .getBudgetTaskPeriodIndirectCostItems(),
108: taskPeriod)) {
109: // Tack it on to the collection.
110: idc.getBudgetTaskPeriodIndirectCostItems().add(
111: taskPeriod);
112: }
113: }
114: }
115:
116: // Make sure our list is sorted.
117: Collections.sort(budgetDocument.getBudget().getIndirectCost()
118: .getBudgetTaskPeriodIndirectCostItems());
119: }
120:
121: /**
122: * Calculate personnel and non-personnel agency request amounts for a given task/period. This method will loop over personnel /
123: * nonpersonnel items to gather the total amount requested for the given BudgetTaskPeriodIndirectCost (taskPeriod).
124: *
125: * @param budgetDocument
126: */
127: private KualiInteger calculateTotalDirectCost(
128: BudgetTaskPeriodIndirectCost taskPeriod,
129: BudgetDocument budgetDocument) {
130: Budget budget = budgetDocument.getBudget();
131:
132: List userAppointmentTaskPeriods = budget
133: .getAllUserAppointmentTaskPeriods(false);
134: List nonPersonnelItems = budget.getNonpersonnelItems();
135: List<BudgetNonpersonnel> temporaryNonPersonnelItems = new ArrayList<BudgetNonpersonnel>();
136:
137: // Task / Period totals for nonpersonnel, personnel and modular.
138: KualiInteger personnelTotal = calculatePersonnelTotalDirectCost(
139: userAppointmentTaskPeriods, taskPeriod,
140: temporaryNonPersonnelItems);
141: KualiInteger nonPersonnelTotal = new KualiInteger(0);
142: KualiInteger modularTotal = new KualiInteger(0);
143:
144: // Loop over nonpersonnel items to get the total amount requested for this taskPeriod.
145: for (Iterator nonPersonnelIterator = nonPersonnelItems
146: .iterator(); nonPersonnelIterator.hasNext();) {
147: BudgetNonpersonnel nonPersonnelItem = (BudgetNonpersonnel) nonPersonnelIterator
148: .next();
149:
150: // If our task and period sequence numbers match, add the value to the total.
151: if (nonPersonnelItem.getBudgetTaskSequenceNumber().equals(
152: taskPeriod.getBudgetTaskSequenceNumber())
153: && nonPersonnelItem
154: .getBudgetPeriodSequenceNumber()
155: .equals(
156: taskPeriod
157: .getBudgetPeriodSequenceNumber())) {
158: nonPersonnelTotal = nonPersonnelTotal
159: .add(nonPersonnelItem.getAgencyRequestAmount());
160: }
161: }
162:
163: // Now add temporary nonpersonnel amounts that may have been added as a side-effect of personnel
164: for (BudgetNonpersonnel tempNonpersonnelItem : temporaryNonPersonnelItems) {
165: if (tempNonpersonnelItem.getBudgetTaskSequenceNumber()
166: .equals(taskPeriod.getBudgetTaskSequenceNumber())
167: && tempNonpersonnelItem
168: .getBudgetPeriodSequenceNumber()
169: .equals(
170: taskPeriod
171: .getBudgetPeriodSequenceNumber())) {
172: nonPersonnelTotal = nonPersonnelTotal
173: .add(tempNonpersonnelItem
174: .getAgencyRequestAmount());
175: }
176: }
177:
178: // Include modular variance if we have a modular budget and there are values for module variance in the BudgetModularPeriod
179: // list.
180: if (budget.isAgencyModularIndicator()) {
181: budget.refreshReferenceObject("budgetAgency");
182: budgetModularService.generateModularBudget(budget);
183: List modularPeriods = budgetDocument.getBudget()
184: .getModularBudget().getBudgetModularPeriods();
185:
186: for (Iterator modularIterator = modularPeriods.iterator(); modularIterator
187: .hasNext();) {
188: BudgetModularPeriod modularPeriod = (BudgetModularPeriod) modularIterator
189: .next();
190:
191: // Add to the modular total only if the period matches, and the task is marked as including modular variance.
192: if (taskPeriod.getBudgetPeriodSequenceNumber().equals(
193: modularPeriod.getBudgetPeriodSequenceNumber())
194: && taskPeriod
195: .getBudgetTaskSequenceNumber()
196: .equals(
197: budget
198: .getModularBudget()
199: .getBudgetModularTaskNumber())) {
200: modularTotal = modularTotal.add(modularPeriod
201: .getModularVarianceAmount());
202: }
203: }
204: }
205:
206: // After we've iterated over all possible userAppointmentTaskPeriods and nonpersonnelItems, we set our total.
207: return personnelTotal.add(nonPersonnelTotal.add(modularTotal));
208: }
209:
210: private KualiInteger calculateBaseCost(
211: BudgetTaskPeriodIndirectCost taskPeriod,
212: BudgetDocument budgetDocument) {
213: return calculateBaseCost(taskPeriod, budgetDocument.getBudget()
214: .getIndirectCost().getBudgetBaseCode(), budgetDocument);
215: }
216:
217: /**
218: * Calculate base cost amounts based on the budget base code for a given task/period. This calculation is made based on the
219: * budget base, and follows the following logic: TDC - The value calculated in column 1 Standard Base/Modified TDC (MTDC) - TDC
220: * (Column) minus 'Excluded' Non-Personnel Expenses; Nonpersonnel Expenses (BO) have a single Nonpersonnel Object Code (BO)
221: * which has a single Nonpersonnel SubCategory Code (BO), which has an indicator called nonpersonnelMtdcExcludedIndicator which,
222: * if true, should be excluded from MTDC calculation Manual - No calculated amount, text-box shows up on UI for users to
223: * manually enter the Base s
224: *
225: * @param budgetDocument
226: */
227: private KualiInteger calculateBaseCost(
228: BudgetTaskPeriodIndirectCost taskPeriod, String baseCode,
229: BudgetDocument budgetDocument) {
230: Budget budget = budgetDocument.getBudget();
231:
232: List userAppointmentTaskPeriods = budget
233: .getAllUserAppointmentTaskPeriods(false);
234: List nonPersonnelItems = budget.getNonpersonnelItems();
235: List<BudgetNonpersonnel> temporaryNonPersonnelItems = new ArrayList<BudgetNonpersonnel>();
236:
237: KualiInteger baseCost = new KualiInteger(0);
238:
239: // Task / Period totals for nonpersonnel and personnel.
240: KualiInteger personnelTotal = new KualiInteger(0);
241: KualiInteger nonPersonnelTotal = new KualiInteger(0);
242: KualiInteger modularTotal = new KualiInteger(0);
243:
244: // If there is no base, or if the base is manual, we don't need any calculations.
245: if (baseCode != null
246: && !KraConstants.MANUAL_BASE.equals(baseCode)) {
247:
248: personnelTotal = calculatePersonnelTotalDirectCost(
249: userAppointmentTaskPeriods, taskPeriod,
250: temporaryNonPersonnelItems);
251:
252: // Loop over nonpersonnel items to get the total amount requested for this taskPeriod.
253: for (Iterator nonPersonnelIterator = nonPersonnelItems
254: .iterator(); nonPersonnelIterator.hasNext();) {
255: BudgetNonpersonnel nonPersonnelItem = (BudgetNonpersonnel) nonPersonnelIterator
256: .next();
257:
258: // If our task and period sequence numbers match, add the value to the total.
259: if (nonPersonnelItem.getBudgetTaskSequenceNumber()
260: .equals(
261: taskPeriod
262: .getBudgetTaskSequenceNumber())
263: && nonPersonnelItem
264: .getBudgetPeriodSequenceNumber()
265: .equals(
266: taskPeriod
267: .getBudgetPeriodSequenceNumber())) {
268:
269: // If we have a MTDC base, we need to exclude certain nonpersonnel items.
270: // But first, test to see if the base code is null or not MTDC.
271: if (!KraConstants.MODIFIED_TOTAL_DIRECT_COST
272: .equals(baseCode)
273: || nonPersonnelItem
274: .getNonpersonnelObjectCode() == null
275: || !nonPersonnelItem
276: .getNonpersonnelObjectCode()
277: .getNonpersonnelSubCategory()
278: .isNonpersonnelMtdcExcludedIndicator()) {
279: nonPersonnelTotal = nonPersonnelTotal
280: .add(nonPersonnelItem
281: .getAgencyRequestAmount());
282: }
283: }
284: }
285:
286: // Now add temporary nonpersonnel amounts that may have been added as a side-effect of personnel
287: for (BudgetNonpersonnel tempNonpersonnelItem : temporaryNonPersonnelItems) {
288: if (tempNonpersonnelItem.getBudgetTaskSequenceNumber()
289: .equals(
290: taskPeriod
291: .getBudgetTaskSequenceNumber())
292: && tempNonpersonnelItem
293: .getBudgetPeriodSequenceNumber()
294: .equals(
295: taskPeriod
296: .getBudgetPeriodSequenceNumber())) {
297: if (!KraConstants.MODIFIED_TOTAL_DIRECT_COST
298: .equals(baseCode)
299: || tempNonpersonnelItem
300: .getNonpersonnelObjectCode() == null
301: || !tempNonpersonnelItem
302: .getNonpersonnelObjectCode()
303: .getNonpersonnelSubCategory()
304: .isNonpersonnelMtdcExcludedIndicator()) {
305: nonPersonnelTotal = nonPersonnelTotal
306: .add(tempNonpersonnelItem
307: .getAgencyRequestAmount());
308: }
309: }
310: }
311:
312: // Include modular variance if we have a modular budget and there are values for module variance in the
313: // BudgetModularPeriod list.
314: if (budget.isAgencyModularIndicator()) {
315: budget.refreshReferenceObject("budgetAgency");
316: budgetModularService.generateModularBudget(budget);
317: List modularPeriods = budgetDocument.getBudget()
318: .getModularBudget().getBudgetModularPeriods();
319:
320: for (Iterator modularIterator = modularPeriods
321: .iterator(); modularIterator.hasNext();) {
322: BudgetModularPeriod modularPeriod = (BudgetModularPeriod) modularIterator
323: .next();
324:
325: // Add to the modular total only if the period matches, and the task is marked as including modular variance.
326: if (taskPeriod
327: .getBudgetPeriodSequenceNumber()
328: .equals(
329: modularPeriod
330: .getBudgetPeriodSequenceNumber())
331: && taskPeriod
332: .getBudgetTaskSequenceNumber()
333: .equals(
334: budget
335: .getModularBudget()
336: .getBudgetModularTaskNumber())) {
337: modularTotal = modularTotal.add(modularPeriod
338: .getModularVarianceAmount());
339: }
340: }
341: }
342:
343: // After we've iterated over all possible userAppointmentTaskPeriods and nonpersonnelItems, we set our total.
344: baseCost = personnelTotal.add(nonPersonnelTotal
345: .add(modularTotal));
346: } else if (KraConstants.MANUAL_BASE.equals(baseCode)) {
347: baseCost = new KualiInteger(taskPeriod
348: .getBudgetManualMtdcAmount() != null ? taskPeriod
349: .getBudgetManualMtdcAmount().intValue() : 0);
350: }
351:
352: return baseCost;
353: }
354:
355: private KualiInteger calculatePersonnelTotalDirectCost(
356: List userAppointmentTaskPeriods,
357: BudgetTaskPeriodIndirectCost taskPeriod,
358: List<BudgetNonpersonnel> temporaryNonpersonnelItems) {
359:
360: KualiInteger personnelTotal = new KualiInteger(0);
361:
362: List<String> graduateAssistantAppointmentTypes = parameterService
363: .getParameterValues(
364: BudgetDocument.class,
365: KraConstants.KRA_BUDGET_PERSONNEL_GRADUATE_RESEARCH_ASSISTANT_APPOINTMENT_TYPES);
366:
367: String graduateAssistentNonpersonnelCategoryCode = parameterService
368: .getParameterValue(
369: ParameterConstants.RESEARCH_ADMINISTRATION_DOCUMENT.class,
370: KraConstants.GRADUATE_ASSISTANT_NONPERSONNEL_CATEGORY_CODE);
371: String graduateAssistantNonpesonnelSubcategoryCode = parameterService
372: .getParameterValue(
373: ParameterConstants.RESEARCH_ADMINISTRATION_DOCUMENT.class,
374: KraConstants.GRADUATE_ASSISTANT_NONPERSONNEL_SUB_CATEGORY_CODE);
375:
376: // Loop over user appointments to get the total amount requested for this taskPeriod.
377: for (Iterator userAppointmentTaskPeriodIterator = userAppointmentTaskPeriods
378: .iterator(); userAppointmentTaskPeriodIterator
379: .hasNext();) {
380: UserAppointmentTaskPeriod userAppointmentTaskPeriod = (UserAppointmentTaskPeriod) userAppointmentTaskPeriodIterator
381: .next();
382:
383: // If our task and period sequence numbers match, add the value to the total.
384: if (userAppointmentTaskPeriod.getBudgetTaskSequenceNumber()
385: .equals(taskPeriod.getBudgetTaskSequenceNumber())
386: && userAppointmentTaskPeriod
387: .getBudgetPeriodSequenceNumber()
388: .equals(
389: taskPeriod
390: .getBudgetPeriodSequenceNumber())) {
391: // Have to look in a different place for grad lines.
392: if (graduateAssistantAppointmentTypes
393: .contains(userAppointmentTaskPeriod
394: .getInstitutionAppointmentTypeCode())) {
395: personnelTotal = personnelTotal
396: .add(userAppointmentTaskPeriod
397: .getAgencySalaryAmount());
398: personnelTotal = personnelTotal
399: .add(userAppointmentTaskPeriod
400: .getAgencyHealthInsuranceAmount());
401:
402: // If it is a GA, we need to add Nonpersonnel Fee Remission to Nonpersonnel list (not stored in database).
403: BudgetNonpersonnel budgetNonpersonnel = new BudgetNonpersonnel(
404: userAppointmentTaskPeriod
405: .getBudgetTaskSequenceNumber(),
406: userAppointmentTaskPeriod
407: .getBudgetPeriodSequenceNumber(),
408: graduateAssistentNonpersonnelCategoryCode,
409: graduateAssistantNonpesonnelSubcategoryCode,
410: "",
411: userAppointmentTaskPeriod
412: .getAgencyRequestedFeesAmount(),
413: userAppointmentTaskPeriod
414: .getInstitutionRequestedFeesAmount());
415: budgetNonpersonnel
416: .refreshReferenceObject("nonpersonnelObjectCode");
417: budgetNonpersonnel.getNonpersonnelObjectCode()
418: .refreshReferenceObject(
419: "nonpersonnelSubCategory");
420:
421: temporaryNonpersonnelItems.add(budgetNonpersonnel);
422:
423: } else {
424: personnelTotal = personnelTotal
425: .add(userAppointmentTaskPeriod
426: .getAgencyRequestTotalAmount());
427: personnelTotal = personnelTotal
428: .add(userAppointmentTaskPeriod
429: .getAgencyFringeBenefitTotalAmount());
430: }
431: }
432: }
433:
434: return personnelTotal;
435: }
436:
437: private KualiDecimal getIndirectCostRate(
438: BudgetTaskPeriodIndirectCost taskPeriod,
439: BudgetDocument budgetDocument,
440: boolean overrideManualRateIndicator) {
441: // First, we attempt to pull an existing rate from the taskPeriod.
442: KualiDecimal rate = taskPeriod
443: .getBudgetManualIndirectCostRate();
444:
445: // If the existing rate is not already set, we need to look it up based on budgetTaskOnCampus and budgetPurposeCode.
446: // The corresponding table is ER_IDC_LU_T.
447: if (overrideManualRateIndicator
448: || "N".equals(budgetDocument.getBudget()
449: .getIndirectCost()
450: .getBudgetManualRateIndicator())) {
451: BudgetIndirectCostLookup tempBicl = new BudgetIndirectCostLookup();
452: tempBicl.setDocumentNumber(budgetDocument.getBudget()
453: .getDocumentNumber());
454: tempBicl.setBudgetOnCampusIndicator(taskPeriod.getTask()
455: .isBudgetTaskOnCampus());
456: tempBicl.setBudgetPurposeCode(budgetDocument.getBudget()
457: .getIndirectCost().getBudgetPurposeCode());
458:
459: if (ObjectUtils.collectionContainsObjectWithIdentitcalKey(
460: budgetDocument.getBudget()
461: .getBudgetIndirectCostLookups(), tempBicl)) {
462: rate = ((BudgetIndirectCostLookup) ObjectUtils
463: .retrieveObjectWithIdentitcalKey(budgetDocument
464: .getBudget()
465: .getBudgetIndirectCostLookups(),
466: tempBicl)).getBudgetIndirectCostRate();
467: } else {
468: IndirectCostLookup idcLookup = (IndirectCostLookup) businessObjectService
469: .retrieve(new IndirectCostLookup(taskPeriod
470: .getTask().isBudgetTaskOnCampus(),
471: budgetDocument.getBudget()
472: .getIndirectCost()
473: .getBudgetPurposeCode()));
474: rate = idcLookup.getBudgetIndirectCostRate();
475: tempBicl.setBudgetIndirectCostRate(rate);
476: budgetDocument.getBudget()
477: .getBudgetIndirectCostLookups().add(tempBicl);
478: }
479: }
480:
481: return rate;
482: }
483:
484: private KualiDecimal getIndirectCostRate(
485: BudgetTaskPeriodIndirectCost taskPeriod,
486: BudgetDocument budgetDocument) {
487: return getIndirectCostRate(taskPeriod, budgetDocument, false);
488: }
489:
490: /**
491: * Calculate cost share base.
492: *
493: * @param taskPeriod
494: * @param budgetDocument
495: */
496: private KualiInteger calculateCostShareBaseCost(
497: BudgetTaskPeriodIndirectCost taskPeriod,
498: BudgetDocument budgetDocument) {
499: // Task / Period totals for nonpersonnel and personnel.
500: KualiInteger personnelTotal = new KualiInteger(0);
501: KualiInteger nonPersonnelTotal = new KualiInteger(0);
502:
503: if (budgetDocument.getBudget().getIndirectCost()
504: .getBudgetIndirectCostCostShareIndicator()) {
505: List<String> graduateAssistantAppointmentTypes = parameterService
506: .getParameterValues(
507: BudgetDocument.class,
508: KraConstants.KRA_BUDGET_PERSONNEL_GRADUATE_RESEARCH_ASSISTANT_APPOINTMENT_TYPES);
509: String graduateAssistentNonpersonnelCategoryCode = parameterService
510: .getParameterValue(
511: ParameterConstants.RESEARCH_ADMINISTRATION_DOCUMENT.class,
512: KraConstants.GRADUATE_ASSISTANT_NONPERSONNEL_CATEGORY_CODE);
513: String graduateAssistantNonpersonnelSubcategoryCode = parameterService
514: .getParameterValue(
515: ParameterConstants.RESEARCH_ADMINISTRATION_DOCUMENT.class,
516: KraConstants.GRADUATE_ASSISTANT_NONPERSONNEL_SUB_CATEGORY_CODE);
517:
518: List<BudgetNonpersonnel> temporaryNonpersonnelItems = new ArrayList<BudgetNonpersonnel>();
519:
520: // Loop over user appointments to get the total amount requested for this taskPeriod.
521: for (UserAppointmentTaskPeriod userAppointmentTaskPeriod : budgetDocument
522: .getBudget()
523: .getAllUserAppointmentTaskPeriods(false)) {
524: if (userAppointmentTaskPeriod
525: .getBudgetTaskSequenceNumber().equals(
526: taskPeriod
527: .getBudgetTaskSequenceNumber())
528: && userAppointmentTaskPeriod
529: .getBudgetPeriodSequenceNumber()
530: .equals(
531: taskPeriod
532: .getBudgetPeriodSequenceNumber())) {
533: // If our task and period sequence numbers match, add the value to the total.
534: if (graduateAssistantAppointmentTypes
535: .contains(userAppointmentTaskPeriod
536: .getInstitutionAppointmentTypeCode())) {
537: personnelTotal = personnelTotal
538: .add(userAppointmentTaskPeriod
539: .getInstitutionSalaryAmount());
540: personnelTotal = personnelTotal
541: .add(userAppointmentTaskPeriod
542: .getInstitutionHealthInsuranceAmount());
543:
544: // If it is a GA, we need to add Nonpersonnel Fee Remission to Nonpersonnel list (not stored in database).
545: BudgetNonpersonnel budgetNonpersonnel = new BudgetNonpersonnel(
546: userAppointmentTaskPeriod
547: .getBudgetTaskSequenceNumber(),
548: userAppointmentTaskPeriod
549: .getBudgetPeriodSequenceNumber(),
550: graduateAssistentNonpersonnelCategoryCode,
551: graduateAssistantNonpersonnelSubcategoryCode,
552: "",
553: userAppointmentTaskPeriod
554: .getAgencyRequestedFeesAmount(),
555: userAppointmentTaskPeriod
556: .getInstitutionRequestedFeesAmount());
557: budgetNonpersonnel
558: .refreshReferenceObject("nonpersonnelObjectCode");
559: budgetNonpersonnel.getNonpersonnelObjectCode()
560: .refreshReferenceObject(
561: "nonpersonnelSubCategory");
562:
563: temporaryNonpersonnelItems
564: .add(budgetNonpersonnel);
565:
566: } else {
567: personnelTotal = personnelTotal
568: .add(userAppointmentTaskPeriod
569: .getInstitutionCostShareRequestTotalAmount());
570: personnelTotal = personnelTotal
571: .add(userAppointmentTaskPeriod
572: .getInstitutionCostShareFringeBenefitTotalAmount());
573: }
574: }
575: }
576:
577: // Loop over nonpersonnel items to get the total amount requested for this taskPeriod.
578: for (BudgetNonpersonnel nonPersonnelItem : budgetDocument
579: .getBudget().getNonpersonnelItems()) {
580:
581: // If our task and period sequence numbers match, add the value to the total.
582: if (nonPersonnelItem.getBudgetTaskSequenceNumber()
583: .equals(
584: taskPeriod
585: .getBudgetTaskSequenceNumber())
586: && nonPersonnelItem
587: .getBudgetPeriodSequenceNumber()
588: .equals(
589: taskPeriod
590: .getBudgetPeriodSequenceNumber())) {
591: if (nonPersonnelItem.getNonpersonnelObjectCode() == null
592: || !nonPersonnelItem
593: .getNonpersonnelObjectCode()
594: .getNonpersonnelSubCategory()
595: .isNonpersonnelMtdcExcludedIndicator()) {
596: nonPersonnelTotal = nonPersonnelTotal
597: .add(nonPersonnelItem
598: .getBudgetInstitutionCostShareAmount());
599: }
600: }
601: }
602:
603: // Now add temporary nonpersonnel amounts that may have been added as a side-effect of personnel
604: for (BudgetNonpersonnel tempNonpersonnelItem : temporaryNonpersonnelItems) {
605: if (tempNonpersonnelItem.getBudgetTaskSequenceNumber()
606: .equals(
607: taskPeriod
608: .getBudgetTaskSequenceNumber())
609: && tempNonpersonnelItem
610: .getBudgetPeriodSequenceNumber()
611: .equals(
612: taskPeriod
613: .getBudgetPeriodSequenceNumber())) {
614: if (tempNonpersonnelItem
615: .getNonpersonnelObjectCode() == null
616: || !tempNonpersonnelItem
617: .getNonpersonnelObjectCode()
618: .getNonpersonnelSubCategory()
619: .isNonpersonnelMtdcExcludedIndicator()) {
620: nonPersonnelTotal = nonPersonnelTotal
621: .add(tempNonpersonnelItem
622: .getAgencyRequestAmount());
623: }
624: }
625: }
626: } // else 0 because budgetIndirectCostCostShareIndicator == false
627:
628: // After we've iterated over all possible userAppointmentTaskPeriods and nonpersonnelItems, we set our total.
629: return personnelTotal.add(nonPersonnelTotal);
630: }
631:
632: /**
633: * Calculates Cost Share Indirect Cost. Get the system rate to calculate this, it is always coming from the system.
634: *
635: * @param taskPeriod
636: * @param budgetDocument
637: */
638: private KualiInteger calculateCostShareIndirectCost(
639: BudgetTaskPeriodIndirectCost taskPeriod,
640: BudgetDocument budgetDocument) {
641: KualiDecimal rate = taskPeriod.getCostShareIndirectCostRate();
642: KualiInteger costShareCalculatedIndirectCost = new KualiInteger(
643: 0);
644:
645: if (budgetDocument.getBudget().getIndirectCost()
646: .getBudgetIndirectCostCostShareIndicator()) {
647: costShareCalculatedIndirectCost = new KualiInteger(
648: taskPeriod.getCostShareBaseCost().multiply(rate)
649: .divide(new KualiInteger(100)));
650: }
651:
652: return costShareCalculatedIndirectCost;
653: }
654:
655: /**
656: * Calculate unrecovered indirect cost. This is the difference between what the IDC would be with the system rates and what it
657: * would be with a manual rate. This can only be calculated when a manual rate has been chosen.
658: */
659: private KualiInteger calculateCostShareUnrecoveredIndirectCost(
660: BudgetTaskPeriodIndirectCost taskPeriod,
661: BudgetDocument budgetDocument) {
662: KualiDecimal rate = getIndirectCostRate(taskPeriod,
663: budgetDocument, true);
664: KualiInteger costShareUnrecoveredIndirectCost = new KualiInteger(
665: 0);
666:
667: if (budgetDocument.getBudget()
668: .isInstitutionCostShareIndicator()
669: && "Y".equals(budgetDocument.getBudget()
670: .getIndirectCost()
671: .getBudgetManualRateIndicator())
672: && budgetDocument.getBudget().getIndirectCost()
673: .isBudgetUnrecoveredIndirectCostIndicator()) {
674: costShareUnrecoveredIndirectCost = new KualiInteger(
675: calculateBaseCost(taskPeriod,
676: KraConstants.MODIFIED_TOTAL_DIRECT_COST,
677: budgetDocument).multiply(rate).divide(
678: new KualiInteger(100))).subtract(taskPeriod
679: .getCalculatedIndirectCost());
680: }
681: return costShareUnrecoveredIndirectCost;
682: }
683:
684: /**
685: * Iterate over all task/period lines and calculate Idc values based on personnel/non-personnel expenses.
686: *
687: * @param budgetDocument
688: */
689: private void calculateTaskPeriodIdcListValues(
690: BudgetDocument budgetDocument) {
691:
692: // Attempt to pull the idc object. If it doesn't exist yet we are dealing with a new
693: // or rotten budget, so we will have to create it.
694: BudgetIndirectCost idc = budgetDocument.getBudget()
695: .getIndirectCost();
696: if (idc == null) {
697: this .createTaskPeriodIdcList(budgetDocument);
698: }
699:
700: // We should now have a list, since createTaskPeriodIdcList would have
701: // set it up for us just now, or on any previous save.
702: List idcItems = budgetDocument.getBudget().getIndirectCost()
703: .getBudgetTaskPeriodIndirectCostItems();
704:
705: // Iterate over all existing idc list items and calculate the appropriate values for each taskPeriodLine.
706: for (Iterator idcItemsIterator = idcItems.iterator(); idcItemsIterator
707: .hasNext();) {
708: BudgetTaskPeriodIndirectCost taskPeriod = (BudgetTaskPeriodIndirectCost) idcItemsIterator
709: .next();
710:
711: taskPeriod.setTotalDirectCost(this
712: .calculateTotalDirectCost(taskPeriod,
713: budgetDocument));
714: taskPeriod.setBaseCost(this .calculateBaseCost(taskPeriod,
715: budgetDocument));
716: taskPeriod.setIndirectCostRate(this .getIndirectCostRate(
717: taskPeriod, budgetDocument));
718: taskPeriod.setCalculatedIndirectCost(new KualiInteger(
719: taskPeriod.getBaseCost().multiply(
720: taskPeriod.getIndirectCostRate()).divide(
721: new KualiInteger(100))));
722: taskPeriod.setCostShareBaseCost(this
723: .calculateCostShareBaseCost(taskPeriod,
724: budgetDocument));
725: taskPeriod.setCostShareIndirectCostRate(this
726: .getIndirectCostRate(taskPeriod, budgetDocument,
727: true));
728: taskPeriod.setCostShareCalculatedIndirectCost(this
729: .calculateCostShareIndirectCost(taskPeriod,
730: budgetDocument));
731: taskPeriod
732: .setCostShareUnrecoveredIndirectCost(calculateCostShareUnrecoveredIndirectCost(
733: taskPeriod, budgetDocument));
734: }
735: }
736:
737: /**
738: * Verify that our IDC object and our taskPeriodIDC objects all match what is found in the budget. We don't want to have
739: * mismatched task/period sequence numbers.
740: *
741: * @param budgetDocument
742: */
743: public void reconcileIndirectCost(BudgetDocument budgetDocument) {
744: this .cleanseIndirectCost(budgetDocument);
745: if (!budgetDocument.getBudget()
746: .isInstitutionCostShareIndicator()
747: && !budgetDocument.getBudget()
748: .isBudgetThirdPartyCostShareIndicator()
749: && budgetDocument.getBudget().getIndirectCost() != null) {
750: budgetDocument.getBudget().getIndirectCost()
751: .setBudgetIndirectCostCostShareIndicator(false);
752: budgetDocument.getBudget().getIndirectCost()
753: .setBudgetUnrecoveredIndirectCostIndicator(false);
754: }
755: this .createTaskPeriodIdcList(budgetDocument);
756: }
757:
758: /**
759: * Refresh an IndirectCost object.
760: *
761: * @0param budgetDocument
762: */
763: public void refreshIndirectCost(BudgetDocument budgetDocument) {
764: if (budgetDocument.getBudget().getIndirectCost() != null
765: && budgetDocument.getBudget().getIndirectCost()
766: .getBudgetTaskPeriodIndirectCostItems() != null) {
767: BudgetIndirectCost budgetIndirectCost = budgetDocument
768: .getBudget().getIndirectCost();
769: List taskPeriodItems = budgetIndirectCost
770: .getBudgetTaskPeriodIndirectCostItems();
771:
772: for (Iterator i = taskPeriodItems.iterator(); i.hasNext();) {
773: BudgetTaskPeriodIndirectCost taskPeriod = (BudgetTaskPeriodIndirectCost) i
774: .next();
775:
776: taskPeriod.refreshReferenceObject("task");
777: taskPeriod.refreshReferenceObject("period");
778: }
779:
780: budgetDocument.getBudget().refreshReferenceObject(
781: "personnel");
782: budgetDocument.getBudget().refreshReferenceObject(
783: "nonpersonnelItems");
784: calculateTaskPeriodIdcListValues(budgetDocument);
785:
786: if ("N".equals(budgetIndirectCost
787: .getBudgetManualRateIndicator())) {
788: budgetIndirectCost
789: .setBudgetManualRateIndicatorDescription(parameterService
790: .getParameterValue(
791: BudgetDocument.class,
792: KraConstants.KRA_BUDGET_INDIRECT_COST_PROVIDED_SYSTEM));
793: } else {
794: budgetIndirectCost
795: .setBudgetManualRateIndicatorDescription(parameterService
796: .getParameterValue(
797: BudgetDocument.class,
798: KraConstants.KRA_BUDGET_INDIRECT_COST_PROVIDED_MANUALLY));
799: }
800: }
801: }
802:
803: /**
804: * Cleanse the task period list to make sure we don't have orphaned task period items.
805: *
806: * @param budgetDocument
807: */
808: private void cleanseIndirectCost(BudgetDocument budgetDocument) {
809: // if there are no lines in the list, then it's likely the first time that the save is occurring. We don't care about
810: // cleansing in that case.
811: if (budgetDocument.getBudget().getIndirectCost() != null
812: && budgetDocument.getBudget().getIndirectCost()
813: .getBudgetTaskPeriodIndirectCostItems() != null) {
814: List taskPeriodItems = budgetDocument.getBudget()
815: .getIndirectCost()
816: .getBudgetTaskPeriodIndirectCostItems();
817: List budgetTasks = budgetDocument.getBudget().getTasks();
818: List budgetPeriods = budgetDocument.getBudget()
819: .getPeriods();
820:
821: for (Iterator i = taskPeriodItems.iterator(); i.hasNext();) {
822: BudgetTaskPeriodIndirectCost taskPeriod = (BudgetTaskPeriodIndirectCost) i
823: .next();
824:
825: BudgetTask budgetTask = (BudgetTask) businessObjectService
826: .retrieve(new BudgetTask(taskPeriod
827: .getDocumentNumber(), taskPeriod
828: .getBudgetTaskSequenceNumber()));
829:
830: BudgetPeriod budgetPeriod = (BudgetPeriod) businessObjectService
831: .retrieve(new BudgetPeriod(taskPeriod
832: .getDocumentNumber(), taskPeriod
833: .getBudgetPeriodSequenceNumber()));
834:
835: if (!ObjectUtils
836: .collectionContainsObjectWithIdentitcalKey(
837: budgetTasks, budgetTask)
838: || !ObjectUtils
839: .collectionContainsObjectWithIdentitcalKey(
840: budgetPeriods, budgetPeriod)) {
841: i.remove();
842: }
843: }
844: }
845: }
846:
847: /**
848: * @see org.kuali.module.kra.budget.service.BudgetIndirectCostService#setupIndirectCostRates(org.kuali.module.kra.budget.bo.Budget)
849: */
850: public void setupIndirectCostRates(Budget budget) {
851: Map fieldValues = new HashMap();
852: fieldValues.put(KFSPropertyConstants.ACTIVE,
853: KFSConstants.ACTIVE_INDICATOR);
854:
855: List<IndirectCostLookup> indirectCostLookups = new ArrayList<IndirectCostLookup>(
856: businessObjectService.findMatching(
857: IndirectCostLookup.class, fieldValues));
858: List<BudgetIndirectCostLookup> budgetIndirectCostLookupList = new ArrayList();
859: for (IndirectCostLookup indirectCostLookup : indirectCostLookups) {
860: budgetIndirectCostLookupList
861: .add(new BudgetIndirectCostLookup(budget,
862: indirectCostLookup));
863: }
864: budget
865: .setBudgetIndirectCostLookups(budgetIndirectCostLookupList);
866: }
867:
868: /**
869: * @see org.kuali.module.kra.budget.service.BudgetIndirectCostService#getDefaultBudgetBaseCodeValues()
870: */
871: public List<BudgetBaseCode> getDefaultBudgetBaseCodeValues() {
872: Map fieldValues = new HashMap();
873: fieldValues.put(KFSPropertyConstants.ACTIVE,
874: KFSConstants.ACTIVE_INDICATOR);
875:
876: return new ArrayList(businessObjectService.findMatching(
877: BudgetBaseCode.class, fieldValues));
878: }
879:
880: /**
881: * @return Returns the budgetModularService.
882: */
883: public BudgetModularService getBudgetModularService() {
884: return budgetModularService;
885: }
886:
887: /**
888: * @param budgetModularService The budgetModularService to set.
889: */
890: public void setBudgetModularService(
891: BudgetModularService budgetModularService) {
892: this .budgetModularService = budgetModularService;
893: }
894:
895: public void setParameterService(ParameterService parameterService) {
896: this .parameterService = parameterService;
897: }
898:
899: public void setBusinessObjectService(
900: BusinessObjectService businessObjectService) {
901: this.businessObjectService = businessObjectService;
902: }
903: }
|