org.kuali.coeus.common.budget.impl.rate.BudgetRatesServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.kuali.coeus.common.budget.impl.rate.BudgetRatesServiceImpl.java

Source

/*
 * 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.rate;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.kuali.coeus.common.budget.framework.calculator.ValidCalcType;
import org.kuali.coeus.common.budget.framework.query.operator.*;
import org.kuali.coeus.common.budget.framework.rate.*;
import org.kuali.coeus.common.budget.framework.rate.RateClassType;
import org.kuali.coeus.common.framework.fiscalyear.FiscalYearMonthService;
import org.kuali.coeus.common.framework.type.ActivityType;
import org.kuali.coeus.common.framework.unit.Unit;
import org.kuali.coeus.common.framework.unit.UnitService;
import org.kuali.coeus.common.budget.framework.rate.AbstractInstituteRate;
import org.kuali.coeus.common.budget.framework.rate.InstituteLaRate;
import org.kuali.coeus.common.budget.framework.rate.InstituteRate;
import org.kuali.coeus.common.budget.framework.query.QueryList;
import org.kuali.coeus.common.budget.framework.core.Budget;
import org.kuali.coeus.common.budget.framework.core.BudgetParent;
import org.kuali.coeus.common.budget.framework.period.BudgetPeriod;
import org.kuali.coeus.common.budget.framework.personnel.BudgetPerson;
import org.kuali.coeus.sys.api.model.ScaleTwoDecimal;
import org.kuali.kra.infrastructure.Constants;
import org.kuali.rice.core.api.criteria.QueryByCriteria;
import org.kuali.rice.krad.data.DataObjectService;
import org.kuali.rice.krad.bo.BusinessObject;
import org.kuali.rice.krad.service.BusinessObjectService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

import java.sql.Date;
import java.util.*;
import java.util.stream.Collectors;

import javax.persistence.EntityManager;

public abstract class BudgetRatesServiceImpl implements BudgetRatesService {
    private static final String SPACE = " ";
    static final String UNIT_NUMBER_KEY = "unitNumber";
    static final String ACTIVITY_TYPE_CODE_KEY = "activityTypeCode";

    private static final String PERIOD_SEARCH_SEPARATOR = "|";
    private static final String PERIOD_DISPLAY_SEPARATOR = ",";
    private static final String RATE_CLASS_TYPE = "rateClassType";
    private static final String DEPENDENT_RATE_CLASS_TYPE = "dependentRateClassType";

    private static final Log LOG = LogFactory.getLog(BudgetRatesServiceImpl.class);

    @Autowired
    @Qualifier("businessObjectService")
    private BusinessObjectService businessObjectService;

    @Autowired
    @Qualifier("dataObjectService")
    private DataObjectService dataObjectService;

    @Autowired
    @Qualifier("unitService")
    private UnitService unitService;

    @Autowired
    @Qualifier("fiscalYearMonthService")
    private FiscalYearMonthService fiscalYearMonthService;

    @Autowired
    @Qualifier("kcEntityManager")
    private EntityManager entityManager;

    @Override
    public void resetAllBudgetRates(Budget budget) {
        resetAbstractBudgetApplicableRatesToInstituteRates(budget.getBudgetRates());
        resetAbstractBudgetApplicableRatesToInstituteRates(budget.getBudgetLaRates());
    }

    /**
     * reset budget rates for a panel
     * each panel is based on rate class type 
     *
     */
    @Override
    public void resetBudgetRatesForRateClassType(String rateClassType, Budget budget) {
        List<RateClass> rateClasses = budget.getRateClasses();
        resetBudgetRatesForRateClassType(rateClasses, rateClassType, budget.getBudgetRates());
        resetBudgetRatesForRateClassType(rateClasses, rateClassType, budget.getBudgetLaRates());
    }

    @Override
    public void syncAllBudgetRates(Budget budget) {
        List<InstituteRate> allInstituteRates = new ArrayList<>(getInstituteRates(budget));
        List<InstituteLaRate> allInstituteLaRates = new ArrayList<>(getInstituteLaRates(budget));

        if (isOutOfSync(budget)) {
            Map<String, AbstractInstituteRate> mapOfExistingBudgetProposalRates = mapRatesToKeys(
                    budget.getBudgetRates());
            Map<String, AbstractInstituteRate> mapOfExistingBudgetProposalLaRates = mapRatesToKeys(
                    budget.getBudgetLaRates());

            budget.getBudgetRates().clear();
            budget.getBudgetLaRates().clear();
            budget.getRateClasses().clear();

            // since different rate schedules can change UnrecoveredFandA, clear here
            budget.getBudgetUnrecoveredFandAs().clear();

            getBudgetRates(budget, allInstituteRates);
            getBudgetLaRates(budget, allInstituteLaRates);

            syncVersionNumber(mapOfExistingBudgetProposalRates, budget.getBudgetRates());
            syncVersionNumber(mapOfExistingBudgetProposalLaRates, budget.getBudgetLaRates());
        } else {
            syncBudgetRates(budget.getBudgetRates(), allInstituteRates);
            syncBudgetRates(budget.getBudgetLaRates(), allInstituteLaRates);
        }
    }

    protected void syncVersionNumber(Map<String, AbstractInstituteRate> oldRateMap, List rates) {
        List<AbstractBudgetRate> abstractBudgetRates = (List<AbstractBudgetRate>) rates;
        for (AbstractBudgetRate budgetRate : abstractBudgetRates) {
            AbstractInstituteRate oldRate = oldRateMap.get(budgetRate.getRateKeyAsString());
            if (oldRate != null) {
                budgetRate.setVersionNumber(oldRate.getVersionNumber());
            }
        }
    }

    /* update view - location
     *  
     */
    @Override
    public void viewLocation(String viewLocation, Integer budgetPeriod, Budget budget) {
        viewLocation(viewLocation, budgetPeriod, budget.getBudgetRates());
        viewLocation(viewLocation, budgetPeriod, budget.getBudgetLaRates());
    }

    /**
     * 
     * Does nothing. Placeholder for Award Budget
     */
    @Override
    public void syncParentDocumentRates(Budget budget) {
    }

    /* sync budget rates for a panel
     * each panel is based on rate class type 
     */
    @Override
    public void syncBudgetRatesForRateClassType(String rateClassType, Budget budget) {
        populateInstituteRates(budget);

        Map<String, AbstractInstituteRate> mapOfExistingBudgetProposalRates = mapRatesToKeys(
                budget.getBudgetRates());
        Map<String, AbstractInstituteRate> mapOfExistingBudgetProposalLaRates = mapRatesToKeys(
                budget.getBudgetLaRates());
        replaceRateClassesForRateClassType(rateClassType, budget, budget.getInstituteRates());
        replaceRateClassesForRateClassType(rateClassType, budget, budget.getInstituteLaRates());
        replaceBudgetRatesForRateClassType(rateClassType, budget, budget.getBudgetRates(),
                budget.getInstituteRates());
        replaceBudgetRatesForRateClassType(rateClassType, budget, budget.getBudgetLaRates(),
                budget.getInstituteLaRates());
        syncVersionNumber(mapOfExistingBudgetProposalRates, budget.getBudgetRates());
        syncVersionNumber(mapOfExistingBudgetProposalLaRates, budget.getBudgetLaRates());
    }

    @Override
    public void getBudgetRates(List<RateClassType> rateClassTypes, Budget budget) {
        getBudgetRates(rateClassTypes, budget, getInstituteRates(budget));
    }

    /* verify and add activity type prefix if required for rate class type description
     * 
     * */
    protected void checkActivityPrefixForRateClassTypes(List<RateClassType> rateClassTypes, Budget budget) {
        String activityTypeDescription = getActivityTypeDescription(budget);
        List<BudgetRate> budgetRates = budget.getBudgetRates();
        List<BudgetLaRate> budgetLaRates = budget.getBudgetLaRates();
        for (RateClassType rateClassType : rateClassTypes) {
            if (rateClassType.getPrefixActivityType()) {
                //making changes to the DO here, need to detach to make sure these changes aren't persisted.
                entityManager.detach(rateClassType);
                String newRateClassTypeDescription = activityTypeDescription.concat(rateClassType.getDescription());
                rateClassType.setDescription(newRateClassTypeDescription);
                rateClassType.setPrefixActivityType(false);
                /* set in proposal rates reference */
                for (BudgetRate budgetRate : budgetRates) {
                    RateClassType BPRateClassType = budgetRate.getRateClass().getRateClassType();
                    if (rateClassType.getCode().equalsIgnoreCase(BPRateClassType.getCode())) {
                        BPRateClassType.setDescription(newRateClassTypeDescription);
                    }
                }
                /* set in proposal LA rates reference */
                for (BudgetLaRate budgetLaRate : budgetLaRates) {
                    RateClassType BPLRateClassType = budgetLaRate.getRateClass().getRateClassType();
                    if (rateClassType.getCode().equalsIgnoreCase(BPLRateClassType.getCode())) {
                        BPLRateClassType.setDescription(newRateClassTypeDescription);
                    }
                }
            }
        }
    }

    protected String getActivityTypeDescription(Budget budget) {
        BudgetParent budgetParent = budget.getBudgetParent();

        if (budget.isRateSynced() || !checkActivityTypeChange(budget)) {
            if (budgetParent.getActivityType() != null) {
                return budgetParent.getActivityType().getDescription().concat(SPACE);
            } else {
                return "";
            }
        } else {
            String activityTypeCode = null;
            if (CollectionUtils.isNotEmpty(budget.getBudgetRates())) {
                activityTypeCode = budget.getBudgetRates().get(0).getActivityTypeCode();
            }

            if (activityTypeCode != null) {
                Map<String, Object> pkMap = new HashMap<>();
                pkMap.put("code", activityTypeCode);
                ActivityType activityType = getBusinessObjectService().findByPrimaryKey(ActivityType.class, pkMap);
                if (activityType == null) {
                    return "";
                } else {
                    return activityType.getDescription().concat(SPACE);
                }
            } else {
                return "";
            }
        }
    }

    /**
     * Build rates for each period.
     */
    protected void updateRatesForEachPeriod(Budget budget) {
        List<BudgetRate> budgetRates = budget.getBudgetRates();
        List<BudgetLaRate> budgetLaRates = budget.getBudgetLaRates();
        List<BudgetPeriod> budgetPeriods = budget.getBudgetPeriods();

        for (BudgetPeriod budgetPeriod : budgetPeriods) {
            for (BudgetRate budgetRate : budgetRates) {
                if (budgetRate.getStartDate().compareTo(budgetPeriod.getEndDate()) <= 0) {
                    String dispBudgetPeriod = budgetPeriod.getBudgetPeriod().toString();
                    String formattedPeriod = dispBudgetPeriod.concat(PERIOD_SEARCH_SEPARATOR);
                    String currBudgetPeriod = budgetRate.getTrackAffectedPeriod();
                    if (currBudgetPeriod == null) {
                        currBudgetPeriod = PERIOD_SEARCH_SEPARATOR.concat(formattedPeriod);
                        budgetRate.setTrackAffectedPeriod(currBudgetPeriod);
                    } else {
                        if (!currBudgetPeriod.contains(formattedPeriod)) {
                            currBudgetPeriod = currBudgetPeriod.concat(formattedPeriod);
                            budgetRate.setTrackAffectedPeriod(currBudgetPeriod);
                        }
                    }
                    budgetRate.setAffectedBudgetPeriod(getFormattedAffectedBudgetPeriod(currBudgetPeriod));
                }
            }
            for (BudgetLaRate budgetLaRate : budgetLaRates) {
                if (budgetLaRate.getStartDate().compareTo(budgetPeriod.getEndDate()) <= 0) {
                    String dispBudgetPeriod = budgetPeriod.getBudgetPeriod().toString();
                    String formattedPeriod = dispBudgetPeriod.concat(PERIOD_SEARCH_SEPARATOR);
                    String currBudgetPeriod = budgetLaRate.getTrackAffectedPeriod();
                    if (currBudgetPeriod == null) {
                        currBudgetPeriod = PERIOD_SEARCH_SEPARATOR.concat(formattedPeriod);
                        budgetLaRate.setTrackAffectedPeriod(currBudgetPeriod);
                    } else {
                        if (!currBudgetPeriod.contains(formattedPeriod)) {
                            currBudgetPeriod = currBudgetPeriod.concat(formattedPeriod);
                            budgetLaRate.setTrackAffectedPeriod(currBudgetPeriod);
                        }
                    }
                    budgetLaRate.setAffectedBudgetPeriod(getFormattedAffectedBudgetPeriod(currBudgetPeriod));
                }
            }
        }
    }

    /**
     * This method load institute rates to hashmap
     */

    protected Map<String, AbstractInstituteRate> mapRatesToKeys(Collection rates) {
        Collection<AbstractInstituteRate> abstractInstituteRates = (Collection<AbstractInstituteRate>) rates;
        Map<String, AbstractInstituteRate> rateMap = new HashMap<>();
        for (AbstractInstituteRate abstractInstituteRate : abstractInstituteRates) {
            rateMap.put(abstractInstituteRate.getRateKeyAsString(), abstractInstituteRate);
        }
        return rateMap;
    }

    /* get all institute rates - based on activity type
     * and unit number 
     * */

    protected Collection<InstituteRate> getInstituteRates(Budget budget) {
        //get first unit number in hierarchy with rates then select appropriate rates
        Unit firstUnit = findFirstUnitWithRates(budget.getBudgetParent().getUnit(), InstituteRate.class);
        if (firstUnit == null) {
            return new ArrayList<>();
        }
        Collection abstractRates = getActiveInstituteRates(InstituteRate.class, firstUnit,
                budget.getBudgetParent().getActivityTypeCode());
        return (Collection<InstituteRate>) abstractRates;
    }

    protected Unit findFirstUnitWithRates(Unit leadUnit, Class rateType) {
        Unit currentUnit = leadUnit;
        Map<String, String> currentSearchMap = new HashMap<>();
        while (currentUnit != null) {
            currentSearchMap.put(UNIT_NUMBER_KEY, currentUnit.getUnitNumber());
            Collection currentRates = filterForActiveRatesOnly(
                    getBusinessObjectService().findMatching(rateType, currentSearchMap));
            if (currentRates != null && !currentRates.isEmpty()) {
                break;
            }
            currentUnit = currentUnit.getParentUnit();
        }
        return currentUnit;
    }

    protected Collection<AbstractInstituteRate> getActiveInstituteRates(Class rateType, Unit unit,
            String activityTypeCode) {
        Map<String, String> searchMap = new HashMap<>();
        searchMap.put(UNIT_NUMBER_KEY, unit.getUnitNumber());
        searchMap.put(ACTIVITY_TYPE_CODE_KEY, activityTypeCode);
        return filterForActiveRatesOnly(getBusinessObjectService().findMatching(rateType, searchMap));
    }

    /* get all institute rates - based on 
     * and unit number 
     * */

    protected Collection<InstituteLaRate> getInstituteLaRates(Budget budget) {
        Collection abstractInstituteRates = getFilteredInstituteLaRates(InstituteLaRate.class,
                getRateFilterMap(budget));
        abstractInstituteRates = abstractInstituteRates.size() > 0 ? abstractInstituteRates : new ArrayList();
        return (Collection<InstituteLaRate>) abstractInstituteRates;
    }

    protected Map<String, String> getRateFilterMap(Budget budget) {
        BudgetParent budgetParent = budget.getBudgetParent();
        Map<String, String> rateFilterMap = new HashMap<>();
        rateFilterMap.put(UNIT_NUMBER_KEY, budgetParent.getUnitNumber());
        return rateFilterMap;
    }

    protected Collection getFilteredInstituteLaRates(Class rateType, Map<String, String> rateFilterMap) {
        Collection abstractInstituteRates;
        abstractInstituteRates = filterForActiveRatesOnly(
                getBusinessObjectService().findMatching(rateType, rateFilterMap));
        return abstractInstituteRates;
    }

    protected Collection filterForActiveRatesOnly(Collection abstractInstituteRates) {
        List filteredList = new ArrayList();
        for (AbstractInstituteRate rate : (Collection<AbstractInstituteRate>) abstractInstituteRates) {
            if (rate.isActive()) {
                filteredList.add(rate);
            } else {
                if (LOG.isDebugEnabled()) {
                    LOG.debug("Filtering inactive rate: " + rate.getObjectId());
                }
            }
        }
        return filteredList;
    }

    /* Rate effective date is between project start and end dates.
     * But if budget persons are defined and the earliest salary effective
     * date is prior to project start date, Inflation rates are retrieved from
     * that date on (salary effective date).  
     * This date is used to fetch inflation rates  
     * 
     * */
    protected Date getRateEffectiveStartDate(Budget budget, AbstractInstituteRate rate, Date personEffectiveDate) {
        Date effectiveDate = budget.getStartDate();
        if (rate.getRateClass().getRateClassTypeCode().equalsIgnoreCase(Constants.RATE_CLASS_TYPE_FOR_INFLATION)
                && personEffectiveDate != null && personEffectiveDate.compareTo(effectiveDate) < 0) {
            effectiveDate = personEffectiveDate;
        }
        return effectiveDate;
    }

    /* Look for budget persons salary effective date and return the 
     * earliest effective date 
     * This date is used to fetch/calculate inflation rates  
     * 
     * */

    protected Date getBudgetPersonSalaryEffectiveDate(Budget budget) {
        Map<String, Object> queryMap = new HashMap<>();
        queryMap.put("budgetId", budget.getBudgetId());
        Collection<BudgetPerson> budgetPersons = getBusinessObjectService().findMatching(BudgetPerson.class,
                queryMap);
        Date effectiveDate = null;
        for (BudgetPerson budgetPerson : budgetPersons) {
            if (effectiveDate == null || budgetPerson.getEffectiveDate().compareTo(effectiveDate) < 0) {
                effectiveDate = budgetPerson.getEffectiveDate();
            }
        }
        return effectiveDate;
    }

    /* get applicable rates before project start date  
     * get the latest 
     * */
    protected void filterInstituteRates(Budget budget, Collection<AbstractInstituteRate> allRates,
            Collection<AbstractInstituteRate> filteredRates, Date personSalaryEffectiveDate) {
        List<String> addedList = new ArrayList<>();
        QueryList<AbstractInstituteRate> instituteRates = new QueryList<>(allRates);
        for (AbstractInstituteRate instituteRate : allRates) {
            String hKey = generateThreePartKey(instituteRate);
            if (!addedList.contains(hKey)) {
                addedList.add(hKey);
                Equals eqRateClassCode = new Equals("rateClassCode", instituteRate.getRateClassCode());
                Equals eqRateTypeCode = new Equals("rateTypeCode", instituteRate.getRateTypeCode());
                Equals eqCampusFlag = new Equals("onOffCampusFlag", instituteRate.getOnOffCampusFlag());
                And rateClassAndRateType = new And(eqRateClassCode, eqRateTypeCode);
                And rcRtCampus = new And(rateClassAndRateType, eqCampusFlag);
                QueryList<AbstractInstituteRate> tempFilteredRates = instituteRates.filter(rcRtCampus);
                Date effectiveStartDate = getRateEffectiveStartDate(budget, instituteRate,
                        personSalaryEffectiveDate);
                Equals eqEndDate = new Equals("startDate", budget.getEndDate());
                LesserThan ltEndDate = new LesserThan("startDate", budget.getEndDate());
                Or ltEqEndDate = new Or(eqEndDate, ltEndDate);
                tempFilteredRates = tempFilteredRates.filter(ltEqEndDate);
                GreaterThan gtStartDate = new GreaterThan("startDate", effectiveStartDate);
                QueryList<AbstractInstituteRate> rateWithinProjectPeriod = tempFilteredRates.filter(gtStartDate);
                filteredRates.addAll(rateWithinProjectPeriod);
                tempFilteredRates.removeAll(rateWithinProjectPeriod);
                if (!tempFilteredRates.isEmpty()) {
                    tempFilteredRates.sort("startDate", false);
                    filteredRates.add(tempFilteredRates.get(0));
                }
            }

        }
    }

    protected String generateThreePartKey(AbstractInstituteRate instituteRate) {
        return new StringBuilder(instituteRate.getRateClassCode()).append(instituteRate.getRateTypeCode())
                .append(getLocationFlagAsString(instituteRate.getOnOffCampusFlag())).toString();
    }

    /* filter institute rates - get rates applicable for 
     * the project 
     * */

    protected void filterRates(Budget budget, Collection allAbstractInstituteRates,
            Collection filteredAbstractInstituteRates) {
        filteredAbstractInstituteRates.clear();
        Date personSalaryEffectiveDate = getBudgetPersonSalaryEffectiveDate(budget);
        filterInstituteRates(budget, allAbstractInstituteRates, filteredAbstractInstituteRates,
                personSalaryEffectiveDate);
    }

    protected boolean isOutOfSync(Budget budget) {
        return isOutOfSync(budget.getInstituteRates(), budget.getBudgetRates())
                || isOutOfSync(budget.getInstituteLaRates(), budget.getBudgetLaRates());
    }

    protected boolean isOutOfSync(List instituteRates, List budgetRates) {
        boolean outOfSync = areNumbersOfBudgetRatesOutOfSyncWithInstituteRates(instituteRates, budgetRates);
        if (!outOfSync) {
            outOfSync = areBudgetRatesOutOfSyncWithInsttituteRates(instituteRates, budgetRates);
        }

        return outOfSync;
    }

    protected boolean areNumbersOfBudgetRatesOutOfSyncWithInstituteRates(List instituteRates, List budgetRates) {
        return instituteRates.size() != budgetRates.size();
    }

    protected boolean areBudgetRatesOutOfSyncWithInsttituteRates(List instituteRates, List budgetRates) {
        Set<String> instituteRateKeys = storeAllKeys((List<AbstractInstituteRate>) instituteRates);
        Set<String> budgetRateKeys = storeAllKeys((List<AbstractInstituteRate>) budgetRates);

        return !instituteRateKeys.containsAll(budgetRateKeys);
    }

    protected Set<String> storeAllKeys(List<AbstractInstituteRate> rates) {
        Set<String> keys = new HashSet<>(rates.size(), 1.0f);
        keys.addAll(rates.stream().map(AbstractInstituteRate::getRateKeyAsString).collect(Collectors.toList()));
        return keys;
    }

    protected void resetAbstractBudgetApplicableRatesToInstituteRates(List budgetRates) {
        List<AbstractBudgetRate> abstractBudgetRates = (List<AbstractBudgetRate>) budgetRates;
        for (AbstractBudgetRate abstractBudgetRate : abstractBudgetRates) {
            abstractBudgetRate.setApplicableRate(abstractBudgetRate.getExternalApplicableRate());
        }
    }

    protected void resetBudgetRatesForRateClassType(List<RateClass> rateClasses, String rateClassType,
            List budgetRates) {
        List<AbstractBudgetRate> abstractBudgetRates = (List<AbstractBudgetRate>) budgetRates;

        for (RateClass rateClass : rateClasses) {
            if (rateClass.getRateClassTypeCode().equalsIgnoreCase(rateClassType)) {
                abstractBudgetRates.stream().filter(abstractBudgetRate -> abstractBudgetRate.getRateClassCode()
                        .equalsIgnoreCase(rateClass.getCode())).forEach(abstractBudgetRate -> {
                            abstractBudgetRate.setApplicableRate(abstractBudgetRate.getExternalApplicableRate());
                        });
            }
        }
    }

    protected void syncBudgetRates(List budgetRates, Collection abstractIntituteRates) {
        List<AbstractBudgetRate> abstractBudgetRates = (List<AbstractBudgetRate>) budgetRates;
        Map<String, AbstractInstituteRate> instRateMap = mapRatesToKeys(abstractIntituteRates);
        for (AbstractBudgetRate abstractBudgetRate : abstractBudgetRates) {
            String hKey = abstractBudgetRate.getRateKeyAsString();
            AbstractInstituteRate abstractInstituteRate = instRateMap.get(hKey);
            abstractBudgetRate.setInstituteRate(abstractInstituteRate.getInstituteRate());
            abstractBudgetRate.setApplicableRate(abstractInstituteRate.getExternalApplicableRate());
        }
    }

    protected String getFormattedAffectedBudgetPeriod(String periodAffected) {
        String budgetPeriodAffected = periodAffected;
        if (budgetPeriodAffected != null) {
            budgetPeriodAffected = budgetPeriodAffected.trim();
            budgetPeriodAffected = budgetPeriodAffected.replace(PERIOD_SEARCH_SEPARATOR, PERIOD_DISPLAY_SEPARATOR);
            budgetPeriodAffected = budgetPeriodAffected.substring(1, budgetPeriodAffected.length() - 1);
        }
        return budgetPeriodAffected;
    }

    protected void viewLocation(String viewLocation, Integer budgetPeriod, List rates) {
        List<AbstractBudgetRate> budgetRates = (List<AbstractBudgetRate>) rates;

        for (AbstractBudgetRate budgetRate : budgetRates) {
            String onOffCampusFlag = getLocationFlagAsString(budgetRate.getOnOffCampusFlag());
            boolean displayRate = (viewLocation == null || (viewLocation.equalsIgnoreCase(onOffCampusFlag)));

            /* check budget Period */
            if (displayRate && budgetPeriod != null) {
                String trackAffectedPeriod = budgetRate.getTrackAffectedPeriod();
                String formattedBudgetPeriod = getSeparatedBudgetPeriod(budgetPeriod);
                if (trackAffectedPeriod == null || (!trackAffectedPeriod.contains(formattedBudgetPeriod))) {
                    displayRate = false;
                }
            }
            budgetRate.setDisplayLocation(displayRate);
        }
    }

    protected String getLocationFlagAsString(boolean onOffCampusFlag) {
        return onOffCampusFlag ? Constants.ON_CAMUS_FLAG : Constants.OFF_CAMUS_FLAG;
    }

    protected String getSeparatedBudgetPeriod(Integer budgetPeriod) {
        return new StringBuilder(PERIOD_SEARCH_SEPARATOR).append(budgetPeriod).append(PERIOD_SEARCH_SEPARATOR)
                .toString();
    }

    protected Map<String, RateClassType> populateExistingRateClassTypeMap(List<RateClassType> rateClassTypes) {
        Map<String, RateClassType> existingRateClassTypeMap = new HashMap<>();
        for (RateClassType rateClassType : rateClassTypes) {
            existingRateClassTypeMap.put(rateClassType.getCode(), rateClassType);
        }
        return existingRateClassTypeMap;
    }

    protected void getBudgetRates(Budget budget, Collection<InstituteRate> allInstituteRates) {
        getBudgetRates(budget.getRateClassTypes(), budget, allInstituteRates);
    }

    /* get budget rates applicable for the proposal - based on activity type
     * and unit number 
     * */
    protected void getBudgetRates(List<RateClassType> rateClassTypes, Budget budget,
            Collection<InstituteRate> allInstituteRates) {
        List<InstituteRate> instituteRates = budget.getInstituteRates();
        filterRates(budget, allInstituteRates, instituteRates);
        List<BudgetRate> budgetRates = budget.getBudgetRates();

        syncBudgetRateCollections(rateClassTypes, budget, instituteRates, budgetRates);

        getBudgetLaRates(rateClassTypes, budget);
        checkActivityPrefixForRateClassTypes(rateClassTypes, budget);
    }

    protected void getBudgetLaRates(Budget budget, List<InstituteLaRate> allInstituteLaRates) {
        getBudgetLaRates(budget.getRateClassTypes(), budget, allInstituteLaRates);
    }

    protected void getBudgetLaRates(List<RateClassType> rateClassTypes, Budget budget) {
        getBudgetLaRates(rateClassTypes, budget, new ArrayList<>(getInstituteLaRates(budget)));
    }

    /**
     * Get budget LA rates applicable for the proposal - based on unit number
     */
    protected void getBudgetLaRates(List<RateClassType> rateClassTypes, Budget budget,
            List<InstituteLaRate> allInstituteLaRates) {
        List<InstituteLaRate> instituteLaRates = budget.getInstituteLaRates();
        filterRates(budget, allInstituteLaRates, instituteLaRates);
        List<BudgetLaRate> budgetRates = budget.getBudgetLaRates();

        syncBudgetRateCollections(rateClassTypes, budget, instituteLaRates, budgetRates);
    }

    protected void syncBudgetRateCollections(List<RateClassType> rateClassTypes, Budget budget,
            List abstractInstituteRates, List budgetRates) {

        List<AbstractBudgetRate> abstractBudgetRates = (List<AbstractBudgetRate>) budgetRates;
        List<AbstractInstituteRate> instituteRates = (List<AbstractInstituteRate>) abstractInstituteRates;

        syncAllRateClasses(budget, instituteRates);
        syncAllRateClassTypes(rateClassTypes, instituteRates);
        if (budgetRates.size() == 0) {
            syncAllBudgetRatesForInstituteRateType(budget, abstractBudgetRates, instituteRates);
        }
    }

    @Override
    public void syncBudgetRateCollectionsToExistingRates(List<RateClassType> rateClassTypes, Budget budget) {

        syncAllRateClasses(budget, (List) budget.getBudgetRates());
        syncAllRateClassTypes(rateClassTypes, (List) budget.getBudgetRates());

        syncAllRateClasses(budget, (List) budget.getBudgetLaRates());
        syncAllRateClassTypes(rateClassTypes, (List) budget.getBudgetLaRates());

        checkActivityPrefixForRateClassTypes(rateClassTypes, budget);
    }

    protected void syncAllBudgetRatesForInstituteRateType(Budget budget, List<AbstractBudgetRate> budgetRates,
            List<AbstractInstituteRate> instituteRates) {
        budgetRates.addAll(instituteRates.stream()
                .filter(abstractInstituteRate -> abstractInstituteRate.getRateClass() != null)
                .map(abstractInstituteRate -> generateBudgetRate(budget, abstractInstituteRate))
                .collect(Collectors.toList()));

        updateRatesForEachPeriod(budget);
        Collections.sort(budgetRates);
    }

    protected void replaceRateClassesForRateClassType(String rateClassType, Budget budget, List rates) {
        List<AbstractInstituteRate> instituteRates = (List<AbstractInstituteRate>) rates;
        List<RateClass> budgetRateClasses = budget.getRateClasses();

        removeAllPreviouslyRegisteredRateClassesForRateClassType(rateClassType, budgetRateClasses);
        addRateClassesForRateClassType(rateClassType, instituteRates);
    }

    protected void removeAllPreviouslyRegisteredRateClassesForRateClassType(String rateClassType,
            List<RateClass> budgetRateClasses) {
        Iterator<RateClass> iter = budgetRateClasses.iterator();
        while (iter.hasNext()) {
            RateClass rateClass = iter.next();
            if (rateClassType.equals(rateClass.getRateClassTypeCode())) {
                iter.remove();
            }
        }
    }

    protected void addRateClassesForRateClassType(String rateClassType,
            List<AbstractInstituteRate> instituteRates) {
        Map<String, RateClass> mapOfMatchingRateClasses = new HashMap<>();
        for (AbstractInstituteRate abstractInstituteRate : instituteRates) {
            if (abstractInstituteRate.getRateType() != null) {
                RateClass rateClass = abstractInstituteRate.getRateType().getRateClass();
                if (rateClass == null)
                    abstractInstituteRate.getRateType().refreshNonUpdateableReferences();
                rateClass = abstractInstituteRate.getRateType().getRateClass();
                if (rateClass.getRateClassTypeCode().equals(rateClassType)
                        && mapOfMatchingRateClasses.get(rateClass.getCode()) == null) {
                    mapOfMatchingRateClasses.put(rateClass.getCode(), rateClass);
                }
            }
        }
    }

    protected void replaceBudgetRatesForRateClassType(String rateClassType, Budget budget, List existingBudgetRates,
            List rates) {
        List<AbstractInstituteRate> instituteRates = (List<AbstractInstituteRate>) rates;
        List<AbstractBudgetRate> abstractBudgetRates = (List<AbstractBudgetRate>) existingBudgetRates;

        Map<String, AbstractBudgetRate> existingBudgetRateMap = preservePersistedBudgetRatesForRateClassType(
                rateClassType, abstractBudgetRates);
        removeRegisteredBudgetRatesForRateClassType(rateClassType, abstractBudgetRates);

        Map<String, AbstractBudgetRate> newBudgetRateMap = generateNewAndUpdatedBudgetRates(rateClassType, budget,
                instituteRates, existingBudgetRateMap);

        registerNewAndUpdatedBudgetRates(abstractBudgetRates, newBudgetRateMap);

        updateRatesForEachPeriod(budget);
        Collections.sort(abstractBudgetRates);
    }

    protected void registerNewAndUpdatedBudgetRates(List<AbstractBudgetRate> abstractBudgetRates,
            Map<String, AbstractBudgetRate> newBudgetRateMap) {
        abstractBudgetRates.addAll(newBudgetRateMap.values());
    }

    protected Map<String, AbstractBudgetRate> generateNewAndUpdatedBudgetRates(String rateClassType, Budget budget,
            List<AbstractInstituteRate> instituteRates, Map<String, AbstractBudgetRate> existingBudgetRateMap) {
        Map<String, AbstractBudgetRate> newBudgetRateMap = new HashMap<>();

        instituteRates.stream().filter(abstractInstituteRate -> abstractInstituteRate.getRateType() != null)
                .forEach(abstractInstituteRate -> {
                    RateClass rateClass = abstractInstituteRate.getRateType().getRateClass();
                    if (rateClassType.equals(rateClass.getRateClassTypeCode())) {
                        AbstractBudgetRate newBudgetRate = generateBudgetRate(budget, abstractInstituteRate);
                        String hKey = abstractInstituteRate.getRateKeyAsString();
                        AbstractBudgetRate existingBudgetRate = existingBudgetRateMap.get(hKey);
                        if (existingBudgetRate != null) {
                            newBudgetRate.setVersionNumber(existingBudgetRate.getVersionNumber());
                        }
                        newBudgetRateMap.put(hKey, newBudgetRate);
                    }
                });
        return newBudgetRateMap;
    }

    protected void removeRegisteredBudgetRatesForRateClassType(String rateClassType,
            List<AbstractBudgetRate> abstractBudgetRates) {
        Iterator<AbstractBudgetRate> iter = abstractBudgetRates.iterator();
        while (iter.hasNext()) {
            AbstractBudgetRate budgetRate = iter.next();
            if (rateClassType.equals(budgetRate.getRateClass().getRateClassTypeCode())) {
                iter.remove();
            }
        }
    }

    protected Map<String, AbstractBudgetRate> preservePersistedBudgetRatesForRateClassType(String rateClassType,
            List<AbstractBudgetRate> abstractBudgetRates) {
        Map<String, AbstractBudgetRate> existingBudgetRateMap = new HashMap<>();
        abstractBudgetRates.stream()
                .filter(abstractBudgetRate -> rateClassType
                        .equals(abstractBudgetRate.getRateClass().getRateClassTypeCode()))
                .forEach(abstractBudgetRate -> {
                    existingBudgetRateMap.put(abstractBudgetRate.getRateKeyAsString(), abstractBudgetRate);
                });
        return existingBudgetRateMap;
    }

    protected void syncAllRateClasses(Budget budget, List<AbstractInstituteRate> instituteRates) {
        Map<String, RateClass> rateClassMap = new HashMap<>();
        instituteRates.stream().filter(abstractInstituteRate -> abstractInstituteRate.getRateClass() != null)
                .forEach(abstractInstituteRate -> {
                    String rateClassCode = abstractInstituteRate.getRateClassCode();
                    if (rateClassMap.get(rateClassCode) == null) {
                        rateClassMap.put(rateClassCode, abstractInstituteRate.getRateClass());
                    }
                });

        budget.getRateClasses().addAll(rateClassMap.values());
    }

    protected void syncAllRateClassTypes(List<RateClassType> rateClassTypes,
            List<AbstractInstituteRate> instituteRates) {
        Map<String, RateClassType> existingRateClassTypeMap = populateExistingRateClassTypeMap(rateClassTypes);
        Map<String, RateClassType> rateClassTypeMap = new HashMap<>();
        instituteRates.stream().filter(abstractInstituteRate -> abstractInstituteRate.getRateClass() != null)
                .forEach(abstractInstituteRate -> {
                    String rateClassType = abstractInstituteRate.getRateClass().getRateClassTypeCode();
                    if (existingRateClassTypeMap.get(rateClassType) == null) {
                        rateClassTypeMap.put(rateClassType,
                                abstractInstituteRate.getRateClass().getRateClassType());
                    }
                });

        rateClassTypes.addAll(rateClassTypeMap.values());
    }

    protected AbstractBudgetRate generateBudgetProposalRate(Budget budget, InstituteRate instituteRate) {
        BudgetParent budgetParent = budget.getBudgetParent();
        return new BudgetRate(budgetParent.getUnitNumber(), instituteRate);
    }

    protected AbstractBudgetRate generateBudgetProposalLaRate(Budget budget, InstituteLaRate instituteLaRate) {
        BudgetParent budgetParent = budget.getBudgetParent();
        return new BudgetLaRate(budgetParent.getUnitNumber(), instituteLaRate);
    }

    protected AbstractBudgetRate generateBudgetRate(Budget budget, AbstractInstituteRate abstractInstituteRate) {
        AbstractBudgetRate abstractBudgetRate = (abstractInstituteRate instanceof InstituteRate)
                ? generateBudgetProposalRate(budget, (InstituteRate) abstractInstituteRate)
                : generateBudgetProposalLaRate(budget, (InstituteLaRate) abstractInstituteRate);
        abstractBudgetRate.setBudgetId(budget.getBudgetId());
        abstractBudgetRate.setBudget(budget);
        return abstractBudgetRate;
    }

    /**
     * Searches for persisted {@link RateClass} instances based on the given <code>rateClassType</code>. Uses the {@link BusinessObjectService}
     * to grab appropriate {@link RateClass} instances since {@link RateClass} is a {@link BusinessObject}
     *
     * @param rateClassType to use for retrieving {@link RateClass} instances
     * @return a List of {@link RateClass} instances
     */

    @Override
    public Collection<RateClass> getBudgetRateClasses(String rateClassType) {
        return getDataObjectService()
                .findMatching(RateClass.class, QueryByCriteria.Builder
                        .andAttributes(Collections.singletonMap("rateClassTypeCode", rateClassType)).build())
                .getResults();
    }

    /**
     * Retrieves {@link RateClass} instances as a {@link Map} keyed from the <code>rateTypeCode</code>. This makes it easy for
     * classes (particularly in the UI) to grab {@link RateClass} information via rateTypeCode
     *
     * @param rateClassType to use for {@link RateClass} instances to be retrieved
     * @return a {@link Map} keyed on rateTypeCode containing {@link RateClass} instances
     */
    @Override
    public Map<String, RateClass> getBudgetRateClassMap(String rateClassType) {
        Map<String, RateClass> retval = new HashMap<>();

        for (RateClass rateClass : getBudgetRateClasses(rateClassType)) {
            retval.put(rateClass.getCode(), rateClass);
        }

        return retval;
    }

    @Override
    public void populateInstituteRates(Budget budget) {
        List instituteRates = (List) getInstituteRates(budget);
        filterRates(budget, instituteRates, budget.getInstituteRates());
        List instituteLaRates = (List) getInstituteLaRates(budget);
        filterRates(budget, instituteLaRates, budget.getInstituteLaRates());
    }

    /**
     * By default it does not have to perform sync
     *
     */
    @Override
    public boolean performSyncFlag(Budget budget) {
        return false;
    }

    @Override
    public ScaleTwoDecimal getUnitFormulatedCost(String unitNumber, String formulatedTypeCode) {
        Map<String, String> param = new HashMap<>();
        param.put("formulatedTypeCode", formulatedTypeCode);
        List<UnitFormulatedCost> unitFormulatedCosts = (List<UnitFormulatedCost>) getBusinessObjectService()
                .findMatchingOrderBy(UnitFormulatedCost.class, param, "unitNumber", true);
        List<Unit> unitHierarchy = unitService.getUnitHierarchyForUnit(unitNumber);
        for (Unit unit : unitHierarchy) {
            for (UnitFormulatedCost unitFormulatedCost : unitFormulatedCosts) {
                if (unit.getUnitNumber().equals(unitFormulatedCost.getUnitNumber())) {
                    return unitFormulatedCost.getUnitCost();
                }
            }
        }
        return ScaleTwoDecimal.ZERO;
    }

    @Override
    public Collection<BudgetRate> getSavedBudgetRates(Budget budget) {
        Map<String, Long> qMap = new HashMap<>();
        qMap.put("budgetId", budget.getBudgetId());
        Collection<BudgetRate> rates = businessObjectService.findMatching(BudgetRate.class, qMap);
        for (BudgetRate rate : rates) {
            java.util.Calendar startDate = new java.util.GregorianCalendar();
            startDate.setTime(rate.getStartDate());
            Integer newFY = this.getFiscalYearMonthService().getFiscalYearFromDate(startDate);
            rate.setFiscalYear(newFY.toString());
        }
        return rates;
    }

    @Override
    public boolean checkActivityTypeChange(Budget budget) {
        return checkActivityTypeChange(getSavedBudgetRates(budget), budget.getBudgetParent().getActivityTypeCode());
    }

    @Override
    public boolean checkActivityTypeChange(Collection<BudgetRate> allPropRates, String activityTypeCode) {
        if (CollectionUtils.isNotEmpty(allPropRates)) {
            Equals equalsActivityType = new Equals("activityTypeCode", activityTypeCode);
            QueryList<BudgetRate> matchActivityTypePropRates = new QueryList<>(allPropRates)
                    .filter(equalsActivityType);
            if (CollectionUtils.isEmpty(matchActivityTypePropRates)
                    || allPropRates.size() != matchActivityTypePropRates.size()) {
                return true;
            }
        }

        return false;
    }

    @Override
    public boolean isVacation(String rateClassTypeCode) {
        return StringUtils.equals(rateClassTypeCode,
                org.kuali.coeus.common.budget.api.rate.RateClassType.VACATION.getRateClassType());
    }

    @Override
    public boolean isEmployeeBenefit(String rateClassTypeCode) {
        return StringUtils.equals(rateClassTypeCode,
                org.kuali.coeus.common.budget.api.rate.RateClassType.EMPLOYEE_BENEFITS.getRateClassType());
    }

    @Override
    public boolean isLabAllocationSalary(String rateClassTypeCode) {
        return StringUtils.equals(rateClassTypeCode,
                org.kuali.coeus.common.budget.api.rate.RateClassType.LA_SALARIES.getRateClassType());

    }

    @Override
    public boolean isVacationOnLabAllocation(String rateClassCode, String rateTypeCode) {
        ValidCalcType vacationOnLaValidCalcType = getDependentValidRateClassTypeForLA(
                org.kuali.coeus.common.budget.api.rate.RateClassType.VACATION.getRateClassType());
        return StringUtils.equals(rateClassCode, vacationOnLaValidCalcType.getRateClassCode())
                && StringUtils.equals(rateTypeCode, vacationOnLaValidCalcType.getRateTypeCode());
    }

    @Override
    public boolean isEmployeeBenefitOnLabAllocation(String rateClassCode, String rateTypeCode) {
        ValidCalcType ebOnLaValidCalcType = getDependentValidRateClassTypeForLA(
                org.kuali.coeus.common.budget.api.rate.RateClassType.EMPLOYEE_BENEFITS.getRateClassType());
        return StringUtils.equals(rateClassCode, ebOnLaValidCalcType.getRateClassCode())
                && StringUtils.equals(rateTypeCode, ebOnLaValidCalcType.getRateTypeCode());
    }

    protected ValidCalcType getDependentValidRateClassTypeForLA(String rateClassType) {
        Map<String, String> param = new HashMap<>();
        param.put(RATE_CLASS_TYPE, rateClassType);
        param.put(DEPENDENT_RATE_CLASS_TYPE,
                org.kuali.coeus.common.budget.api.rate.RateClassType.LA_SALARIES.getRateClassType());
        List<ValidCalcType> result = (List<ValidCalcType>) getBusinessObjectService()
                .findMatching(ValidCalcType.class, param);
        return result.isEmpty() ? null : result.get(0);
    }

    public BusinessObjectService getBusinessObjectService() {
        return businessObjectService;
    }

    public void setBusinessObjectService(BusinessObjectService businessObjectService) {
        this.businessObjectService = businessObjectService;
    }

    public UnitService getUnitService() {
        return unitService;
    }

    public void setUnitService(UnitService unitService) {
        this.unitService = unitService;
    }

    public FiscalYearMonthService getFiscalYearMonthService() {
        return fiscalYearMonthService;
    }

    public void setFiscalYearMonthService(FiscalYearMonthService fiscalYearMonthService) {
        this.fiscalYearMonthService = fiscalYearMonthService;
    }

    public DataObjectService getDataObjectService() {
        return dataObjectService;
    }

    public void setDataObjectService(DataObjectService dataObjectService) {
        this.dataObjectService = dataObjectService;
    }

    public EntityManager getEntityManager() {
        return entityManager;
    }

    public void setEntityManager(EntityManager entityManager) {
        this.entityManager = entityManager;
    }
}