org.kuali.coeus.common.budget.impl.period.BudgetPeriodRule.java Source code

Java tutorial

Introduction

Here is the source code for org.kuali.coeus.common.budget.impl.period.BudgetPeriodRule.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.period;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.kuali.coeus.sys.framework.service.KcServiceLocator;
import org.kuali.kra.award.budget.AwardBudgetExt;
import org.kuali.coeus.common.budget.framework.core.Budget;
import org.kuali.coeus.common.budget.framework.core.SaveBudgetEvent;
import org.kuali.coeus.common.budget.framework.nonpersonnel.BudgetLineItem;
import org.kuali.coeus.common.budget.framework.period.AddBudgetPeriodAndTotalEvent;
import org.kuali.coeus.common.budget.framework.period.AddBudgetPeriodEvent;
import org.kuali.coeus.common.budget.framework.period.BudgetPeriod;
import org.kuali.coeus.common.budget.framework.period.BudgetSummaryErrorConstants;
import org.kuali.coeus.common.budget.framework.period.DeleteBudgetPeriodEvent;
import org.kuali.coeus.common.budget.framework.period.SaveBudgetPeriodAndTotalEvent;
import org.kuali.coeus.common.budget.framework.period.GenerateBudgetPeriodEvent;
import org.kuali.coeus.common.budget.framework.personnel.BudgetPersonnelDetails;
import org.kuali.coeus.common.budget.framework.summary.BudgetSummaryService;
import org.kuali.coeus.common.framework.ruleengine.KcBusinessRule;
import org.kuali.coeus.common.framework.ruleengine.KcEventMethod;
import org.kuali.kra.infrastructure.KeyConstants;
import org.kuali.rice.coreservice.framework.parameter.ParameterService;
import org.kuali.rice.krad.util.GlobalVariables;
import org.kuali.rice.krad.util.MessageMap;

import java.sql.Date;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

@KcBusinessRule("budgetPeriodRule")
public class BudgetPeriodRule {
    private static final Log LOG = LogFactory.getLog(BudgetPeriodRule.class);

    private static final String NEW_BUDGET_PERIOD = "newBudgetPeriod";
    private static final String DEFAULT_ERROR_PATH_PREFIX = "document.budgetPeriods";
    private static final String DEFAULT_COST_LIMIT_ERROR_PATH_PREFIX = "document.budget.budgetPeriods";
    private static final String DEFAULT_NEW_BUDGET_PERIOD_ERROR_PATH_PREFIX = "newBudgetPeriod.error";

    private ParameterService parameterService;

    @KcEventMethod
    public boolean processAddBudgetPeriodBusinessRules(AddBudgetPeriodEvent event) {
        Budget budget = event.getBudget();
        BudgetPeriod newBudgetPeriod = event.getBudgetPeriod();

        boolean rulePassed = true;

        if (!isValidBudgetPeriod(budget, DEFAULT_ERROR_PATH_PREFIX)) {
            rulePassed = false;
        }

        if (rulePassed && (newBudgetPeriod != null)) {
            rulePassed = isValidNewBudgetPeriod(budget, newBudgetPeriod, NEW_BUDGET_PERIOD);
        }

        if (rulePassed) {
            rulePassed = isValidToInsert(budget, newBudgetPeriod, DEFAULT_NEW_BUDGET_PERIOD_ERROR_PATH_PREFIX);
        }

        return rulePassed;
    }

    @KcEventMethod
    public boolean processAddBudgetPeriodBusinessRules(AddBudgetPeriodAndTotalEvent event) {
        Budget budget = event.getBudget();
        BudgetPeriod newBudgetPeriod = event.getBudgetPeriod();
        boolean rulePassed = isValidNewBudgetPeriod(budget, newBudgetPeriod, event.getErrorPath());

        if (rulePassed) {
            rulePassed = isValidToInsert(budget, newBudgetPeriod, event.getErrorPath().concat("startDate"));
        }
        return rulePassed;
    }

    @KcEventMethod
    public boolean processSaveBudgetPeriodBusinessRules(SaveBudgetEvent event) {
        Budget budget = event.getBudget();
        boolean rulePassed = true;

        if (!isValidBudgetPeriod(budget, DEFAULT_ERROR_PATH_PREFIX)) {
            rulePassed = false;
        } else if (!isValidBudgetPeriodBoundaries(budget, DEFAULT_ERROR_PATH_PREFIX)) {
            rulePassed = false;
        }
        if (!budget.isProposalBudget()) {
            rulePassed &= isValidBudgetPeriodCostLimit(budget, DEFAULT_COST_LIMIT_ERROR_PATH_PREFIX);
        }

        return rulePassed;
    }

    @KcEventMethod
    public boolean processSaveBudgetPeriodBusinessRules(SaveBudgetPeriodAndTotalEvent event) {
        Budget budget = event.getBudget();
        boolean rulePassed = true;

        if (!isValidBudgetPeriod(budget, event.getErrorPath())) {
            rulePassed = false;
        } else if (!isValidBudgetPeriodBoundaries(budget, event.getErrorPath())) {
            rulePassed = false;
        }
        if (!budget.isProposalBudget()) {
            rulePassed &= isValidBudgetPeriodCostLimit(budget, event.getErrorPath());
        }

        return rulePassed;
    }

    @KcEventMethod
    public boolean processGenerateBudgetPeriodBusinessRules(GenerateBudgetPeriodEvent event) {
        Budget document = event.getBudget();
        BudgetPeriod newBudgetPeriod = event.getBudgetPeriod();
        MessageMap errorMap = GlobalVariables.getMessageMap();
        boolean rulePassed = true;
        int budgetPeriodNumber = 0;

        //1. Check budget periods are valid
        //2. Check for valid periods in line item
        //3. Look for line item in period 1 (needed to generate budget periods)
        //4. Check for other periods to populate
        //5. Make sure other periods have no pre-existing line items
        if (!isValidBudgetPeriod(document, DEFAULT_ERROR_PATH_PREFIX)) {
            rulePassed = false;
        } else if (newBudgetPeriod != null
                && !isValidNewBudgetPeriod(document, newBudgetPeriod, NEW_BUDGET_PERIOD)) {
            rulePassed = false;
        } else if (!isValidBudgetPeriodBoundaries(document, DEFAULT_ERROR_PATH_PREFIX)) {
            rulePassed = false;
        } else if (!getBudgetSummaryService().budgetLineItemExists(document, budgetPeriodNumber)) {
            errorMap.addToErrorPath(NEW_BUDGET_PERIOD);
            rulePassed = false;
            saveErrors("ERROR_PERIOD_LINE_ITEM_DOESNOT_EXIST", errorMap);
        } else if (document.getBudgetPeriods().size() <= (budgetPeriodNumber + 1)) {
            errorMap.addToErrorPath(NEW_BUDGET_PERIOD);
            rulePassed = false;
            saveErrors("ERROR_NO_FUTURE_PERIOD_TO_GENERATE", errorMap);
        } else {
            String errorParam = "";
            for (int i = budgetPeriodNumber + 1; i < document.getBudgetPeriods().size(); i++) {
                if (getBudgetSummaryService().budgetLineItemExists(document, i)) {
                    errorParam += ("" + (i + 1) + ", ");
                }
            }
            if (errorParam.length() > 0) {
                errorMap.addToErrorPath(NEW_BUDGET_PERIOD);
                rulePassed = false;
                errorParam = errorParam.substring(0, errorParam.length() - 2);
                saveErrors("ERROR_GENERATE_PERIOD", errorMap, errorParam);
            }
        }
        errorMap.removeFromErrorPath(NEW_BUDGET_PERIOD);
        return rulePassed;
    }

    @KcEventMethod
    public boolean processDeleteBudgetPeriodBusinessRules(DeleteBudgetPeriodEvent event) {
        Budget budget = event.getBudget();
        int budgetPeriodNumber = event.getBudgetPeriodNumber();
        MessageMap errorMap = GlobalVariables.getMessageMap();
        boolean rulePassed = true;

        if (getBudgetSummaryService().budgetLineItemExists(budget, budgetPeriodNumber)) {
            errorMap.addToErrorPath("document.budgetPeriods[" + budgetPeriodNumber + "]");
            rulePassed = false;
            saveErrors("ERROR_LINE_ITEM_EXISTS", errorMap);
            errorMap.removeFromErrorPath("document.budgetPeriods[" + budgetPeriodNumber + "]");
        }
        return rulePassed;
    }

    private boolean isValidBudgetPeriodBoundaries(Budget budget, String errorPathPrefix) {
        boolean validBoundaries = true;
        List<BudgetPeriod> budgetPeriods = budget.getBudgetPeriods();
        MessageMap errorMap = GlobalVariables.getMessageMap();
        for (BudgetPeriod budgetPeriod : budgetPeriods) {
            String[] dateParams = { budgetPeriod.getBudgetPeriod() + "" };
            /* get all line items for each budget period */
            Collection<BudgetLineItem> periodLineItems = new ArrayList();
            Collection<BudgetPersonnelDetails> periodPersonnelDetails = new ArrayList();
            /* filter by budget period */
            Integer budgetPeriodNumber = budgetPeriod.getBudgetPeriod();
            int index = budgetPeriodNumber - 1;
            errorMap.addToErrorPath(errorPathPrefix + "[" + index + "]");
            /* check line items */
            periodLineItems = budgetPeriod.getBudgetLineItems();
            BUDGET_LINEITEM_LOOP: for (BudgetLineItem periodLineItem : periodLineItems) {
                if (budgetPeriod.getBudgetPeriod() == periodLineItem.getBudgetPeriod()) {
                    if ((periodLineItem.getStartDate().before(budgetPeriod.getStartDate()))
                            || (periodLineItem.getStartDate().after(budgetPeriod.getEndDate()))
                            || (periodLineItem.getEndDate().after(budgetPeriod.getEndDate()))
                            || (periodLineItem.getEndDate().before(budgetPeriod.getStartDate()))) {
                        saveErrors("ERROR_LINE_ITEM_DATE_DOESNOTMATCH", errorMap, dateParams);
                        validBoundaries = false;
                        break;
                    }
                }
                /* check personnel line items */
                periodPersonnelDetails = periodLineItem.getBudgetPersonnelDetailsList();
                for (BudgetPersonnelDetails periodPersonnelDetail : periodPersonnelDetails) {
                    if (budgetPeriod.getBudgetPeriod() == periodPersonnelDetail.getBudgetPeriod()) {
                        if ((periodPersonnelDetail.getStartDate().before(budgetPeriod.getStartDate()))
                                || (periodPersonnelDetail.getStartDate().after(budgetPeriod.getEndDate()))
                                || (periodPersonnelDetail.getEndDate().after(budgetPeriod.getEndDate()))
                                || (periodPersonnelDetail.getEndDate().before(budgetPeriod.getStartDate()))) {
                            saveErrors("ERROR_LINE_ITEM_DATE_DOESNOTMATCH", errorMap, dateParams);
                            validBoundaries = false;
                            break BUDGET_LINEITEM_LOOP;
                        }
                    }
                }
            }
            errorMap.removeFromErrorPath(errorPathPrefix + "[" + index + "]");
        }
        return validBoundaries;

    }

    private boolean isValidBudgetPeriodCostLimit(Budget budget, String errorPathPrefix) {
        boolean valid = true;
        List<BudgetPeriod> budgetPeriods = budget.getBudgetPeriods();
        MessageMap errorMap = GlobalVariables.getMessageMap();
        int i = 0;
        for (BudgetPeriod budgetPeriod : budgetPeriods) {
            if (budgetPeriod.getTotalCostLimit().isGreaterThan(((AwardBudgetExt) budget).getObligatedTotal())) {
                GlobalVariables.getMessageMap().putError(errorPathPrefix + "[" + i + "].totalCostLimit",
                        KeyConstants.ERROR_PERIOD_COST_LIMIT_EXCEED_OBLIGATED_TOTAL);
                valid = false;
            }
            i++;
        }
        return valid;

    }

    /* check new budget period */
    private boolean isValidNewBudgetPeriod(Budget budget, BudgetPeriod newBudgetPeriod, String errorPathPrefix) {
        MessageMap errorMap = GlobalVariables.getMessageMap();
        boolean validNewBudgetPeriod = true;
        List<BudgetPeriod> budgetPeriods = budget.getBudgetPeriods();
        Date previousPeriodStartDate = null;
        Date previousPeriodEndDate = null;
        Date periodStartDate = null;
        Date periodEndDate = null;
        Date newPeriodStartDate = null;
        Date newPeriodEndDate = null;
        int index = 0;

        /* check new budget period */
        newPeriodStartDate = newBudgetPeriod.getStartDate();
        newPeriodEndDate = newBudgetPeriod.getEndDate();
        errorMap.addToErrorPath(errorPathPrefix);
        if (newPeriodStartDate == null) {
            saveErrors("ERROR_PERIOD_START_REQUIRED", errorMap);
            validNewBudgetPeriod = false;
        }
        if (newPeriodEndDate == null) {
            saveErrors("ERROR_PERIOD_END_REQUIRED", errorMap);
            validNewBudgetPeriod = false;
        }
        errorMap.removeFromErrorPath(errorPathPrefix);

        if (CollectionUtils.isEmpty(budgetPeriods)) {
            newBudgetPeriod.setBudgetPeriod(1);
        }

        /* if dates are valid, check further where we can insert this new date */
        if (validNewBudgetPeriod) {
            int totalBudgetPeriods = budgetPeriods.size() - 1;
            errorMap.addToErrorPath(errorPathPrefix);
            for (BudgetPeriod budgetPeriod : budgetPeriods) {
                Date validDateBefore;
                periodStartDate = budgetPeriod.getStartDate();
                periodEndDate = budgetPeriod.getEndDate();
                String dateCompareValue = null;
                /* check first record */
                if (previousPeriodStartDate == null) {
                    validDateBefore = budget.getStartDate();
                } else {
                    validDateBefore = previousPeriodEndDate;
                }
                /* check if entered new period already exists in budget periods list */
                int periodNum = index;
                String[] newPeriodDateParams = { periodNum + "", periodNum + 1 + "" };
                String invalidErrorMessage = null;
                if (index == 0 || index == totalBudgetPeriods) {
                    invalidErrorMessage = "ERROR_NEW_PERIOD_INVALID";
                } else {
                    invalidErrorMessage = "ERROR_NEW_PERIOD_VALID";
                }
                if ((newPeriodStartDate.compareTo(periodStartDate) == 0)
                        || (newPeriodEndDate.compareTo(periodEndDate) == 0)) {
                    saveErrors(invalidErrorMessage, errorMap, newPeriodDateParams);
                    validNewBudgetPeriod = false;
                    break;
                } else if (newPeriodStartDate.before(periodStartDate)
                        || (index == totalBudgetPeriods && newPeriodStartDate.after(periodEndDate))) {
                    /* check if new period start date is before current period start date */
                    boolean lastRecord = false;
                    if (index == totalBudgetPeriods) {
                        lastRecord = true;
                        if (newPeriodStartDate.after(periodEndDate)) {
                            periodNum = index + 1;
                        }
                    }
                    /* check new budget period */
                    if (newPeriodStartDate.before(budget.getStartDate())) {
                        dateCompareValue = "ERROR_PERIOD_START_BEFORE_PROJECT_START";
                    } else if (newPeriodStartDate.after(budget.getEndDate())) {
                        dateCompareValue = "ERROR_NEW_PERIOD_START_AFTER_PROJECT_END";
                    } else if (newPeriodEndDate.after(budget.getEndDate())) {
                        dateCompareValue = "ERROR_NEW_PERIOD_END_DATE";
                    } else if (newPeriodStartDate.before(validDateBefore)) {
                        dateCompareValue = invalidErrorMessage;
                    } else if ((index < totalBudgetPeriods) && newPeriodEndDate.after(periodStartDate)) {
                        if (!lastRecord) {
                            dateCompareValue = invalidErrorMessage;
                        } else {
                            dateCompareValue = "ERROR_NEW_PERIOD_PROJECT_END";
                        }
                    }
                    if (dateCompareValue != null) {
                        saveErrors(dateCompareValue, errorMap, newPeriodDateParams);
                        validNewBudgetPeriod = false;
                    } else {
                        newBudgetPeriod.setBudgetPeriod(periodNum + 1);
                    }
                    break;
                } else if (newPeriodStartDate.compareTo(periodEndDate) <= 0) {
                    dateCompareValue = "ERROR_NEW_PERIOD_START_BEFORE_PREVIOUS_END";
                    saveErrors(dateCompareValue, errorMap, newPeriodDateParams);
                    validNewBudgetPeriod = false;
                    break;
                }
                previousPeriodStartDate = budgetPeriod.getStartDate();
                previousPeriodEndDate = budgetPeriod.getEndDate();
                index++;
            }
            errorMap.removeFromErrorPath(errorPathPrefix);
        }
        return validNewBudgetPeriod;
    }

    /* check existing budget periods */
    private boolean isValidBudgetPeriod(Budget budget, String errorPathPrefix) {
        List<BudgetPeriod> budgetPeriods = budget.getBudgetPeriods();
        boolean validBudgetPeriod = true;
        Date previousPeriodStartDate = null;
        Date previousPeriodEndDate = null;
        Date periodStartDate = null;
        Date periodEndDate = null;
        int index = 0;
        MessageMap errorMap = GlobalVariables.getMessageMap();

        /* verify existing budget periods */
        for (BudgetPeriod budgetPeriod : budgetPeriods) {
            errorMap.addToErrorPath(errorPathPrefix + "[" + index + "]");
            periodStartDate = budgetPeriod.getStartDate();
            periodEndDate = budgetPeriod.getEndDate();
            Date validDateBefore;
            boolean isDateNull = false;
            String[] dateParams = { index + 1 + "" };
            /* check for changes - start date is null */
            if (periodStartDate == null) {
                saveErrors("ERROR_PERIOD_START_REQUIRED", errorMap, dateParams);
                validBudgetPeriod = false;
                isDateNull = true;
            }
            /* check for changes - end date is null */
            if (periodEndDate == null) {
                saveErrors("ERROR_PERIOD_END_REQUIRED", errorMap, dateParams);
                validBudgetPeriod = false;
                isDateNull = true;
            }
            /* if date not null, validate budget period */
            if (!isDateNull) {
                /* check first record */
                if (previousPeriodStartDate == null) {
                    validDateBefore = budget.getStartDate();
                } else {
                    validDateBefore = previousPeriodEndDate;
                }
                String dateCompareValue = compareDate(budget, periodStartDate, periodEndDate,
                        previousPeriodEndDate);
                if (dateCompareValue != null) {
                    saveErrors(dateCompareValue, errorMap, dateParams);
                    validBudgetPeriod = false;
                }
                errorMap.removeFromErrorPath(errorPathPrefix + "[" + index + "]");
            }
            previousPeriodStartDate = budgetPeriod.getStartDate();
            previousPeriodEndDate = budgetPeriod.getEndDate();
            index++;
        }

        return validBudgetPeriod;
    }

    private void saveErrors(String errorValue, MessageMap errorMap, String... parameters) {
        BudgetSummaryErrorConstants budgetSummaryErrorConstants = BudgetSummaryErrorConstants.valueOf(errorValue);
        String errorKey = budgetSummaryErrorConstants.errorKey();
        String errorProperty = budgetSummaryErrorConstants.errorProperty();
        errorMap.putError(errorProperty, errorKey, parameters);
    }

    private String compareDate(Budget budget, Date periodStartDate, Date periodEndDate,
            Date previousPeriodEndDate) {
        String returnErrorValue = null;
        LOG.info("prd st dt " + periodStartDate.getTime() + periodEndDate.getTime()
                + budget.getStartDate().getTime() + budget.getEndDate().getTime());
        Date budgetEndDate = new Date(budget.getBudgetEndDate().getTime());
        Date budgetStartDate = new Date(budget.getBudgetStartDate().getTime());
        if (periodStartDate.after(budgetEndDate)) {
            LOG.info("ERROR_PERIOD_START_AFTER_PROJECT_END" + periodStartDate + budget.getEndDate());
            returnErrorValue = "ERROR_PERIOD_START_AFTER_PROJECT_END";
        } else if (periodStartDate.before(budgetStartDate)) {
            LOG.info("ERROR_PERIOD_START_BEFORE_PROJECT_START" + periodStartDate + budget.getStartDate());
            returnErrorValue = "ERROR_PERIOD_START_BEFORE_PROJECT_START";
        } else if (periodEndDate.before(budget.getStartDate())) {
            LOG.info("ERROR_PERIOD_END_BEFORE_PROJECT_START" + periodEndDate + budget.getStartDate());
            returnErrorValue = "ERROR_PERIOD_END_BEFORE_PROJECT_START";
        } else if (periodEndDate.after(budgetEndDate)) {
            LOG.info("ERROR_PERIOD_END_AFTER_PROJECT_END" + periodEndDate + budget.getEndDate());
            returnErrorValue = "ERROR_PERIOD_END_AFTER_PROJECT_END";
        } else if (periodStartDate.after(periodEndDate)) {
            LOG.info("ERROR_PERIOD_START_AFTER_PERIOD_END" + periodStartDate + periodEndDate);
            returnErrorValue = "ERROR_PERIOD_START_AFTER_PERIOD_END";
        } else if (previousPeriodEndDate != null && !periodStartDate.after(previousPeriodEndDate)) {
            LOG.info("ERROR_PERIOD_START_BEFORE_PREVIOUS_END" + previousPeriodEndDate + periodStartDate);
            returnErrorValue = "ERROR_PERIOD_START_BEFORE_PREVIOUS_END";
        } else if (previousPeriodEndDate != null && !periodEndDate.after(previousPeriodEndDate)) {
            LOG.info("ERROR_PERIOD_END_BEFORE_PREVIOUS_END" + previousPeriodEndDate + periodEndDate);
            returnErrorValue = "ERROR_PERIOD_END_BEFORE_PREVIOUS_END";
        }
        return returnErrorValue;
    }

    private BudgetSummaryService getBudgetSummaryService() {
        return KcServiceLocator.getService(BudgetSummaryService.class);
    }

    private boolean isValidToInsert(Budget budget, BudgetPeriod newBudgetPeriod, String errorPathPrefix) {

        int expenseExistStatus = checkExpenseInBudget(budget);
        MessageMap errorMap = GlobalVariables.getMessageMap();
        if (CollectionUtils.isNotEmpty(budget.getBudgetPeriods())) {
            if (newBudgetPeriod.getEndDate().before(budget.getBudgetPeriod(0).getStartDate())) {
                // insert before 1st period
                if (expenseExistStatus >= 1) {
                    errorMap.putError(errorPathPrefix, KeyConstants.ERROR_INSERT_BUDGET_PERIOD);
                    return false;
                }
            } else if (newBudgetPeriod.getEndDate()
                    .before(budget.getBudgetPeriod(budget.getBudgetPeriods().size() - 1).getStartDate())
                    && expenseExistStatus > 1) {
                errorMap.putError(errorPathPrefix, KeyConstants.ERROR_INSERT_BUDGET_PERIOD);
                return false;
            }
        }
        return true;
    }

    private int checkExpenseInBudget(Budget budget) {
        int retVal = 0;
        for (BudgetPeriod budgetPeriod : budget.getBudgetPeriods()) {
            if (CollectionUtils.isNotEmpty(budgetPeriod.getBudgetLineItems())) {
                retVal = budgetPeriod.getBudgetPeriod();
            }
        }
        return retVal;
    }

    protected ParameterService getParameterService() {
        if (this.parameterService == null) {
            this.parameterService = KcServiceLocator.getService(ParameterService.class);
        }
        return this.parameterService;
    }
}