001 /* 002 * Copyright 2011 The Kuali Foundation. 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package org.kuali.kfs.module.bc.document.web.struts; 017 018 import java.text.MessageFormat; 019 import java.util.List; 020 021 import javax.servlet.http.HttpServletRequest; 022 import javax.servlet.http.HttpServletResponse; 023 import javax.servlet.http.HttpSession; 024 025 import org.apache.commons.lang.StringUtils; 026 import org.apache.struts.action.ActionForm; 027 import org.apache.struts.action.ActionForward; 028 import org.apache.struts.action.ActionMapping; 029 import org.kuali.kfs.fp.service.FiscalYearFunctionControlService; 030 import org.kuali.kfs.module.bc.BCConstants; 031 import org.kuali.kfs.module.bc.BCKeyConstants; 032 import org.kuali.kfs.module.bc.BCPropertyConstants; 033 import org.kuali.kfs.module.bc.businessobject.BudgetConstructionAuthorizationStatus; 034 import org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding; 035 import org.kuali.kfs.module.bc.document.BudgetConstructionDocument; 036 import org.kuali.kfs.module.bc.document.service.BudgetDocumentService; 037 import org.kuali.kfs.module.bc.document.service.SalarySettingService; 038 import org.kuali.kfs.module.bc.document.validation.event.AdjustSalarySettingLinePercentEvent; 039 import org.kuali.kfs.module.bc.document.validation.event.BudgetExpansionEvent; 040 import org.kuali.kfs.module.bc.document.validation.event.NormalizePayrateAndAmountEvent; 041 import org.kuali.kfs.sys.KFSConstants; 042 import org.kuali.kfs.sys.KFSKeyConstants; 043 import org.kuali.kfs.sys.context.SpringContext; 044 import org.kuali.rice.kim.bo.Person; 045 import org.kuali.rice.kim.service.IdentityManagementService; 046 import org.kuali.rice.kns.exception.AuthorizationException; 047 import org.kuali.rice.kns.question.ConfirmationQuestion; 048 import org.kuali.rice.kns.rule.event.KualiDocumentEvent; 049 import org.kuali.rice.kns.service.BusinessObjectService; 050 import org.kuali.rice.kns.service.KualiConfigurationService; 051 import org.kuali.rice.kns.service.KualiRuleService; 052 import org.kuali.rice.kns.util.GlobalVariables; 053 import org.kuali.rice.kns.util.KNSConstants; 054 055 /** 056 * the base action class for salary setting, which provides the implementations of common actions of the salary setting screens 057 */ 058 public abstract class SalarySettingBaseAction extends BudgetExpansionAction { 059 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(SalarySettingBaseAction.class); 060 061 private SalarySettingService salarySettingService = SpringContext.getBean(SalarySettingService.class); 062 private BusinessObjectService businessObjectService = SpringContext.getBean(BusinessObjectService.class); 063 private KualiConfigurationService kualiConfiguration = SpringContext.getBean(KualiConfigurationService.class); 064 private BudgetDocumentService budgetDocumentService = SpringContext.getBean(BudgetDocumentService.class); 065 private KualiRuleService kualiRuleService = SpringContext.getBean(KualiRuleService.class); 066 067 /** 068 * loads the data for the expansion screen based on the passed in url parameters 069 */ 070 public abstract ActionForward loadExpansionScreen(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception; 071 072 /** 073 * @see org.kuali.rice.kns.web.struts.action.KualiAction#execute(org.apache.struts.action.ActionMapping, 074 * org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) 075 */ 076 @Override 077 public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 078 SalarySettingBaseForm salarySettingForm = (SalarySettingBaseForm) form; 079 080 // if org sal setting we need to initialize authorization 081 if (!salarySettingForm.isBudgetByAccountMode() && salarySettingForm.getMethodToCall().equals(BCConstants.LOAD_EXPANSION_SCREEN_METHOD)) { 082 initAuthorization(salarySettingForm); 083 } 084 085 populateAuthorizationFields(salarySettingForm); 086 087 ActionForward forward = super.execute(mapping, form, request, response); 088 if (!salarySettingForm.isLostSession()) { 089 salarySettingForm.postProcessBCAFLines(); 090 091 // re-init the session form if session scoped 092 if (salarySettingForm.getMethodToCall().equals("refresh")) { 093 if (BCConstants.MAPPING_SCOPE_SESSION.equals(mapping.getScope())) { 094 HttpSession sess = request.getSession(Boolean.FALSE); 095 String formName = mapping.getAttribute(); 096 sess.setAttribute(formName, salarySettingForm); 097 } 098 } 099 } 100 return forward; 101 } 102 103 protected void populateAuthorizationFields(SalarySettingBaseForm salarySettingForm) { 104 BudgetConstructionAuthorizationStatus authorizationStatus = (BudgetConstructionAuthorizationStatus) GlobalVariables.getUserSession().retrieveObject(BCConstants.BC_DOC_AUTHORIZATION_STATUS_SESSIONKEY); 105 106 if (authorizationStatus == null) { 107 // just return, BudgetExpansionAction.execute() will see the session time out 108 // and redirect back to BudgetConstructionSelection 109 return; 110 } 111 112 salarySettingForm.setDocumentActions(authorizationStatus.getDocumentActions()); 113 salarySettingForm.setEditingMode(authorizationStatus.getEditingMode()); 114 } 115 116 protected void initAuthorization(SalarySettingBaseForm salarySettingForm) { 117 Person user = GlobalVariables.getUserSession().getPerson(); 118 119 boolean isAuthorized = SpringContext.getBean(IdentityManagementService.class).isAuthorized(user.getPrincipalId(), BCConstants.BUDGET_CONSTRUCTION_NAMESPACE, BCConstants.KimConstants.USE_ORG_SALARY_SETTING_PERMISSION_NAME, null, null); 120 if (isAuthorized) { 121 salarySettingForm.getDocumentActions().put(KNSConstants.KUALI_ACTION_CAN_EDIT, KNSConstants.KUALI_DEFAULT_TRUE_VALUE); 122 } 123 else { 124 throw new AuthorizationException(user.getName(), "view", salarySettingForm.getAccountNumber() + ", " + salarySettingForm.getSubAccountNumber()); 125 } 126 127 if (!SpringContext.getBean(FiscalYearFunctionControlService.class).isBudgetUpdateAllowed(salarySettingForm.getUniversityFiscalYear())) { 128 salarySettingForm.getEditingMode().put(BCConstants.EditModes.SYSTEM_VIEW_ONLY, KNSConstants.KUALI_DEFAULT_TRUE_VALUE); 129 } 130 131 BudgetConstructionAuthorizationStatus editStatus = new BudgetConstructionAuthorizationStatus(); 132 editStatus.setDocumentActions(salarySettingForm.getDocumentActions()); 133 editStatus.setEditingMode(salarySettingForm.getEditingMode()); 134 135 GlobalVariables.getUserSession().addObject(BCConstants.BC_DOC_AUTHORIZATION_STATUS_SESSIONKEY, editStatus); 136 } 137 138 /** 139 * save the information in the current form into underlying data store 140 */ 141 public ActionForward save(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 142 GlobalVariables.getMessageMap().putError(KFSConstants.GLOBAL_MESSAGES, KFSKeyConstants.ERROR_UNIMPLEMENTED, "Save For Salary Setting by Incumbent"); 143 return mapping.findForward(KFSConstants.MAPPING_BASIC); 144 } 145 146 /** 147 * @see org.kuali.kfs.module.bc.document.web.struts.BudgetExpansionAction#close(org.apache.struts.action.ActionMapping, 148 * org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) 149 */ 150 @Override 151 public ActionForward close(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 152 SalarySettingBaseForm salarySettingForm = (SalarySettingBaseForm) form; 153 154 // ask a question before closing unless it has been answered 155 String question = request.getParameter(KFSConstants.QUESTION_INST_ATTRIBUTE_NAME); 156 if (StringUtils.isBlank(question)) { 157 String questionText = kualiConfiguration.getPropertyString(KFSKeyConstants.QUESTION_SAVE_BEFORE_CLOSE); 158 return this.performQuestionWithoutInput(mapping, salarySettingForm, request, response, KFSConstants.DOCUMENT_SAVE_BEFORE_CLOSE_QUESTION, questionText, KFSConstants.CONFIRMATION_QUESTION, KFSConstants.MAPPING_CLOSE, ""); 159 } 160 161 // save the salary setting if the user answers to the question with "Yes" (save and close) 162 String buttonClicked = request.getParameter(KFSConstants.QUESTION_CLICKED_BUTTON); 163 if (StringUtils.equals(KFSConstants.DOCUMENT_SAVE_BEFORE_CLOSE_QUESTION, question) && StringUtils.equals(ConfirmationQuestion.YES, buttonClicked)) { 164 ActionForward saveAction = this.save(mapping, salarySettingForm, request, response); 165 166 return saveAction; 167 } 168 169 // indicate the salary setting has been closed 170 salarySettingForm.setSalarySettingClosed(true); 171 172 return this.returnAfterClose(salarySettingForm, mapping, request, response); 173 } 174 175 /** 176 * vacate the specified appointment funding line 177 */ 178 public ActionForward vacateSalarySettingLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 179 SalarySettingBaseForm salarySettingForm = (SalarySettingBaseForm) form; 180 List<PendingBudgetConstructionAppointmentFunding> appointmentFundings = salarySettingForm.getAppointmentFundings(); 181 PendingBudgetConstructionAppointmentFunding appointmentFunding = this.getSelectedFundingLine(request, salarySettingForm); 182 183 salarySettingService.vacateAppointmentFunding(appointmentFundings, appointmentFunding); 184 185 return mapping.findForward(KFSConstants.MAPPING_BASIC); 186 } 187 188 /** 189 * mark the selected salary setting line as purged 190 */ 191 public ActionForward purgeSalarySettingLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 192 SalarySettingBaseForm salarySettingForm = (SalarySettingBaseForm) form; 193 194 this.getSelectedFundingLine(request, salarySettingForm).setPurged(true); 195 196 return mapping.findForward(KFSConstants.MAPPING_BASIC); 197 } 198 199 /** 200 * restore the selected salary setting line if it is marked as purged 201 */ 202 public ActionForward restorePurgedSalarySettingLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 203 SalarySettingBaseForm salarySettingForm = (SalarySettingBaseForm) form; 204 205 this.getSelectedFundingLine(request, salarySettingForm).setPurged(false); 206 207 return mapping.findForward(KFSConstants.MAPPING_BASIC); 208 } 209 210 /** 211 * mark the selected salary setting line as deleted 212 */ 213 public ActionForward deleteSalarySettingLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 214 SalarySettingBaseForm salarySettingForm = (SalarySettingBaseForm) form; 215 PendingBudgetConstructionAppointmentFunding appointmentFunding = this.getSelectedFundingLine(request, salarySettingForm); 216 217 salarySettingService.markAsDelete(appointmentFunding); 218 219 return mapping.findForward(KFSConstants.MAPPING_BASIC); 220 } 221 222 /** 223 * unmark the selected salary setting line that has been marked as deleted 224 */ 225 public ActionForward undeleteSalarySettingLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 226 SalarySettingBaseForm salarySettingForm = (SalarySettingBaseForm) form; 227 228 this.getSelectedFundingLine(request, salarySettingForm).setAppointmentFundingDeleteIndicator(false); 229 230 return mapping.findForward(KFSConstants.MAPPING_BASIC); 231 } 232 233 /** 234 * revert the selected salary setting line that just has been marked as deleted 235 */ 236 public ActionForward revertSalarySettingLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 237 SalarySettingBaseForm salarySettingForm = (SalarySettingBaseForm) form; 238 List<PendingBudgetConstructionAppointmentFunding> appointmentFundings = salarySettingForm.getAppointmentFundings(); 239 PendingBudgetConstructionAppointmentFunding appointmentFunding = this.getSelectedFundingLine(request, salarySettingForm); 240 241 salarySettingService.revert(appointmentFundings, appointmentFunding); 242 243 return mapping.findForward(KFSConstants.MAPPING_BASIC); 244 } 245 246 /** 247 * adjust the salary amount of the specified funding line 248 */ 249 public ActionForward adjustSalarySettingLinePercent(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 250 SalarySettingBaseForm salarySettingForm = (SalarySettingBaseForm) form; 251 PendingBudgetConstructionAppointmentFunding appointmentFunding = this.getSelectedFundingLine(request, salarySettingForm); 252 List<PendingBudgetConstructionAppointmentFunding> appointmentFundings = salarySettingForm.getAppointmentFundings(); 253 254 String errorKeyPrefix = this.getErrorKeyPrefixOfAppointmentFundingLine(appointmentFundings, appointmentFunding); 255 256 // retrieve corresponding document in advance in order to use the rule framework 257 BudgetConstructionDocument document = budgetDocumentService.getBudgetConstructionDocument(appointmentFunding); 258 if (document == null) { 259 GlobalVariables.getMessageMap().putError(errorKeyPrefix, BCKeyConstants.ERROR_BUDGET_DOCUMENT_NOT_FOUND, appointmentFunding.getAppointmentFundingString()); 260 return mapping.findForward(KFSConstants.MAPPING_BASIC); 261 } 262 263 return this.adjustSalarySettingLinePercent(mapping, salarySettingForm, appointmentFunding, document, errorKeyPrefix); 264 } 265 266 /** 267 * adjust the requested salary amount of the given appointment funding line by pecent or given amount 268 */ 269 public ActionForward adjustSalarySettingLinePercent(ActionMapping mapping, ActionForm form, PendingBudgetConstructionAppointmentFunding appointmentFunding, BudgetConstructionDocument document, String errorKeyPrefix) { 270 SalarySettingBaseForm salarySettingForm = (SalarySettingBaseForm) form; 271 272 // validate the new appointment funding line 273 BudgetExpansionEvent adjustPercentEvent = new AdjustSalarySettingLinePercentEvent(KFSConstants.EMPTY_STRING, errorKeyPrefix, document, appointmentFunding); 274 boolean isValid = this.invokeRules(adjustPercentEvent); 275 if (!isValid) { 276 return mapping.findForward(KFSConstants.MAPPING_BASIC); 277 } 278 279 this.adjustSalary(appointmentFunding); 280 return mapping.findForward(KFSConstants.MAPPING_BASIC); 281 } 282 283 /** 284 * adjust the requested salary amount of the given appointment funding line 285 */ 286 protected void adjustSalary(PendingBudgetConstructionAppointmentFunding appointmentFunding) { 287 String adjustmentMeasurement = appointmentFunding.getAdjustmentMeasurement(); 288 if (BCConstants.SalaryAdjustmentMeasurement.PERCENT.measurement.equals(adjustmentMeasurement)) { 289 salarySettingService.adjustRequestedSalaryByPercent(appointmentFunding); 290 } 291 else if (BCConstants.SalaryAdjustmentMeasurement.AMOUNT.measurement.equals(adjustmentMeasurement)) { 292 salarySettingService.adjustRequestedSalaryByAmount(appointmentFunding); 293 } 294 } 295 296 /** 297 * normalize the hourly pay rate and annual pay amount 298 */ 299 public ActionForward normalizePayRateAndAmount(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { 300 SalarySettingBaseForm salarySettingForm = (SalarySettingBaseForm) form; 301 PendingBudgetConstructionAppointmentFunding appointmentFunding = this.getSelectedFundingLine(request, salarySettingForm); 302 List<PendingBudgetConstructionAppointmentFunding> appointmentFundings = salarySettingForm.getAppointmentFundings(); 303 String errorKeyPrefix = this.getErrorKeyPrefixOfAppointmentFundingLine(appointmentFundings, appointmentFunding); 304 305 // retrieve corresponding document in advance in order to use the rule framework 306 BudgetConstructionDocument document = budgetDocumentService.getBudgetConstructionDocument(appointmentFunding); 307 if (document == null) { 308 GlobalVariables.getMessageMap().putError(errorKeyPrefix, BCKeyConstants.ERROR_BUDGET_DOCUMENT_NOT_FOUND, appointmentFunding.getAppointmentFundingString()); 309 return mapping.findForward(KFSConstants.MAPPING_BASIC); 310 } 311 312 // validate the new appointment funding line 313 BudgetExpansionEvent normalizePayRateAndAmountEvent = new NormalizePayrateAndAmountEvent(KFSConstants.EMPTY_STRING, errorKeyPrefix, document, appointmentFunding); 314 boolean isValid = this.invokeRules(normalizePayRateAndAmountEvent); 315 if (!isValid) { 316 return mapping.findForward(KFSConstants.MAPPING_BASIC); 317 } 318 319 salarySettingService.normalizePayRateAndAmount(appointmentFunding); 320 return mapping.findForward(KFSConstants.MAPPING_BASIC); 321 } 322 323 /** 324 * get the selected appointment funding line 325 */ 326 protected PendingBudgetConstructionAppointmentFunding getSelectedFundingLine(HttpServletRequest request, SalarySettingBaseForm salarySettingForm) { 327 List<PendingBudgetConstructionAppointmentFunding> appointmentFundings = salarySettingForm.getAppointmentFundings(); 328 329 int indexOfSelectedLine = this.getSelectedLine(request); 330 return appointmentFundings.get(indexOfSelectedLine); 331 } 332 333 /** 334 * execute the rules associated with the given event 335 * 336 * @param event the event that just occured 337 * @return true if the rules associated with the given event pass; otherwise, false 338 */ 339 protected boolean invokeRules(KualiDocumentEvent event) { 340 return kualiRuleService.applyRules(event); 341 } 342 343 /** 344 * build the error key prefix based on the given information 345 * 346 * @param fundingAwareObjectName the name of object that holds the given set of appointment funding lines 347 * @param appointmentFundings the given set of appointment funding lines 348 * @param appointmentFunding the given appointment funding line 349 * @return the error key prefix built from the given information 350 */ 351 protected String getErrorKeyPrefixOfAppointmentFundingLine(List<PendingBudgetConstructionAppointmentFunding> appointmentFundings, PendingBudgetConstructionAppointmentFunding appointmentFunding) { 352 int indexOfFundingLine = appointmentFundings.indexOf(appointmentFunding); 353 String pattern = "{0}.{1}[{2}]"; 354 355 return MessageFormat.format(pattern, this.getFundingAwareObjectName(), BCPropertyConstants.PENDING_BUDGET_CONSTRUCTION_APPOINTMENT_FUNDING, indexOfFundingLine); 356 } 357 358 /** 359 * return after salary setting is closed 360 */ 361 protected ActionForward returnAfterClose(SalarySettingBaseForm salarySettingForm, ActionMapping mapping, HttpServletRequest request, HttpServletResponse response) throws Exception { 362 if (salarySettingForm.isBudgetByAccountMode()) { 363 salarySettingForm.getCallBackMessages().add(BCKeyConstants.MESSAGE_BUDGET_SUCCESSFUL_CLOSE); 364 return this.returnToCaller(mapping, salarySettingForm, request, response); 365 } 366 367 this.cleanupAnySessionForm(mapping, request); 368 GlobalVariables.getMessageList().add(BCKeyConstants.MESSAGE_BUDGET_SUCCESSFUL_CLOSE); 369 return mapping.findForward(BCConstants.MAPPING_ORGANIZATION_SALARY_SETTING_RETURNING); 370 } 371 372 /** 373 * get the name of object that holds a set of appointment funding lines 374 */ 375 protected abstract String getFundingAwareObjectName(); 376 }