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.math.BigDecimal;
019: import java.util.ArrayList;
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.util.KualiInteger;
026: import org.kuali.core.util.ObjectUtils;
027: import org.kuali.module.cg.bo.Agency;
028: import org.kuali.module.kra.budget.bo.Budget;
029: import org.kuali.module.kra.budget.bo.BudgetModular;
030: import org.kuali.module.kra.budget.bo.BudgetModularPeriod;
031: import org.kuali.module.kra.budget.bo.BudgetPeriod;
032: import org.kuali.module.kra.budget.bo.UserAppointmentTaskPeriod;
033: import org.kuali.module.kra.budget.service.BudgetModularService;
034: import org.kuali.module.kra.budget.service.BudgetNonpersonnelService;
035: import org.kuali.module.kra.budget.web.struts.form.BudgetNonpersonnelFormHelper;
036: import org.springframework.transaction.annotation.Transactional;
037:
038: @Transactional
039: public class BudgetModularServiceImpl implements BudgetModularService {
040:
041: private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger
042: .getLogger(BudgetModularServiceImpl.class);
043:
044: private BudgetNonpersonnelService budgetNonpersonnelService;
045: private ModularAgencyHelper modularAgencyHelper;
046:
047: /**
048: * @see org.kuali.module.kra.budget.service.BudgetModularService#generateModularBudget(org.kuali.module.kra.budget.bo.Budget)
049: */
050: public void generateModularBudget(Budget budget) {
051:
052: budgetNonpersonnelService.refreshNonpersonnelObjectCode(budget
053: .getNonpersonnelItems());
054:
055: List nonpersonnelCategories = new ArrayList();
056: try {
057: nonpersonnelCategories = budgetNonpersonnelService
058: .getAllNonpersonnelCategories();
059: } catch (Exception e) {
060: LOG
061: .error("Some exception retrieving nonpersonnelCategories.");
062: throw new RuntimeException(
063: "Exception trying to retrieve nonpersonnelCategories: "
064: + e);
065: }
066:
067: generateModularBudget(budget, nonpersonnelCategories);
068: }
069:
070: /**
071: * @see org.kuali.module.kra.budget.service.BudgetModularService#generateModularBudget(org.kuali.module.kra.budget.bo.Budget,
072: * List nonpersonnelCategories)
073: */
074: public void generateModularBudget(Budget budget,
075: List nonpersonnelCategories) {
076:
077: BudgetModular modularBudget = initializeModularBudgetBaseValues(
078: budget, nonpersonnelCategories);
079:
080: if (!isValidModularBudget(budget.getBudgetAgency(),
081: modularBudget, budget.getPeriods().size())) {
082: setupModularBudgetInvalidMode(modularBudget);
083: } else {
084: setupValidModularBudget(modularBudget);
085: }
086: }
087:
088: /**
089: * @see org.kuali.module.kra.budget.service.BudgetModularService#resetModularBudget(org.kuali.module.kra.budget.bo.Budget)
090: */
091: public void resetModularBudget(Budget budget) {
092:
093: budgetNonpersonnelService.refreshNonpersonnelObjectCode(budget
094: .getNonpersonnelItems());
095:
096: List nonpersonnelCategories = new ArrayList();
097: try {
098: nonpersonnelCategories = budgetNonpersonnelService
099: .getAllNonpersonnelCategories();
100: } catch (Exception e) {
101: LOG
102: .error("Some exception retrieving nonpersonnelCategories.");
103: throw new RuntimeException(
104: "Exception trying to retrieve nonpersonnelCategories: "
105: + e);
106: }
107:
108: BudgetModular modularBudget = initializeModularBudgetBaseValues(
109: budget, nonpersonnelCategories);
110:
111: if (isValidModularBudget(budget.getBudgetAgency(),
112: modularBudget, budget.getPeriods().size())) {
113: modularBudget
114: .setBudgetModularDirectCostAmount(determineModularDirectCost(
115: modularBudget.getBudgetModularPeriods()
116: .size(), modularBudget));
117:
118: for (Iterator iter = budget.getModularBudget()
119: .getBudgetModularPeriods().iterator(); iter
120: .hasNext();) {
121: BudgetModularPeriod currentModularPeriod = (BudgetModularPeriod) iter
122: .next();
123: currentModularPeriod
124: .setBudgetAdjustedModularDirectCostAmount(budget
125: .getModularBudget()
126: .getBudgetModularDirectCostAmount());
127: }
128: }
129: }
130:
131: /**
132: * @see org.kuali.module.kra.budget.service.BudgetModularService#agencySupportsModular(org.kuali.module.cg.bo.Agency)
133: */
134: public boolean agencySupportsModular(Agency agency) {
135: if (ObjectUtils.isNull(agency)) {
136: return false;
137: }
138: if (ObjectUtils.isNotNull(agency.getAgencyExtension())) {
139: return agency.getAgencyExtension()
140: .isAgencyModularIndicator();
141: }
142: if (ObjectUtils.isNotNull(agency.getReportsToAgency())) {
143: return agencySupportsModular(agency.getReportsToAgency());
144: }
145: return false;
146: }
147:
148: /**
149: * @see org.kuali.module.kra.budget.service.BudgetModularService#determineBudgetPeriodMaximumAmount(org.kuali.module.cg.bo.Agency)
150: */
151: public KualiInteger determineBudgetPeriodMaximumAmount(Agency agency) {
152: ModularAgencyHelper helper = getModularAgencyInformation(
153: agency, new ModularAgencyHelper());
154: return helper.getBudgetPeriodMaximumAmount();
155: }
156:
157: /**
158: * Setup initial Modular Budget values.
159: *
160: * @param Budget budget
161: * @param List nonpersonnelCategories
162: * @return BudgetModular
163: */
164: private BudgetModular initializeModularBudgetBaseValues(
165: Budget budget, List nonpersonnelCategories) {
166:
167: modularAgencyHelper = getModularAgencyInformation(budget
168: .getBudgetAgency(), new ModularAgencyHelper());
169:
170: BudgetModular modularBudget;
171: if (ObjectUtils.isNotNull(budget.getModularBudget())) { // If modular budget already present in budget, just rebuild that
172: // one.
173: modularBudget = budget.getModularBudget();
174: if (modularBudget.getBudgetModularIncrementAmount() == null) { // Might not be stored
175: modularBudget
176: .setBudgetModularIncrementAmount(modularAgencyHelper
177: .getBudgetModularIncrementAmount());
178: }
179: } else { // First visit to Modular page - get a new Modular Budget.
180: modularBudget = new BudgetModular(budget
181: .getDocumentNumber());
182: // This is stored, b/c if agency info changes, we still have to use the "legacy" increments if modular budget was
183: // created using those values
184: modularBudget
185: .setBudgetModularIncrementAmount(modularAgencyHelper
186: .getBudgetModularIncrementAmount());
187: budget.setModularBudget(modularBudget);
188: }
189:
190: modularBudget.setBudgetPeriodMaximumAmount(modularAgencyHelper
191: .getBudgetPeriodMaximumAmount());
192: calculateDirectCostAmountsByPeriod(budget,
193: nonpersonnelCategories);
194: modularBudget
195: .setTotalActualDirectCostAmount(calculateTotalActualDirectCostAmount(modularBudget
196: .getBudgetModularPeriods()));
197: modularBudget
198: .setIncrements(generateAgencyModularIncrements(modularBudget));
199: return modularBudget;
200: }
201:
202: /**
203: * Setup appropriate values for an invalid ModularBudget.
204: *
205: * @param BudgetModular modularBudget
206: */
207: private void setupModularBudgetInvalidMode(
208: BudgetModular modularBudget) {
209:
210: List increments = new ArrayList();
211: increments.add("0");
212: modularBudget.setIncrements(increments);
213:
214: for (Iterator iter = modularBudget.getBudgetModularPeriods()
215: .iterator(); iter.hasNext();) {
216: BudgetModularPeriod currentModularPeriod = (BudgetModularPeriod) iter
217: .next();
218: currentModularPeriod.setTotalPeriodDirectCostAmount(null);
219: }
220:
221: modularBudget
222: .setTotalConsortiumAmount(calculateTotalConsortiumAmount(modularBudget
223: .getBudgetModularPeriods()));
224: modularBudget.setTotalAdjustedModularDirectCostAmount(null);
225: modularBudget.setTotalModularDirectCostAmount(null);
226: modularBudget.setTotalDirectCostAmount(null);
227: modularBudget.setBudgetModularDirectCostAmount(null);
228: modularBudget.setInvalidMode(true);
229: }
230:
231: /**
232: * Setup appropriate values for an valid ModularBudget.
233: *
234: * @param BudgetModular modularBudget
235: */
236: private void setupValidModularBudget(BudgetModular modularBudget) {
237: modularBudget
238: .setBudgetModularDirectCostAmount(determineModularDirectCost(
239: modularBudget.getBudgetModularPeriods().size(),
240: modularBudget));
241:
242: for (Iterator iter = modularBudget.getBudgetModularPeriods()
243: .iterator(); iter.hasNext();) {
244: BudgetModularPeriod currentModularPeriod = (BudgetModularPeriod) iter
245: .next();
246: if (currentModularPeriod
247: .getBudgetAdjustedModularDirectCostAmount() == null) {
248: if (modularBudget.getBudgetModularDirectCostAmount()
249: .isNonZero()) {
250: currentModularPeriod
251: .setBudgetAdjustedModularDirectCostAmount(modularBudget
252: .getBudgetModularDirectCostAmount());
253: } else {
254: currentModularPeriod
255: .setBudgetAdjustedModularDirectCostAmount(new KualiInteger(
256: (String) modularBudget
257: .getIncrements().get(0)));
258: }
259: }
260: currentModularPeriod
261: .setTotalPeriodDirectCostAmount(currentModularPeriod
262: .getConsortiumAmount()
263: .add(
264: currentModularPeriod
265: .getBudgetAdjustedModularDirectCostAmount()));
266: }
267:
268: modularBudget
269: .setTotalModularDirectCostAmount(calculateTotalModularDirectCostAmount(
270: modularBudget
271: .getBudgetModularDirectCostAmount(),
272: modularBudget.getBudgetModularPeriods()));
273: modularBudget
274: .setTotalAdjustedModularDirectCostAmount(calculateTotalAdjustedModularDirectCostAmount(modularBudget
275: .getBudgetModularPeriods()));
276: modularBudget
277: .setTotalDirectCostAmount(calculateTotalDirectCostAmount(modularBudget
278: .getBudgetModularPeriods()));
279: modularBudget
280: .setTotalConsortiumAmount(calculateTotalConsortiumAmount(modularBudget
281: .getBudgetModularPeriods()));
282: modularBudget.setInvalidMode(false);
283: }
284:
285: /**
286: * Setup Agency-related information needed for Modular Budget, based on given Agency.
287: *
288: * @param Agency agency
289: * @param ModularAgencyHelper helper
290: * @return ModularAgencyHelper
291: */
292: private ModularAgencyHelper getModularAgencyInformation(
293: Agency agency, ModularAgencyHelper helper) {
294: if (ObjectUtils.isNotNull(agency.getAgencyExtension())) {
295: if (helper.getBudgetModularIncrementAmount() == null
296: && agency.getAgencyExtension()
297: .getBudgetModularIncrementAmount() != null) {
298: helper.setBudgetModularIncrementAmount(agency
299: .getAgencyExtension()
300: .getBudgetModularIncrementAmount());
301: }
302: if (helper.getBudgetPeriodMaximumAmount() == null
303: && agency.getAgencyExtension()
304: .getBudgetPeriodMaximumAmount() != null) {
305: helper.setBudgetPeriodMaximumAmount(agency
306: .getAgencyExtension()
307: .getBudgetPeriodMaximumAmount());
308: }
309: }
310:
311: if (helper.isComplete()) {
312: return helper;
313: } else {
314: return getModularAgencyInformation(agency
315: .getReportsToAgency(), helper);
316: }
317: }
318:
319: /**
320: * Generate list of modular increments.
321: *
322: * @param BudgetModular mb
323: * @return List
324: */
325: private List generateAgencyModularIncrements(BudgetModular mb) {
326: List returnList = new ArrayList();
327:
328: if (mb.getTotalActualDirectCostAmount().isZero()) {
329: returnList.add("0");
330: }
331:
332: for (KualiInteger eval = mb.getBudgetModularIncrementAmount(); eval
333: .compareTo(mb.getBudgetPeriodMaximumAmount()) <= 0; eval = eval
334: .add(mb.getBudgetModularIncrementAmount())) {
335: returnList.add(eval.toString());
336: }
337:
338: return returnList;
339: }
340:
341: /**
342: * Calculate direct cost amounts by period for given Budget and nonpersonnel categories list.
343: *
344: * @param Budget budget
345: * @param List nonpersonnelCategories
346: */
347: private void calculateDirectCostAmountsByPeriod(Budget budget,
348: List nonpersonnelCategories) {
349: BudgetModular modularBudget = budget.getModularBudget();
350: List periods = budget.getPeriods();
351:
352: Map directCostsMap = new HashMap();
353:
354: // setup hashmap
355: for (Iterator iter = periods.iterator(); iter.hasNext();) {
356: BudgetPeriod period = (BudgetPeriod) iter.next();
357: directCostsMap.put(period.getBudgetPeriodSequenceNumber()
358: .toString(), new KualiInteger(0));
359: }
360:
361: // calculate personnel direct costs
362: List userTaskPeriodList = budget
363: .getAllUserAppointmentTaskPeriods();
364: for (Iterator iter = userTaskPeriodList.iterator(); iter
365: .hasNext();) {
366: UserAppointmentTaskPeriod currentUserAppointmentTaskPeriod = (UserAppointmentTaskPeriod) iter
367: .next();
368: String periodKey = currentUserAppointmentTaskPeriod
369: .getBudgetPeriodSequenceNumber().toString();
370: KualiInteger actualDirectCostAmountLessExcluded = (KualiInteger) directCostsMap
371: .get(periodKey);
372:
373: if (actualDirectCostAmountLessExcluded != null) { // Could be null in the case of a period being deleted
374: actualDirectCostAmountLessExcluded = actualDirectCostAmountLessExcluded
375: .add(currentUserAppointmentTaskPeriod
376: .getAgencyRequestTotalAmount());
377: actualDirectCostAmountLessExcluded = actualDirectCostAmountLessExcluded
378: .add(currentUserAppointmentTaskPeriod
379: .getAgencyFringeBenefitTotalAmount());
380: directCostsMap.put(periodKey,
381: actualDirectCostAmountLessExcluded);
382: }
383: }
384:
385: // calculate nonpersonnel direct costs, then total all direct costs & set in BO
386: for (int i = 0; i < periods.size(); i++) {
387: BudgetPeriod period = (BudgetPeriod) periods.get(i);
388: BudgetModularPeriod currentModularPeriod;
389: if (modularBudget.getBudgetModularPeriods().size() > i) {
390: currentModularPeriod = (BudgetModularPeriod) modularBudget
391: .getBudgetModularPeriods().get(i);
392: } else {
393: currentModularPeriod = new BudgetModularPeriod(
394: modularBudget.getDocumentNumber(), period
395: .getBudgetPeriodSequenceNumber());
396: modularBudget.getBudgetModularPeriods().add(
397: currentModularPeriod);
398: }
399:
400: KualiInteger actualDirectCostAmountTotal = new BudgetNonpersonnelFormHelper(
401: new Integer(0), period
402: .getBudgetPeriodSequenceNumber(),
403: nonpersonnelCategories, budget
404: .getNonpersonnelItems(), true)
405: .getNonpersonnelAgencyTotal();
406:
407: KualiInteger actualDirectCostAmountLessExcluded = new BudgetNonpersonnelFormHelper(
408: new Integer(0), period
409: .getBudgetPeriodSequenceNumber(),
410: nonpersonnelCategories, budget
411: .getNonpersonnelItems(), false)
412: .getNonpersonnelAgencyTotal();
413:
414: currentModularPeriod
415: .setConsortiumAmount(actualDirectCostAmountTotal
416: .subtract(actualDirectCostAmountLessExcluded));
417:
418: KualiInteger personnelDirectCostAmount = (KualiInteger) directCostsMap
419: .get(period.getBudgetPeriodSequenceNumber()
420: .toString());
421: actualDirectCostAmountLessExcluded = actualDirectCostAmountLessExcluded
422: .add(personnelDirectCostAmount);
423:
424: currentModularPeriod
425: .setActualDirectCostAmount(actualDirectCostAmountLessExcluded);
426: }
427: }
428:
429: /**
430: * Determine the modular direct cost for given modular budget.
431: *
432: * @param int numPeriods
433: * @param BudgetModular modularBudget
434: * @return KualiInteger
435: */
436: private KualiInteger determineModularDirectCost(int numPeriods,
437: BudgetModular modularBudget) {
438: KualiInteger returnVal = null;
439:
440: KualiInteger periods = new KualiInteger(numPeriods);
441: KualiInteger budgetPeriodMaximumAmount = modularBudget
442: .getBudgetPeriodMaximumAmount();
443: KualiInteger budgetPeriodIncrementAmount = modularBudget
444: .getBudgetModularIncrementAmount();
445:
446: BigDecimal rawPeriodActualDirectCost = modularBudget
447: .getTotalActualDirectCostAmount().divide(periods);
448:
449: if (rawPeriodActualDirectCost.toBigInteger().remainder(
450: budgetPeriodMaximumAmount.bigIntegerValue()).intValue() > 0) {
451:
452: KualiInteger absoluteCeiling = budgetPeriodMaximumAmount
453: .multiply(periods);
454:
455: for (KualiInteger eval = new KualiInteger(0); eval
456: .compareTo(absoluteCeiling) <= 0
457: && returnVal == null; eval = eval
458: .add(budgetPeriodIncrementAmount)) {
459:
460: if (eval.compareTo(new KualiInteger(
461: rawPeriodActualDirectCost)) >= 0) {
462: returnVal = eval;
463: } // end if
464: } // end for-loop
465: } else {
466: returnVal = new KualiInteger(rawPeriodActualDirectCost);
467: } // end if/else
468: return returnVal;
469: }
470:
471: /**
472: * Calculate the total actual direct cost for this budget.
473: *
474: * @param List budgetModularPeriods
475: * @return KualiInteger
476: */
477: private KualiInteger calculateTotalActualDirectCostAmount(
478: List budgetModularPeriods) {
479: KualiInteger total = new KualiInteger(0);
480: for (Iterator iter = budgetModularPeriods.iterator(); iter
481: .hasNext();) {
482: BudgetModularPeriod currentModularPeriod = (BudgetModularPeriod) iter
483: .next();
484: total = total.add(currentModularPeriod
485: .getActualDirectCostAmount());
486: }
487: return total;
488: }
489:
490: /**
491: * Calculate the total modular direct cost for this budget.
492: *
493: * @param KualiInteger budgetModularDirectCostAmount
494: * @param List budgetModularPeriods
495: * @return KualiInteger
496: */
497: private KualiInteger calculateTotalModularDirectCostAmount(
498: KualiInteger budgetModularDirectCostAmount,
499: List budgetModularPeriods) {
500: return budgetModularDirectCostAmount.multiply(new KualiInteger(
501: budgetModularPeriods.size()));
502: }
503:
504: /**
505: * Calculate the total adjusted modular direct cost for this budget.
506: *
507: * @param List budgetModularPeriods
508: * @return KualiInteger
509: */
510: private KualiInteger calculateTotalAdjustedModularDirectCostAmount(
511: List budgetModularPeriods) {
512: KualiInteger total = new KualiInteger(0);
513: for (Iterator iter = budgetModularPeriods.iterator(); iter
514: .hasNext();) {
515: BudgetModularPeriod currentModularPeriod = (BudgetModularPeriod) iter
516: .next();
517: total = total.add(currentModularPeriod
518: .getBudgetAdjustedModularDirectCostAmount());
519: }
520: return total;
521: }
522:
523: /**
524: * Calculate the total consortium/F&A cost for this budget.
525: *
526: * @param List budgetModularPeriods
527: * @return KualiInteger
528: */
529: private KualiInteger calculateTotalConsortiumAmount(
530: List budgetModularPeriods) {
531: KualiInteger total = new KualiInteger(0);
532: for (Iterator iter = budgetModularPeriods.iterator(); iter
533: .hasNext();) {
534: BudgetModularPeriod currentModularPeriod = (BudgetModularPeriod) iter
535: .next();
536: total = total.add(currentModularPeriod
537: .getConsortiumAmount());
538: }
539: return total;
540: }
541:
542: /**
543: * Calculate the total direct cost for this budget.
544: *
545: * @param List budgetModularPeriods
546: * @return KualiInteger
547: */
548: private KualiInteger calculateTotalDirectCostAmount(
549: List budgetModularPeriods) {
550: KualiInteger total = new KualiInteger(0);
551: for (Iterator iter = budgetModularPeriods.iterator(); iter
552: .hasNext();) {
553: BudgetModularPeriod currentModularPeriod = (BudgetModularPeriod) iter
554: .next();
555: total = total.add(currentModularPeriod
556: .getTotalPeriodDirectCostAmount());
557: }
558: return total;
559: }
560:
561: /**
562: * Determine whether given Modular Budget is valid.
563: *
564: * @param Agency agency
565: * @param BudgetModular modularBudget
566: * @param int numPeriods
567: * @return KualiInteger
568: */
569: private boolean isValidModularBudget(Agency agency,
570: BudgetModular modularBudget, int numPeriods) {
571: // Total direct cost amount is greater than the number of periods times the period maximum
572: KualiInteger periodMaximum = modularAgencyHelper
573: .getBudgetPeriodMaximumAmount();
574: KualiInteger absoluteCeiling = new KualiInteger(numPeriods)
575: .multiply(periodMaximum);
576: KualiInteger actualAmount = modularBudget
577: .getTotalActualDirectCostAmount();
578: return actualAmount.isLessEqual(absoluteCeiling);
579: }
580:
581: /**
582: * Setter for BudgetNonpersonnelService property.
583: *
584: * @param BudgetNonpersonnelService budgetNonpersonnelService
585: */
586: public void setBudgetNonpersonnelService(
587: BudgetNonpersonnelService budgetNonpersonnelService) {
588: this .budgetNonpersonnelService = budgetNonpersonnelService;
589: }
590:
591: /**
592: * This class encapsulates agency-related information needed to process Modular Budget objects.
593: */
594: private class ModularAgencyHelper {
595: private KualiInteger budgetModularIncrementAmount;
596: private KualiInteger budgetPeriodMaximumAmount;
597:
598: public KualiInteger getBudgetModularIncrementAmount() {
599: return budgetModularIncrementAmount;
600: }
601:
602: public void setBudgetModularIncrementAmount(
603: KualiInteger budgetModularIncrementAmount) {
604: this .budgetModularIncrementAmount = budgetModularIncrementAmount;
605: }
606:
607: public KualiInteger getBudgetPeriodMaximumAmount() {
608: return budgetPeriodMaximumAmount;
609: }
610:
611: public void setBudgetPeriodMaximumAmount(
612: KualiInteger budgetPeriodMaximumAmount) {
613: this .budgetPeriodMaximumAmount = budgetPeriodMaximumAmount;
614: }
615:
616: public boolean isComplete() {
617: return this.getBudgetModularIncrementAmount() != null
618: && this.getBudgetPeriodMaximumAmount() != null;
619: }
620: }
621: }
|