Java tutorial
/* * Kuali Coeus, a comprehensive research administration system for higher education. * * Copyright 2005-2015 Kuali, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.kuali.coeus.common.budget.impl.calculator; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.kuali.coeus.common.budget.api.rate.RateClassType; import org.kuali.coeus.common.budget.framework.calculator.*; import org.kuali.coeus.common.budget.framework.query.*; import org.kuali.coeus.common.budget.framework.query.operator.*; import org.kuali.coeus.common.budget.framework.rate.BudgetRate; import org.kuali.coeus.common.budget.framework.rate.BudgetRatesService; import org.kuali.coeus.common.budget.framework.rate.RateClass; import org.kuali.coeus.common.budget.framework.rate.RateType; import org.kuali.coeus.common.budget.framework.rate.AbstractBudgetRate; import org.kuali.coeus.common.budget.framework.rate.BudgetLaRate; import org.kuali.coeus.common.budget.framework.rate.ValidCeRateType; import org.kuali.coeus.sys.api.model.ScaleTwoDecimal; import org.kuali.coeus.sys.framework.service.KcServiceLocator; import org.kuali.kra.award.commitments.FandaRateType; import org.kuali.coeus.common.budget.framework.core.Budget; import org.kuali.coeus.common.budget.framework.nonpersonnel.AbstractBudgetCalculatedAmount; import org.kuali.coeus.common.budget.framework.nonpersonnel.BudgetLineItem; import org.kuali.coeus.common.budget.framework.nonpersonnel.BudgetLineItemBase; import org.kuali.coeus.common.budget.framework.personnel.BudgetPersonnelCalculatedAmount; import org.kuali.coeus.common.budget.framework.personnel.BudgetPersonnelDetails; import org.kuali.coeus.propdev.impl.core.DevelopmentProposal; import org.kuali.kra.infrastructure.Constants; import org.kuali.rice.core.api.CoreApiServiceLocator; import org.kuali.rice.core.api.datetime.DateTimeService; import org.kuali.rice.coreservice.framework.parameter.ParameterConstants; import org.kuali.rice.coreservice.framework.parameter.ParameterService; import org.kuali.rice.krad.service.LegacyDataAdapter; import java.sql.Date; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.*; /** * * Base class for <code>LineItemCalculator</code> and <code>PersonnelLineItemCalculator</code>. */ public abstract class AbstractBudgetCalculator { public static final String RATE_CLASS_CODE = "rateClassCode"; public static final String ON_OFF_CAMPUS_FLAG = "onOffCampusFlag"; public static final String RATE_TYPE_CODE = "rateTypeCode"; public static final String START_DATE = "startDate"; private LegacyDataAdapter legacyDataAdapter; private DateTimeService dateTimeService; protected Budget budget; protected BudgetLineItemBase budgetLineItem; private QueryList<BudgetLaRate> lineItemPropLaRates; private QueryList<BudgetRate> lineItemPropRates; private List<BreakUpInterval> breakupIntervals; private QueryList<ValidCeRateType> infltionValidCalcCeRates; private QueryList<BudgetRate> underrecoveryRates; private QueryList<BudgetRate> inflationRates; private ParameterService parameterService; public AbstractBudgetCalculator(Budget budget, BudgetLineItemBase budgetLineItem) { this.budget = budget; this.budgetLineItem = budgetLineItem; legacyDataAdapter = KcServiceLocator.getService(LegacyDataAdapter.class); dateTimeService = CoreApiServiceLocator.getDateTimeService(); breakupIntervals = new ArrayList<BreakUpInterval>(); } /** * * Abstract method to populate the applicable cost and applicable cost sharing in boundary. * For LineItemCalculator, applicableCost would be the line item cost and * for PersonnelLineItemCalculator applicable cost would be cumulative salary of all personnel line item * @param boundary */ public abstract void populateApplicableCosts(Boundary boundary); /** * This method is for filtering rates and lab allocation rates. * @param rates * @return */ public QueryList filterRates(List rates) { String activityTypeCode = budget.getBudgetParent().getActivityTypeCode(); if (!rates.isEmpty() && rates.get(0) instanceof BudgetRate) { QueryList qList = filterRates(rates, budgetLineItem.getStartDate(), budgetLineItem.getEndDate(), activityTypeCode); if (qList.isEmpty() && !budget.getActivityTypeCode().equals(activityTypeCode)) { qList = filterRates(rates, budgetLineItem.getStartDate(), budgetLineItem.getEndDate(), budget.getActivityTypeCode()); } return qList; } else { return filterRates(rates, budgetLineItem.getStartDate(), budgetLineItem.getEndDate(), null); } } /** * * This method is for filtering rates between startdate and enddate. * <code>activityTypeCode</code> will be null if its a lab allocation rates * @param rates * @param startDate * @param endDate * @param activityTypeCode * @return list of filtered rates */ private QueryList filterRates(List rates, Date startDate, Date endDate, String activityTypeCode) { List<AbstractBudgetCalculatedAmount> lineItemCalcAmts = budgetLineItem.getBudgetCalculatedAmounts(); QueryList qlRates = new QueryList(rates); QueryList budgetProposalRates = new QueryList(); /* * Get all rates from Proposal Rates & Proposal LA Rates which matches with the rates in line item cal amts */ for (AbstractBudgetCalculatedAmount calAmtsBean : lineItemCalcAmts) { String rateClassCode = calAmtsBean.getRateClassCode(); String rateTypeCode = calAmtsBean.getRateTypeCode(); Equals equalsRC = new Equals(RATE_CLASS_CODE, rateClassCode); Equals equalsRT = new Equals(RATE_TYPE_CODE, rateTypeCode); Equals equalsOnOff = new Equals(ON_OFF_CAMPUS_FLAG, budgetLineItem.getOnOffCampusFlag()); And RCandRT = new And(equalsRC, equalsRT); And RCRTandOnOff = new And(RCandRT, equalsOnOff); QueryList filteredRates = qlRates.filter(RCRTandOnOff); if (filteredRates != null && !filteredRates.isEmpty()) { budgetProposalRates.addAll(qlRates.filter(RCRTandOnOff)); } } if (activityTypeCode != null) { // Add inflation rates separately because, calculated amount list will not have inflation rates listed if (infltionValidCalcCeRates != null && !infltionValidCalcCeRates.isEmpty()) { for (ValidCeRateType inflationValidceRate : infltionValidCalcCeRates) { Equals equalsRC = new Equals(RATE_CLASS_CODE, inflationValidceRate.getRateClassCode()); Equals equalsRT = new Equals(RATE_TYPE_CODE, inflationValidceRate.getRateTypeCode()); Equals equalsOnOff = new Equals(ON_OFF_CAMPUS_FLAG, budgetLineItem.getOnOffCampusFlag()); And RCandRT = new And(equalsRC, equalsRT); And RCRTandOnOff = new And(RCandRT, equalsOnOff); Equals eActType = new Equals("activityTypeCode", activityTypeCode); And RCRTandOnOffandActType = new And(RCRTandOnOff, eActType); QueryList<BudgetRate> filteredRates = qlRates.filter(RCRTandOnOffandActType); if (filteredRates != null && !filteredRates.isEmpty()) { setInflationRates(filteredRates); budgetProposalRates.addAll(filteredRates); } } } // Add underrecovery rates if (!isUndercoveryMatchesOverhead()) { Equals equalsRC = new Equals(RATE_CLASS_CODE, budget.getUrRateClassCode()); Equals equalsOnOff = new Equals(ON_OFF_CAMPUS_FLAG, budgetLineItem.getOnOffCampusFlag()); And RCRTandOnOff = new And(equalsRC, equalsOnOff); budgetProposalRates.addAll(qlRates.filter(RCRTandOnOff)); } Equals eActType = new Equals("activityTypeCode", activityTypeCode); budgetProposalRates = budgetProposalRates.filter(eActType); } if (budgetProposalRates != null && !budgetProposalRates.isEmpty()) { LesserThan lesserThan = new LesserThan(START_DATE, endDate); Equals equals = new Equals(START_DATE, endDate); Or or = new Or(lesserThan, equals); budgetProposalRates = budgetProposalRates.filter(or); } return budgetProposalRates; } private boolean isUndercoveryMatchesOverhead() { return budget.getOhRateClassCode().equals(budget.getUrRateClassCode()); } /** * * This method does the calculation for each line item. Both BudgetLineItem and BudgetPersonnelLineItem invokes this method to do the calculation. */ public void calculate() { if (budget.getBudgetParent() instanceof DevelopmentProposal && ((DevelopmentProposal) budget.getBudgetParent()).isParent()) { budgetLineItem.setDirectCost(budgetLineItem.getLineItemCost()); budgetLineItem.setTotalCostSharingAmount(budgetLineItem.getCostSharingAmount()); budgetLineItem.setIndirectCost(ScaleTwoDecimal.ZERO); if (budgetLineItem instanceof BudgetPersonnelDetails) { BudgetPersonnelDetails budgetPersonnelLineItem = (BudgetPersonnelDetails) budgetLineItem; SalaryCalculator salaryCalculator = new SalaryCalculator(budget, budgetPersonnelLineItem); salaryCalculator.calculate(); budgetPersonnelLineItem.setLineItemCost(budgetPersonnelLineItem.getSalaryRequested()); } QueryList<AbstractBudgetCalculatedAmount> calcAmts = new QueryList<AbstractBudgetCalculatedAmount>(); calcAmts.addAll(budgetLineItem.getBudgetCalculatedAmounts()); for (AbstractBudgetCalculatedAmount calcAmt : calcAmts) { calcAmt.refreshReferenceObject("rateClass"); calcAmt.setRateClassType(calcAmt.getRateClass().getRateClassTypeCode()); } NotEquals notEqualsOH = new NotEquals("rateClassType", RateClassType.OVERHEAD.getRateClassType()); Equals equalsOH = new Equals("rateClassType", RateClassType.OVERHEAD.getRateClassType()); String calculatedCostString = "calculatedCost"; budgetLineItem.setDirectCost( budgetLineItem.getDirectCost().add(calcAmts.sumObjects(calculatedCostString, notEqualsOH))); budgetLineItem.setIndirectCost( budgetLineItem.getIndirectCost().add(calcAmts.sumObjects(calculatedCostString, equalsOH))); budgetLineItem.setTotalCostSharingAmount( budgetLineItem.getTotalCostSharingAmount().add(calcAmts.sumObjects("calculatedCostSharing"))); return; } budgetLineItem.setDirectCost(budgetLineItem.getLineItemCost()); budgetLineItem.setTotalCostSharingAmount(budgetLineItem.getCostSharingAmount()); budgetLineItem.setIndirectCost(ScaleTwoDecimal.ZERO); budgetLineItem.setUnderrecoveryAmount(ScaleTwoDecimal.ZERO); createAndCalculateBreakupIntervals(); updateBudgetLineItemCalculatedAmounts(); populateBudgetRateBaseList(); } /** * This abstract method should be implemented by LineItemCalculator and PersonnelLineItemCalculator. * It is for populating the RateAndBase amounts. */ protected abstract void populateBudgetRateBaseList(); /** * * This method is for populating the calculated amounts by looking at the rate class and rate type for each line item gets applied to. */ @SuppressWarnings("unchecked") protected void updateBudgetLineItemCalculatedAmounts() { List<AbstractBudgetCalculatedAmount> lineItemCalcAmts = budgetLineItem.getBudgetCalculatedAmounts(); List<BreakUpInterval> cvLIBreakupIntervals = getBreakupIntervals(); if (lineItemCalcAmts != null && lineItemCalcAmts.size() > 0 && cvLIBreakupIntervals != null && cvLIBreakupIntervals.size() > 0) { /* * Sum up all the calculated costs for each breakup interval and then update the line item cal amts. */ String rateClassCode = "0"; String rateTypeCode = "0"; ScaleTwoDecimal totalCalculatedCost = ScaleTwoDecimal.ZERO; ScaleTwoDecimal totalCalculatedCostSharing = ScaleTwoDecimal.ZERO; ScaleTwoDecimal totalUnderRecovery = ScaleTwoDecimal.ZERO; ScaleTwoDecimal directCost = ScaleTwoDecimal.ZERO; ScaleTwoDecimal indirectCost = ScaleTwoDecimal.ZERO; Equals equalsRC; Equals equalsRT; And RCandRT = null; QueryList<RateAndCost> cvCombinedAmtDetails = new QueryList<RateAndCost>(); for (BreakUpInterval brkUpInterval : cvLIBreakupIntervals) { cvCombinedAmtDetails.addAll(brkUpInterval.getRateAndCosts()); } for (AbstractBudgetCalculatedAmount calculatedAmount : lineItemCalcAmts) { rateClassCode = calculatedAmount.getRateClassCode(); rateTypeCode = calculatedAmount.getRateTypeCode(); equalsRC = new Equals(RATE_CLASS_CODE, rateClassCode); equalsRT = new Equals(RATE_TYPE_CODE, rateTypeCode); RCandRT = new And(equalsRC, equalsRT); totalCalculatedCost = cvCombinedAmtDetails.sumObjects("calculatedCost", RCandRT); calculatedAmount.setCalculatedCost(totalCalculatedCost); totalCalculatedCostSharing = cvCombinedAmtDetails.sumObjects("calculatedCostSharing", RCandRT); calculatedAmount.setCalculatedCostSharing(totalCalculatedCostSharing); } /* * Sum up all the underRecovery costs for each breakup interval and then update the line item details. */ totalUnderRecovery = new QueryList<BreakUpInterval>(cvLIBreakupIntervals).sumObjects("underRecovery"); budgetLineItem.setUnderrecoveryAmount(totalUnderRecovery); /* * Sum up all direct costs ie, rates for RateClassType <> 'O', for each breakup interval plus the line item cost, and * then update the line item details. */ NotEquals notEqualsOH = new NotEquals("rateClassType", RateClassType.OVERHEAD.getRateClassType()); boolean directCostRolledUp = false; boolean resetTotalUnderRecovery = false; ScaleTwoDecimal newTotalUrAmount = ScaleTwoDecimal.ZERO; ScaleTwoDecimal newTotalCostSharing = ScaleTwoDecimal.ZERO; if (budgetLineItem instanceof BudgetLineItem && CollectionUtils .isNotEmpty(((BudgetLineItem) budgetLineItem).getBudgetPersonnelDetailsList())) { for (BudgetPersonnelDetails budgetPersonnelDetail : ((BudgetLineItem) budgetLineItem) .getBudgetPersonnelDetailsList()) { List<BudgetPersonnelCalculatedAmount> personnelCalAmts = budgetPersonnelDetail .getBudgetCalculatedAmounts(); newTotalUrAmount = newTotalUrAmount.add(budgetPersonnelDetail.getUnderrecoveryAmount()); resetTotalUnderRecovery = true; if (CollectionUtils.isNotEmpty(personnelCalAmts)) { for (BudgetPersonnelCalculatedAmount personnelCalAmt : personnelCalAmts) { if (personnelCalAmt.getRateClass() == null) { personnelCalAmt.refreshReferenceObject("rateClass"); } if (!personnelCalAmt.getRateClass().getRateClassTypeCode().equals("O")) { directCost = directCost.add(personnelCalAmt.getCalculatedCost()); } else { indirectCost = indirectCost.add(personnelCalAmt.getCalculatedCost()); } newTotalCostSharing = newTotalCostSharing .add(personnelCalAmt.getCalculatedCostSharing()); directCostRolledUp = true; } } } } if (resetTotalUnderRecovery) { budgetLineItem.setUnderrecoveryAmount(newTotalUrAmount); } if (!directCostRolledUp) { directCost = cvCombinedAmtDetails.sumObjects("calculatedCost", notEqualsOH); } budgetLineItem.setDirectCost(directCost.add(budgetLineItem.getLineItemCost())); /* * Sum up all Indirect costs ie, rates for RateClassType = 'O', for each breakup interval and then update the line item * details. */ Equals equalsOH = new Equals("rateClassType", RateClassType.OVERHEAD.getRateClassType()); if (!directCostRolledUp) { indirectCost = cvCombinedAmtDetails.sumObjects("calculatedCost", equalsOH); } budgetLineItem.setIndirectCost(indirectCost); /* * Sum up all Cost Sharing amounts ie, rates for RateClassType <> 'O' and set in the calculatedCostSharing field of line * item details */ if (!directCostRolledUp) { totalCalculatedCostSharing = cvCombinedAmtDetails.sumObjects("calculatedCostSharing"); } else { totalCalculatedCostSharing = newTotalCostSharing; } budgetLineItem.setTotalCostSharingAmount( budgetLineItem.getCostSharingAmount() == null ? totalCalculatedCostSharing : budgetLineItem.getCostSharingAmount().add(totalCalculatedCostSharing)); } else if (lineItemCalcAmts != null && lineItemCalcAmts.size() > 0 && (budgetLineItem.getLineItemCost().equals(ScaleTwoDecimal.ZERO) || CollectionUtils.isEmpty(cvLIBreakupIntervals))) { for (AbstractBudgetCalculatedAmount calculatedAmount : lineItemCalcAmts) { calculatedAmount.setCalculatedCost(ScaleTwoDecimal.ZERO); calculatedAmount.setCalculatedCostSharing(ScaleTwoDecimal.ZERO); } } } protected void createAndCalculateBreakupIntervals() { populateCalculatedAmountLineItems(); setQlLineItemPropLaRates(filterRates(getBudgetLaRates())); setQlLineItemPropRates(filterRates(getBudgetRates())); createBreakUpInterval(); calculateBreakUpInterval(); } protected abstract List<BudgetRate> getBudgetRates(); protected abstract List<BudgetLaRate> getBudgetLaRates(); /** * Combine the sorted Prop & LA rates, which should be in sorted order(asc). Now create the breakup boundaries and use it to * create breakup intervals and set all the values required for calculation. Then call calculateBreakupInterval method for each * AmountBean for setting the calculated cost & calculated cost sharing ie for each rate class & rate type. */ protected void createBreakUpInterval() { String messageTemplate = ""; String multipleRatesMesgTemplate = ""; String message = ""; if (breakupIntervals == null) breakupIntervals = new ArrayList<BreakUpInterval>(); if (budgetLineItem.getOnOffCampusFlag()) { messageTemplate = "On-Campus rate information not available for Rate Class - \'"; multipleRatesMesgTemplate = "Multiple On-Campus rates found for the period "; } else { messageTemplate = "Off-Campus rate information not available for Rate Class - \'"; multipleRatesMesgTemplate = "Multiple Off-Campus rates found for the period "; } QueryList<BudgetLaRate> qlLineItemPropLaRates = getQlLineItemPropLaRates(); QueryList<BudgetRate> qlLineItemPropRates = getQlLineItemPropRates(); // combine the sorted Prop & LA Rates QueryList qlCombinedRates = new QueryList(); qlCombinedRates.addAll(qlLineItemPropRates); qlCombinedRates.addAll(qlLineItemPropLaRates); qlCombinedRates.sort(START_DATE, true); Date liStartDate = budgetLineItem.getStartDate(); Date liEndDate = budgetLineItem.getEndDate(); List<Boundary> boundaries = createBreakupBoundaries(qlCombinedRates, liStartDate, liEndDate); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("MMM-dd-yyyy"); // create breakup intervals based on the breakup boundaries if (boundaries != null && boundaries.size() > 0) { for (Boundary boundary : boundaries) { BreakUpInterval breakUpInterval = new BreakUpInterval(); breakUpInterval.setBoundary(boundary); breakUpInterval.setBudgetId(budgetLineItem.getBudgetId()); breakUpInterval.setBudgetPeriod(budgetLineItem.getBudgetPeriod()); breakUpInterval.setLineItemNumber(budgetLineItem.getLineItemNumber()); QueryList<RateAndCost> qlRateAndCosts = new QueryList<RateAndCost>(); QueryList<BudgetRate> qlBreakupPropRates = new QueryList<BudgetRate>(); QueryList<BudgetLaRate> qlBreakupPropLARates = new QueryList<BudgetLaRate>(); QueryList qlTempRates = new QueryList(); QueryList qlMultipleRates = new QueryList(); String rateClassType; String rateClassCode; String rateTypeCode; Boolean applyRateFlag; populateApplicableCosts(boundary); breakUpInterval.setApplicableAmt(boundary.getApplicableCost()); breakUpInterval.setApplicableAmtCostSharing(boundary.getApplicableCostSharing()); List<AbstractBudgetCalculatedAmount> qlLineItemCalcAmts = budgetLineItem .getBudgetCalculatedAmounts(); List<String> warningMessages = new ArrayList<String>(); for (AbstractBudgetCalculatedAmount budgetLineItemCalculatedAmount : qlLineItemCalcAmts) { applyRateFlag = budgetLineItemCalculatedAmount.getApplyRateFlag(); rateClassCode = budgetLineItemCalculatedAmount.getRateClassCode(); rateTypeCode = budgetLineItemCalculatedAmount.getRateTypeCode(); // form the rate not available message // These two statements have to move to the populate method of calculatedAmount later. if (budgetLineItemCalculatedAmount.getRateClass() == null && rateClassCode != null) { budgetLineItemCalculatedAmount.setRateClass( getLegacyDataAdapter().findBySinglePrimaryKey(RateClass.class, rateClassCode)); } rateClassType = budgetLineItemCalculatedAmount.getRateClass().getRateClassTypeCode(); // end block to be moved message = messageTemplate + budgetLineItemCalculatedAmount.getRateClass().getDescription() + "\' Rate Type - \'" + budgetLineItemCalculatedAmount.getRateTypeDescription() + "\' for Period - "; // if apply flag is false and rate class type is not Overhead then skip if ((applyRateFlag == null || !applyRateFlag) && !rateClassType.equals(RateClassType.OVERHEAD.getRateClassType())) { continue; } RateAndCost rateCost = new RateAndCost(); rateCost.setApplyRateFlag(applyRateFlag); rateCost.setRateClassType(rateClassType); rateCost.setRateClassCode(rateClassCode); rateCost.setRateTypeCode(rateTypeCode); rateCost.setCalculatedCost(ScaleTwoDecimal.ZERO); rateCost.setCalculatedCostSharing(ScaleTwoDecimal.ZERO); qlRateAndCosts.add(rateCost); // filter & store the rates applicable for this rate class / rate type Equals equalsRC = new Equals(RATE_CLASS_CODE, rateClassCode); Equals equalsRT = new Equals(RATE_TYPE_CODE, rateTypeCode); LesserThan ltEndDate = new LesserThan(START_DATE, boundary.getEndDate()); Equals equalsEndDate = new Equals(START_DATE, boundary.getEndDate()); GreaterThan gtStartDate = new GreaterThan(START_DATE, boundary.getStartDate()); Equals equalsStartDate = new Equals(START_DATE, boundary.getStartDate()); Or gtStartDateOrEqStartDate = new Or(gtStartDate, equalsStartDate); Or ltEndDateOrEqEndDate = new Or(ltEndDate, equalsEndDate); And gtOrEqStartDateAndltOrEqEndDate = new And(gtStartDateOrEqStartDate, ltEndDateOrEqEndDate); And RCandRT = new And(equalsRC, equalsRT); And RCRTandLtStartDate = new And(RCandRT, ltEndDateOrEqEndDate); And RCRTandgtStartDateAndltEndDate = new And(RCandRT, gtOrEqStartDateAndltOrEqEndDate); if (rateClassType.equalsIgnoreCase(RateClassType.LAB_ALLOCATION.getRateClassType()) || rateClassType.equalsIgnoreCase(RateClassType.LA_SALARIES.getRateClassType())) { qlTempRates = qlLineItemPropLaRates.filter(RCRTandLtStartDate); if (qlTempRates != null && qlTempRates.size() > 0) { /* * Check if multiple rates are present got this period. If there, then show message and don't add any * rates. */ List cvMultipleRates = qlLineItemPropLaRates.filter(RCRTandgtStartDateAndltEndDate); if (qlMultipleRates != null && qlMultipleRates.size() > 1) { // Store the multiple rates available message in a message vector message = multipleRatesMesgTemplate + simpleDateFormat.format(boundary.getStartDate()) + " to " + simpleDateFormat.format(boundary.getEndDate()) + " for Rate Class - \'" + budgetLineItemCalculatedAmount.getRateClass().getDescription() + "\' Rate Type - \'" + budgetLineItemCalculatedAmount.getRateTypeDescription(); warningMessages.add(message); } else { /** * sort the rates in desc order and take the first rate which is the latest */ qlTempRates.sort(START_DATE, false); BudgetLaRate tempPropLaRate = (BudgetLaRate) qlTempRates.get(0); qlBreakupPropLARates.add(tempPropLaRate); } } else { // Store the rate not available message in a message vector message = message + simpleDateFormat.format(boundary.getStartDate()) + " to " + simpleDateFormat.format(boundary.getEndDate()); warningMessages.add(message); } } else { qlTempRates = qlLineItemPropRates.filter(RCRTandLtStartDate); if (qlTempRates != null && qlTempRates.size() > 0) { /** * Check if multiple rates are present for this boundary. If there, then show message and don't add any * rates. */ qlMultipleRates = qlLineItemPropRates.filter(RCRTandgtStartDateAndltEndDate); if (qlMultipleRates != null && qlMultipleRates.size() > 1) { // Store the multiple rates available message in a message vector message = multipleRatesMesgTemplate + simpleDateFormat.format(boundary.getStartDate()) + " to " + simpleDateFormat.format(boundary.getEndDate()) + " for Rate Class - \'" + budgetLineItemCalculatedAmount.getRateClass().getDescription() + "\' Rate Type - \'" + budgetLineItemCalculatedAmount.getRateTypeDescription(); warningMessages.add(message); } else { /** * sort the rates in desc order and take the first rate which is the latest */ qlTempRates.sort(START_DATE, false); qlBreakupPropRates.add((BudgetRate) qlTempRates.get(0)); } } else { // Store the rate not available message in a message vector message = message + simpleDateFormat.format(boundary.getStartDate()) + " to " + simpleDateFormat.format(boundary.getEndDate()); warningMessages.add(message); } } } // breakup interval data setting loop ends here // set the values for the breakup interval in the BreakupInterval bean if (qlRateAndCosts != null && qlRateAndCosts.size() > 0) { breakUpInterval.setRateAndCosts(qlRateAndCosts); breakUpInterval.setBudgetProposalRates(qlBreakupPropRates); breakUpInterval.setBudgetProposalLaRates(qlBreakupPropLARates); breakupIntervals.add(breakUpInterval); } QueryList<ValidCeRateType> underrecoveryRates = getRateMappedToCostElement(); // get the rate class, rate type from the cost element and use that. if (!isUndercoveryMatchesOverhead() && !underrecoveryRates.isEmpty()) { // you cannot have more than one rate mapped to this cost element, so always use the first. // if there's more than one mapped, it is wrong. Equals equalsRC = new Equals(RATE_CLASS_CODE, underrecoveryRates.get(0).getRateClassCode()); Equals equalsOnOff = new Equals(ON_OFF_CAMPUS_FLAG, budgetLineItem.getOnOffCampusFlag()); Equals equalsRT = new Equals(RATE_TYPE_CODE, underrecoveryRates.get(0).getRateTypeCode()); And RCandRT = new And(equalsRC, equalsRT); And RCRTandOnOff = new And(RCandRT, equalsOnOff); QueryList<BudgetRate> qlUnderRecoveryRates = qlLineItemPropRates.filter(RCRTandOnOff); if (qlUnderRecoveryRates != null && qlUnderRecoveryRates.size() > 0) { LesserThan ltEndDate = new LesserThan(START_DATE, boundary.getEndDate()); Equals equalsEndDate = new Equals(START_DATE, boundary.getEndDate()); Or ltEndDateOrEqEndDate = new Or(ltEndDate, equalsEndDate); qlTempRates = qlUnderRecoveryRates.filter(ltEndDateOrEqEndDate); if (qlTempRates != null && qlTempRates.size() > 0) { /* * sort the rates in desc order and take the first rate which is the latest */ qlTempRates.sort(START_DATE, false); breakUpInterval.setURRatesBean((BudgetRate) qlTempRates.get(0)); } } } } } } private QueryList<ValidCeRateType> getRateMappedToCostElement() { Equals equalsRC = new Equals(RATE_CLASS_CODE, budget.getUrRateClassCode()); Equals equalsRCT = new Equals("rateClassType", RateClassType.OVERHEAD.getRateClassType()); And RCRTandRCT = new And(equalsRC, equalsRCT); if (budgetLineItem.getCostElementBO() != null && budgetLineItem.getCostElementBO().getValidCeRateTypes().isEmpty()) { budgetLineItem.getCostElementBO().refreshReferenceObject("validCeRateTypes"); } QueryList<ValidCeRateType> validCeRateTypes = new QueryList<>( budgetLineItem.getCostElementBO().getValidCeRateTypes()); return validCeRateTypes.filter(RCRTandRCT); } public String getPersonnelBudgetCategoryTypeCode() { return getParameterService().getParameterValueAsString(Constants.MODULE_NAMESPACE_BUDGET, ParameterConstants.DOCUMENT_COMPONENT, Constants.BUDGET_CATEGORY_TYPE_PERSONNEL); } /** * Use the combined & sorted Prop & LA rates to create Boundary objects. Each Boundary will contain start date & end date. Check * whether any rate changes, and break at this point to create a new boundary. * * @return List of boundary objects */ public List<Boundary> createBreakupBoundaries(QueryList<AbstractBudgetRate> qlCombinedRates, Date liStartDate, Date liEndDate) { List<Boundary> boundaries = new ArrayList<Boundary>(); if (qlCombinedRates != null && qlCombinedRates.size() > 0) { Date tempStartDate = liStartDate; Date tempEndDate = liEndDate; Date rateChangeDate; GreaterThan greaterThan = new GreaterThan(START_DATE, liStartDate); qlCombinedRates = qlCombinedRates.filter(greaterThan); qlCombinedRates.sort(START_DATE, true); for (AbstractBudgetRate laRate : qlCombinedRates) { rateChangeDate = laRate.getStartDate(); if (rateChangeDate.after(tempStartDate)) { Calendar temEndCal = dateTimeService.getCalendar(rateChangeDate); temEndCal.add(Calendar.DAY_OF_MONTH, -1); try { tempEndDate = dateTimeService.convertToSqlDate(temEndCal.get(Calendar.YEAR) + "-" + (temEndCal.get(Calendar.MONTH) + 1) + "-" + temEndCal.get(Calendar.DAY_OF_MONTH)); } catch (ParseException e) { tempEndDate = new Date(rateChangeDate.getTime() - 86400000); } Boundary boundary = new Boundary(tempStartDate, tempEndDate); boundaries.add(boundary); tempStartDate = rateChangeDate; } } /** * add one more boundary if no rate change on endDate and atleast one boundary is present */ if (boundaries.size() > 0) { Boundary boundary = new Boundary(tempStartDate, liEndDate); boundaries.add(boundary); } /** * if no rate changes during the period create one boundary with startDate & endDate same as that for line item */ if (boundaries.size() == 0) { Boundary boundary = new Boundary(liStartDate, liEndDate); boundaries.add(boundary); } } return boundaries; } // end createBreakupBoundaries protected void calculateBreakUpInterval() { int rateNumber = 0; List<BreakUpInterval> cvLIBreakupIntervals = getBreakupIntervals(); for (BreakUpInterval breakUpInterval : cvLIBreakupIntervals) { breakUpInterval.setRateNumber(rateNumber); getBreakupIntervalService().calculate(breakUpInterval); } } private BreakupIntervalService getBreakupIntervalService() { return KcServiceLocator.getService(BreakupIntervalService.class); } protected List<ValidCalcType> getValidCalcTypes() { return (List<ValidCalcType>) legacyDataAdapter.findAll(ValidCalcType.class); } protected abstract void populateCalculatedAmountLineItems(); private <T> QueryList<T> createQueryList(List<T> immutableList) { if (immutableList == null) { return new QueryList(); } return new QueryList(immutableList); } private void setInflationRateOnLineItem(BudgetLineItemBase lineItem) { QueryList<ValidCeRateType> qValidCeRateTypes = createQueryList( budgetLineItem.getCostElementBO().getValidCeRateTypes()); // Check whether it contains Inflation Rate QueryList<ValidCeRateType> inflationValidCeRates = qValidCeRateTypes .filter(new Equals("rateClassType", RateClassType.INFLATION.getRateClassType())); if (!inflationValidCeRates.isEmpty()) { if (lineItem.getApplyInRateFlag()) { setInfltionValidCalcCeRates(inflationValidCeRates); } } else { lineItem.setApplyInRateFlag(false); } } private Equals equalsOverHeadRateClassType() { return new Equals("rateClassType", RateClassType.OVERHEAD.getRateClassType()); } private NotEquals notEqualsInflationRateClassType() { return new NotEquals("rateClassType", RateClassType.INFLATION.getRateClassType()); } private Equals equalsOverHeadRateClassCode() { return new Equals(RATE_CLASS_CODE, "" + budget.getOhRateClassCode()); } private NotEquals notEqualsOverHeadRateClassType() { return new NotEquals("rateClassType", RateClassType.OVERHEAD.getRateClassType()); } private And notEqualsLabAllocationRateClassType() { return new NotEquals("rateClassType", RateClassType.LAB_ALLOCATION.getRateClassType()) .and(new NotEquals("rateClassType", RateClassType.LA_SALARIES.getRateClassType())); } private void setValidCeRateTypeCalculatedAmounts(BudgetLineItemBase lineItem) { QueryList<ValidCeRateType> qValidCeRateTypes = createQueryList( budgetLineItem.getCostElementBO().getValidCeRateTypes()); qValidCeRateTypes = qValidCeRateTypes .filter(equalsOverHeadRateClassType().and(equalsOverHeadRateClassCode()) .or(notEqualsOverHeadRateClassType()).and(notEqualsInflationRateClassType())); List<BudgetLaRate> budgetLaRates = budget.getBudgetLaRates(); if (budgetLaRates == null || budgetLaRates.size() == 0) { qValidCeRateTypes = qValidCeRateTypes.filter(notEqualsLabAllocationRateClassType()); } addBudgetLineItemCalculatedAmountsForRateTypes(qValidCeRateTypes); } private void addBudgetLineItemCalculatedAmountsForRateTypes(List<ValidCeRateType> rateTypes) { if (CollectionUtils.isEmpty(rateTypes)) { return; } for (ValidCeRateType validCeRateType : rateTypes) { String rateClassType = validCeRateType.getRateClass().getRateClassTypeCode(); if (rateClassType.equals(RateClassType.OVERHEAD.getRateClassType()) && !budget.getBudgetParent().isProposalBudget()) { addOHBudgetLineItemCalculatedAmountForAward(validCeRateType.getRateClassCode(), validCeRateType.getRateType(), validCeRateType.getRateClass().getRateClassTypeCode()); } else { addBudgetLineItemCalculatedAmount(validCeRateType.getRateClassCode(), validCeRateType.getRateType(), validCeRateType.getRateClass().getRateClassTypeCode()); } } } private void addOHBudgetLineItemCalculatedAmountForAward(String rateClassCode, RateType rateType, String rateClassType) { QueryList<BudgetRate> budgetRates = new QueryList<BudgetRate>(budget.getBudgetRates()); Equals eqOhRateClassType = new Equals("rateClassType", rateClassType); Equals eqOhRateClassOnCampusFlag = new Equals(ON_OFF_CAMPUS_FLAG, budgetLineItem.getOnOffCampusFlag()); And eqRateClassTypeAndOhCampusFlag = new And(eqOhRateClassType, eqOhRateClassOnCampusFlag); List<BudgetRate> filteredBudgetRates = budgetRates.filter(eqRateClassTypeAndOhCampusFlag); if (!filteredBudgetRates.isEmpty()) { BudgetRate awardBudgetRate = filteredBudgetRates.get(0); awardBudgetRate.setBudget(budget); if (awardBudgetRate.getNonEditableRateFlag()) { AbstractBudgetCalculatedAmount budgetCalculatedAmount = getNewCalculatedAmountInstance(); budgetCalculatedAmount.setBudgetId(budgetLineItem.getBudgetId()); budgetCalculatedAmount.setBudgetPeriod(budgetLineItem.getBudgetPeriod()); budgetCalculatedAmount.setBudgetPeriodId(budgetLineItem.getBudgetPeriodId()); budgetCalculatedAmount.setLineItemNumber(budgetLineItem.getLineItemNumber()); budgetCalculatedAmount.setRateClassType(rateClassType); budgetCalculatedAmount.setRateClassCode(awardBudgetRate.getRateClassCode()); budgetCalculatedAmount.setRateTypeCode(awardBudgetRate.getRateTypeCode()); budgetCalculatedAmount.setApplyRateFlag(true); budgetCalculatedAmount .setRateTypeDescription(getAwardRateTypeDescription(awardBudgetRate.getRateTypeCode())); budgetCalculatedAmount.setRateClass(awardBudgetRate.getRateClass()); addCalculatedAmount(budgetCalculatedAmount); } else { addBudgetLineItemCalculatedAmount(rateClassCode, rateType, rateClassType); } } } private String getAwardRateTypeDescription(String rateTypeCode) { return getLegacyDataAdapter().findBySinglePrimaryKey(FandaRateType.class, rateTypeCode).getDescription(); } private Equals equalsEmployeeBenefitsRateClassType() { return new Equals("rateClassType", RateClassType.EMPLOYEE_BENEFITS.getRateClassType()); } private Equals equalsVacationRateClassType() { return new Equals("rateClassType", RateClassType.VACATION.getRateClassType()); } private Equals equalsLabAllocationSalariesRateClassType() { return new Equals("rateClassType", RateClassType.LA_SALARIES.getRateClassType()); } private void setLabAllocationSalariesCalculatedAmounts(BudgetLineItemBase lineItem) { QueryEngine queryEngine = new QueryEngine(); queryEngine.addDataCollection(ValidCalcType.class, getValidCalcTypes()); QueryList<ValidCeRateType> qValidCeRateTypes = createQueryList( budgetLineItem.getCostElementBO().getValidCeRateTypes()); QueryList<ValidCeRateType> qLabAllocSalRates = qValidCeRateTypes .filter(equalsLabAllocationSalariesRateClassType()); if (CollectionUtils.isNotEmpty(qLabAllocSalRates)) { List<ValidCalcType> validCalCTypes = queryEngine.executeQuery(ValidCalcType.class, equalsEmployeeBenefitsRateClassType()); if (CollectionUtils.isNotEmpty(validCalCTypes)) { ValidCalcType validCalcType = validCalCTypes.get(0); if (validCalcType.getDependentRateClassType() .equals(RateClassType.LA_SALARIES.getRateClassType())) { addBudgetLineItemCalculatedAmount(validCalcType.getRateClassCode(), validCalcType.getRateType(), validCalcType.getRateClassType()); } } validCalCTypes = queryEngine.executeQuery(ValidCalcType.class, equalsVacationRateClassType()); if (!validCalCTypes.isEmpty()) { ValidCalcType validCalcType = (ValidCalcType) validCalCTypes.get(0); if (validCalcType.getDependentRateClassType() .equals(RateClassType.LA_SALARIES.getRateClassType())) { addBudgetLineItemCalculatedAmount(validCalcType.getRateClassCode(), validCalcType.getRateType(), validCalcType.getRateClassType()); } } } } public final void setCalculatedAmounts(Budget budget, BudgetLineItemBase budgetLineItem) { if (budgetLineItem.getCostElementBO() == null) { budgetLineItem.refreshReferenceObject("costElementBO"); } if (budgetLineItem.getCostElementBO().getValidCeRateTypes().isEmpty()) { budgetLineItem.getCostElementBO().refreshReferenceObject("validCeRateTypes"); } setInflationRateOnLineItem(budgetLineItem); setValidCeRateTypeCalculatedAmounts(budgetLineItem); setLabAllocationSalariesCalculatedAmounts(budgetLineItem); } protected void setInfltionValidCalcCeRates(QueryList<ValidCeRateType> infltionValidCalcCeRates) { this.infltionValidCalcCeRates = infltionValidCalcCeRates; } private void addBudgetLineItemCalculatedAmount(String rateClassCode, RateType rateType, String rateClassType) { QueryList<BudgetRate> budgetRates = new QueryList<BudgetRate>(budget.getBudgetRates()); QueryList<BudgetLaRate> qlBudgetLaRates = new QueryList<BudgetLaRate>(budget.getBudgetLaRates()); Equals eqValidRateClassCode = new Equals(RATE_CLASS_CODE, rateClassCode); Equals eqValidRateTypeCode = new Equals(RATE_TYPE_CODE, rateType.getRateTypeCode()); And eqRateClassCodeAndRateTypeCode = new And(eqValidRateClassCode, eqValidRateTypeCode); List<BudgetRate> filteredBudgetRates = budgetRates.filter(eqRateClassCodeAndRateTypeCode); List<BudgetLaRate> filteredBudgetLaRates = qlBudgetLaRates.filter(eqRateClassCodeAndRateTypeCode); if (filteredBudgetRates.isEmpty() && filteredBudgetLaRates.isEmpty()) return; AbstractBudgetCalculatedAmount budgetCalculatedAmount = getNewCalculatedAmountInstance(); budgetCalculatedAmount.setBudgetId(budgetLineItem.getBudgetId()); budgetCalculatedAmount.setBudgetPeriod(budgetLineItem.getBudgetPeriod()); budgetCalculatedAmount.setBudgetPeriodId(budgetLineItem.getBudgetPeriodId()); budgetCalculatedAmount.setLineItemNumber(budgetLineItem.getLineItemNumber()); budgetCalculatedAmount.setRateClassType(rateClassType); budgetCalculatedAmount.setRateClassCode(rateClassCode); budgetCalculatedAmount.setRateTypeCode(rateType.getRateTypeCode()); budgetCalculatedAmount.setApplyRateFlag(true); budgetCalculatedAmount.refreshReferenceObject("rateClass"); budgetCalculatedAmount.setRateTypeDescription(rateType.getDescription()); addCalculatedAmount(budgetCalculatedAmount); } protected abstract AbstractBudgetCalculatedAmount getNewCalculatedAmountInstance(); protected abstract void addCalculatedAmount(AbstractBudgetCalculatedAmount budgetCalculatedAmount); public LegacyDataAdapter getLegacyDataAdapter() { return legacyDataAdapter; } public void setLegacyDataAdapter(LegacyDataAdapter legacyDataAdapter) { this.legacyDataAdapter = legacyDataAdapter; } protected List<BreakUpInterval> getBreakupIntervals() { return breakupIntervals; } protected void setBreakupIntervals(List<BreakUpInterval> breakupIntervals) { this.breakupIntervals = breakupIntervals; } protected DateTimeService getDateTimeService() { return dateTimeService; } public QueryList<BudgetRate> getUnderrecoveryRates() { return underrecoveryRates; } public void setUnderrecoveryRates(QueryList<BudgetRate> underrecoveryRates) { this.underrecoveryRates = underrecoveryRates; } public QueryList<BudgetRate> getInflationRates() { return inflationRates; } public void setInflationRates(QueryList<BudgetRate> inflationRates) { this.inflationRates = inflationRates; } public QueryList<BudgetLaRate> getQlLineItemPropLaRates() { return lineItemPropLaRates; } public void setQlLineItemPropLaRates(QueryList<BudgetLaRate> qlLineItemPropLaRates) { this.lineItemPropLaRates = qlLineItemPropLaRates; } public QueryList<BudgetRate> getQlLineItemPropRates() { return lineItemPropRates; } public void setQlLineItemPropRates(QueryList<BudgetRate> qlLineItemPropRates) { this.lineItemPropRates = qlLineItemPropRates; } protected BudgetRatesService getBudgetRateService() { return KcServiceLocator.getService(BudgetRatesService.class); } public ParameterService getParameterService() { if (parameterService == null) { parameterService = KcServiceLocator.getService(ParameterService.class); } return parameterService; } }