/* * The Kuali Financial System, a comprehensive financial management system for higher education. * * Copyright 2005-2014 The Kuali Foundation * * 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.kfs.module.bc.document.web.struts; import java.text.MessageFormat; import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.apache.commons.lang.StringUtils; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; import org.kuali.kfs.fp.service.FiscalYearFunctionControlService; import org.kuali.kfs.module.bc.BCConstants; import org.kuali.kfs.module.bc.BCKeyConstants; import org.kuali.kfs.module.bc.BCPropertyConstants; import org.kuali.kfs.module.bc.businessobject.BudgetConstructionAuthorizationStatus; import org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding; import org.kuali.kfs.module.bc.document.BudgetConstructionDocument; import org.kuali.kfs.module.bc.document.service.BudgetDocumentService; import org.kuali.kfs.module.bc.document.service.SalarySettingService; import org.kuali.kfs.module.bc.document.validation.event.AdjustSalarySettingLinePercentEvent; import org.kuali.kfs.module.bc.document.validation.event.BudgetExpansionEvent; import org.kuali.kfs.module.bc.document.validation.event.NormalizePayrateAndAmountEvent; import org.kuali.kfs.sys.KFSConstants; import org.kuali.kfs.sys.KFSKeyConstants; import org.kuali.kfs.sys.context.SpringContext; import org.kuali.rice.core.api.config.property.ConfigurationService; import org.kuali.rice.kim.api.identity.Person; import org.kuali.rice.kim.api.services.IdentityManagementService; import org.kuali.rice.kns.question.ConfirmationQuestion; import org.kuali.rice.kns.util.KNSGlobalVariables; import org.kuali.rice.krad.exception.AuthorizationException; import org.kuali.rice.krad.rules.rule.event.KualiDocumentEvent; import org.kuali.rice.krad.service.BusinessObjectService; import org.kuali.rice.krad.service.KualiRuleService; import org.kuali.rice.krad.util.GlobalVariables; import org.kuali.rice.krad.util.KRADConstants; /** * the base action class for salary setting, which provides the implementations of common actions of the salary setting screens */ public abstract class SalarySettingBaseAction extends BudgetExpansionAction { private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(SalarySettingBaseAction.class); protected SalarySettingService salarySettingService = SpringContext.getBean(SalarySettingService.class); protected BusinessObjectService businessObjectService = SpringContext.getBean(BusinessObjectService.class); protected ConfigurationService kualiConfiguration = SpringContext.getBean(ConfigurationService.class); protected BudgetDocumentService budgetDocumentService = SpringContext.getBean(BudgetDocumentService.class); protected KualiRuleService kualiRuleService = SpringContext.getBean(KualiRuleService.class); /** * loads the data for the expansion screen based on the passed in url parameters */ public abstract ActionForward loadExpansionScreen(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception; /** * @see org.kuali.rice.kns.web.struts.action.KualiAction#execute(org.apache.struts.action.ActionMapping, * org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) */ @Override public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { SalarySettingBaseForm salarySettingForm = (SalarySettingBaseForm) form; // if org sal setting we need to initialize authorization if (!salarySettingForm.isBudgetByAccountMode() && salarySettingForm.getMethodToCall() != null && salarySettingForm.getMethodToCall().equals(BCConstants.LOAD_EXPANSION_SCREEN_METHOD)) { initAuthorization(salarySettingForm); } populateAuthorizationFields(salarySettingForm); ActionForward forward = super.execute(mapping, form, request, response); salarySettingForm.postProcessBCAFLines(); // re-init the session form if session scoped if (salarySettingForm.getMethodToCall().equals("refresh")) { if (BCConstants.MAPPING_SCOPE_SESSION.equals(mapping.getScope())) { HttpSession sess = request.getSession(Boolean.FALSE); String formName = mapping.getAttribute(); sess.setAttribute(formName, salarySettingForm); } } return forward; } protected void populateAuthorizationFields(SalarySettingBaseForm salarySettingForm) { BudgetConstructionAuthorizationStatus authorizationStatus = (BudgetConstructionAuthorizationStatus) GlobalVariables.getUserSession().retrieveObject(BCConstants.BC_DOC_AUTHORIZATION_STATUS_SESSIONKEY); if (authorizationStatus == null) { // just return, BudgetExpansionAction.execute() will see the session time out // and redirect back to BudgetConstructionSelection return; } salarySettingForm.setDocumentActions(authorizationStatus.getDocumentActions()); salarySettingForm.setEditingMode(authorizationStatus.getEditingMode()); } protected void initAuthorization(SalarySettingBaseForm salarySettingForm) { Person user = GlobalVariables.getUserSession().getPerson(); boolean isAuthorized = SpringContext.getBean(IdentityManagementService.class).isAuthorized(user.getPrincipalId(), BCConstants.BUDGET_CONSTRUCTION_NAMESPACE, BCConstants.KimApiConstants.USE_ORG_SALARY_SETTING_PERMISSION_NAME, null); if (isAuthorized) { salarySettingForm.getDocumentActions().put(KRADConstants.KUALI_ACTION_CAN_EDIT, KRADConstants.KUALI_DEFAULT_TRUE_VALUE); } else { throw new AuthorizationException(user.getName(), "view", salarySettingForm.getAccountNumber() + ", " + salarySettingForm.getSubAccountNumber()); } if (!SpringContext.getBean(FiscalYearFunctionControlService.class).isBudgetUpdateAllowed(salarySettingForm.getUniversityFiscalYear())) { salarySettingForm.getEditingMode().put(BCConstants.EditModes.SYSTEM_VIEW_ONLY, KRADConstants.KUALI_DEFAULT_TRUE_VALUE); } BudgetConstructionAuthorizationStatus editStatus = new BudgetConstructionAuthorizationStatus(); editStatus.setDocumentActions(salarySettingForm.getDocumentActions()); editStatus.setEditingMode(salarySettingForm.getEditingMode()); GlobalVariables.getUserSession().addObject(BCConstants.BC_DOC_AUTHORIZATION_STATUS_SESSIONKEY, editStatus); } /** * save the information in the current form into underlying data store */ public ActionForward save(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { GlobalVariables.getMessageMap().putError(KFSConstants.GLOBAL_MESSAGES, KFSKeyConstants.ERROR_UNIMPLEMENTED, "Save For Salary Setting by Incumbent"); return mapping.findForward(KFSConstants.MAPPING_BASIC); } /** * @see org.kuali.kfs.module.bc.document.web.struts.BudgetExpansionAction#close(org.apache.struts.action.ActionMapping, * org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) */ @Override public ActionForward close(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { SalarySettingBaseForm salarySettingForm = (SalarySettingBaseForm) form; // ask a question before closing unless it has been answered String question = request.getParameter(KFSConstants.QUESTION_INST_ATTRIBUTE_NAME); if (StringUtils.isBlank(question)) { String questionText = kualiConfiguration.getPropertyValueAsString(KFSKeyConstants.QUESTION_SAVE_BEFORE_CLOSE); return this.performQuestionWithoutInput(mapping, salarySettingForm, request, response, KFSConstants.DOCUMENT_SAVE_BEFORE_CLOSE_QUESTION, questionText, KFSConstants.CONFIRMATION_QUESTION, KFSConstants.MAPPING_CLOSE, ""); } // save the salary setting if the user answers to the question with "Yes" (save and close) String buttonClicked = request.getParameter(KFSConstants.QUESTION_CLICKED_BUTTON); if (StringUtils.equals(KFSConstants.DOCUMENT_SAVE_BEFORE_CLOSE_QUESTION, question) && StringUtils.equals(ConfirmationQuestion.YES, buttonClicked)) { ActionForward saveAction = this.save(mapping, salarySettingForm, request, response); return saveAction; } // indicate the salary setting has been closed salarySettingForm.setSalarySettingClosed(true); return this.returnAfterClose(salarySettingForm, mapping, request, response); } /** * vacate the specified appointment funding line */ public ActionForward vacateSalarySettingLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { SalarySettingBaseForm salarySettingForm = (SalarySettingBaseForm) form; List<PendingBudgetConstructionAppointmentFunding> appointmentFundings = salarySettingForm.getAppointmentFundings(); PendingBudgetConstructionAppointmentFunding appointmentFunding = this.getSelectedFundingLine(request, salarySettingForm); salarySettingService.vacateAppointmentFunding(appointmentFundings, appointmentFunding); return mapping.findForward(KFSConstants.MAPPING_BASIC); } /** * mark the selected salary setting line as purged */ public ActionForward purgeSalarySettingLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { SalarySettingBaseForm salarySettingForm = (SalarySettingBaseForm) form; this.getSelectedFundingLine(request, salarySettingForm).setPurged(true); return mapping.findForward(KFSConstants.MAPPING_BASIC); } /** * restore the selected salary setting line if it is marked as purged */ public ActionForward restorePurgedSalarySettingLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { SalarySettingBaseForm salarySettingForm = (SalarySettingBaseForm) form; this.getSelectedFundingLine(request, salarySettingForm).setPurged(false); return mapping.findForward(KFSConstants.MAPPING_BASIC); } /** * mark the selected salary setting line as deleted */ public ActionForward deleteSalarySettingLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { SalarySettingBaseForm salarySettingForm = (SalarySettingBaseForm) form; PendingBudgetConstructionAppointmentFunding appointmentFunding = this.getSelectedFundingLine(request, salarySettingForm); salarySettingService.markAsDelete(appointmentFunding); return mapping.findForward(KFSConstants.MAPPING_BASIC); } /** * unmark the selected salary setting line that has been marked as deleted */ public ActionForward undeleteSalarySettingLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { SalarySettingBaseForm salarySettingForm = (SalarySettingBaseForm) form; this.getSelectedFundingLine(request, salarySettingForm).setAppointmentFundingDeleteIndicator(false); return mapping.findForward(KFSConstants.MAPPING_BASIC); } /** * revert the selected salary setting line that just has been marked as deleted */ public ActionForward revertSalarySettingLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { SalarySettingBaseForm salarySettingForm = (SalarySettingBaseForm) form; List<PendingBudgetConstructionAppointmentFunding> appointmentFundings = salarySettingForm.getAppointmentFundings(); PendingBudgetConstructionAppointmentFunding appointmentFunding = this.getSelectedFundingLine(request, salarySettingForm); salarySettingService.revert(appointmentFundings, appointmentFunding); return mapping.findForward(KFSConstants.MAPPING_BASIC); } /** * adjust the salary amount of the specified funding line */ public ActionForward adjustSalarySettingLinePercent(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { SalarySettingBaseForm salarySettingForm = (SalarySettingBaseForm) form; PendingBudgetConstructionAppointmentFunding appointmentFunding = this.getSelectedFundingLine(request, salarySettingForm); List<PendingBudgetConstructionAppointmentFunding> appointmentFundings = salarySettingForm.getAppointmentFundings(); String errorKeyPrefix = this.getErrorKeyPrefixOfAppointmentFundingLine(appointmentFundings, appointmentFunding); // retrieve corresponding document in advance in order to use the rule framework BudgetConstructionDocument document = budgetDocumentService.getBudgetConstructionDocument(appointmentFunding); if (document == null) { GlobalVariables.getMessageMap().putError(errorKeyPrefix, BCKeyConstants.ERROR_BUDGET_DOCUMENT_NOT_FOUND, appointmentFunding.getAppointmentFundingString()); return mapping.findForward(KFSConstants.MAPPING_BASIC); } return this.adjustSalarySettingLinePercent(mapping, salarySettingForm, appointmentFunding, document, errorKeyPrefix); } /** * adjust the requested salary amount of the given appointment funding line by pecent or given amount */ public ActionForward adjustSalarySettingLinePercent(ActionMapping mapping, ActionForm form, PendingBudgetConstructionAppointmentFunding appointmentFunding, BudgetConstructionDocument document, String errorKeyPrefix) { SalarySettingBaseForm salarySettingForm = (SalarySettingBaseForm) form; // validate the new appointment funding line BudgetExpansionEvent adjustPercentEvent = new AdjustSalarySettingLinePercentEvent(KFSConstants.EMPTY_STRING, errorKeyPrefix, document, appointmentFunding); boolean isValid = this.invokeRules(adjustPercentEvent); if (!isValid) { return mapping.findForward(KFSConstants.MAPPING_BASIC); } this.adjustSalary(appointmentFunding); return mapping.findForward(KFSConstants.MAPPING_BASIC); } /** * adjust the requested salary amount of the given appointment funding line */ protected void adjustSalary(PendingBudgetConstructionAppointmentFunding appointmentFunding) { String adjustmentMeasurement = appointmentFunding.getAdjustmentMeasurement(); if (BCConstants.SalaryAdjustmentMeasurement.PERCENT.measurement.equals(adjustmentMeasurement)) { salarySettingService.adjustRequestedSalaryByPercent(appointmentFunding); } else if (BCConstants.SalaryAdjustmentMeasurement.AMOUNT.measurement.equals(adjustmentMeasurement)) { salarySettingService.adjustRequestedSalaryByAmount(appointmentFunding); } } /** * normalize the hourly pay rate and annual pay amount */ public ActionForward normalizePayRateAndAmount(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { SalarySettingBaseForm salarySettingForm = (SalarySettingBaseForm) form; PendingBudgetConstructionAppointmentFunding appointmentFunding = this.getSelectedFundingLine(request, salarySettingForm); List<PendingBudgetConstructionAppointmentFunding> appointmentFundings = salarySettingForm.getAppointmentFundings(); String errorKeyPrefix = this.getErrorKeyPrefixOfAppointmentFundingLine(appointmentFundings, appointmentFunding); // retrieve corresponding document in advance in order to use the rule framework BudgetConstructionDocument document = budgetDocumentService.getBudgetConstructionDocument(appointmentFunding); if (document == null) { GlobalVariables.getMessageMap().putError(errorKeyPrefix, BCKeyConstants.ERROR_BUDGET_DOCUMENT_NOT_FOUND, appointmentFunding.getAppointmentFundingString()); return mapping.findForward(KFSConstants.MAPPING_BASIC); } // validate the new appointment funding line BudgetExpansionEvent normalizePayRateAndAmountEvent = new NormalizePayrateAndAmountEvent(KFSConstants.EMPTY_STRING, errorKeyPrefix, document, appointmentFunding); boolean isValid = this.invokeRules(normalizePayRateAndAmountEvent); if (!isValid) { return mapping.findForward(KFSConstants.MAPPING_BASIC); } salarySettingService.normalizePayRateAndAmount(appointmentFunding); return mapping.findForward(KFSConstants.MAPPING_BASIC); } /** * get the selected appointment funding line */ protected PendingBudgetConstructionAppointmentFunding getSelectedFundingLine(HttpServletRequest request, SalarySettingBaseForm salarySettingForm) { List<PendingBudgetConstructionAppointmentFunding> appointmentFundings = salarySettingForm.getAppointmentFundings(); int indexOfSelectedLine = this.getSelectedLine(request); return appointmentFundings.get(indexOfSelectedLine); } /** * execute the rules associated with the given event * * @param event the event that just occured * @return true if the rules associated with the given event pass; otherwise, false */ protected boolean invokeRules(KualiDocumentEvent event) { return kualiRuleService.applyRules(event); } /** * build the error key prefix based on the given information * * @param fundingAwareObjectName the name of object that holds the given set of appointment funding lines * @param appointmentFundings the given set of appointment funding lines * @param appointmentFunding the given appointment funding line * @return the error key prefix built from the given information */ protected String getErrorKeyPrefixOfAppointmentFundingLine(List<PendingBudgetConstructionAppointmentFunding> appointmentFundings, PendingBudgetConstructionAppointmentFunding appointmentFunding) { int indexOfFundingLine = appointmentFundings.indexOf(appointmentFunding); String pattern = "{0}.{1}[{2}]"; return MessageFormat.format(pattern, this.getFundingAwareObjectName(), BCPropertyConstants.PENDING_BUDGET_CONSTRUCTION_APPOINTMENT_FUNDING, indexOfFundingLine); } /** * return after salary setting is closed */ protected ActionForward returnAfterClose(SalarySettingBaseForm salarySettingForm, ActionMapping mapping, HttpServletRequest request, HttpServletResponse response) throws Exception { if (salarySettingForm.isBudgetByAccountMode()) { salarySettingForm.getCallBackMessages().add(BCKeyConstants.MESSAGE_BUDGET_SUCCESSFUL_CLOSE); return this.returnToCaller(mapping, salarySettingForm, request, response); } this.cleanupAnySessionForm(mapping, request); KNSGlobalVariables.getMessageList().add(BCKeyConstants.MESSAGE_BUDGET_SUCCESSFUL_CLOSE); return mapping.findForward(BCConstants.MAPPING_ORGANIZATION_SALARY_SETTING_RETURNING); } /** * get the name of object that holds a set of appointment funding lines */ protected abstract String getFundingAwareObjectName(); }