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 }