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.math.BigDecimal;
019    import java.util.Arrays;
020    import java.util.Collections;
021    import java.util.HashMap;
022    import java.util.List;
023    import java.util.Map;
024    import java.util.Properties;
025    
026    import javax.servlet.http.HttpServletRequest;
027    import javax.servlet.http.HttpServletResponse;
028    
029    import org.apache.commons.lang.StringUtils;
030    import org.apache.struts.action.ActionForm;
031    import org.apache.struts.action.ActionForward;
032    import org.apache.struts.action.ActionMapping;
033    import org.kuali.kfs.module.bc.BCConstants;
034    import org.kuali.kfs.module.bc.BCKeyConstants;
035    import org.kuali.kfs.module.bc.BCPropertyConstants;
036    import org.kuali.kfs.module.bc.BCConstants.LockStatus;
037    import org.kuali.kfs.module.bc.BCConstants.MonthSpreadDeleteType;
038    import org.kuali.kfs.module.bc.businessobject.BudgetConstructionAccountOrganizationHierarchy;
039    import org.kuali.kfs.module.bc.businessobject.BudgetConstructionAuthorizationStatus;
040    import org.kuali.kfs.module.bc.businessobject.BudgetConstructionHeader;
041    import org.kuali.kfs.module.bc.businessobject.BudgetConstructionLockStatus;
042    import org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionGeneralLedger;
043    import org.kuali.kfs.module.bc.document.BudgetConstructionDocument;
044    import org.kuali.kfs.module.bc.document.service.BudgetConstructionMonthlyBudgetsCreateDeleteService;
045    import org.kuali.kfs.module.bc.document.service.BudgetDocumentService;
046    import org.kuali.kfs.module.bc.document.service.LockService;
047    import org.kuali.kfs.module.bc.document.validation.event.AddPendingBudgetGeneralLedgerLineEvent;
048    import org.kuali.kfs.module.bc.document.validation.event.DeletePendingBudgetGeneralLedgerLineEvent;
049    import org.kuali.kfs.module.bc.exception.BudgetConstructionDocumentAuthorizationException;
050    import org.kuali.kfs.module.bc.identity.BudgetConstructionNoAccessMessageSetting;
051    import org.kuali.kfs.sys.KFSConstants;
052    import org.kuali.kfs.sys.KFSKeyConstants;
053    import org.kuali.kfs.sys.KFSPropertyConstants;
054    import org.kuali.kfs.sys.context.SpringContext;
055    import org.kuali.kfs.sys.identity.KfsKimAttributes;
056    import org.kuali.rice.kew.exception.WorkflowException;
057    import org.kuali.rice.kim.bo.Person;
058    import org.kuali.rice.kim.bo.role.dto.KimRoleInfo;
059    import org.kuali.rice.kim.bo.types.dto.AttributeSet;
060    import org.kuali.rice.kim.bo.types.dto.KimTypeInfo;
061    import org.kuali.rice.kim.service.KimTypeInfoService;
062    import org.kuali.rice.kim.service.RoleManagementService;
063    import org.kuali.rice.kim.service.support.KimRoleTypeService;
064    import org.kuali.rice.kim.util.KimConstants;
065    import org.kuali.rice.kns.UserSession;
066    import org.kuali.rice.kns.document.Document;
067    import org.kuali.rice.kns.document.authorization.TransactionalDocumentAuthorizer;
068    import org.kuali.rice.kns.exception.AuthorizationException;
069    import org.kuali.rice.kns.exception.UnknownDocumentIdException;
070    import org.kuali.rice.kns.question.ConfirmationQuestion;
071    import org.kuali.rice.kns.service.BusinessObjectService;
072    import org.kuali.rice.kns.service.DocumentHelperService;
073    import org.kuali.rice.kns.service.KualiConfigurationService;
074    import org.kuali.rice.kns.service.KualiRuleService;
075    import org.kuali.rice.kns.service.PersistenceService;
076    import org.kuali.rice.kns.service.SessionDocumentService;
077    import org.kuali.rice.kns.util.GlobalVariables;
078    import org.kuali.rice.kns.util.KNSConstants;
079    import org.kuali.rice.kns.util.KualiDecimal;
080    import org.kuali.rice.kns.util.KualiInteger;
081    import org.kuali.rice.kns.util.UrlFactory;
082    import org.kuali.rice.kns.web.struts.action.KualiTransactionalDocumentActionBase;
083    import org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase;
084    import org.kuali.rice.kns.web.struts.form.KualiForm;
085    import org.kuali.rice.kns.workflow.service.KualiWorkflowDocument;
086    
087    /**
088     * need to figure out if this should extend KualiAction, KualiDocumentActionBase or KualiTransactionDocumentActionBase
089     */
090    public class BudgetConstructionAction extends KualiTransactionalDocumentActionBase {
091        protected static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(BudgetConstructionAction.class);
092    
093        /**
094         * Entry point to all actions Checks for cases where methodToCall is loadDocument, performAccountPullup or
095         * performAccountPushdown and creates global messages to describe the new editingMode state. Also handles document locking if
096         * the editingMode is BudgetConstructionEditMode.FULL_ENTRY. (Re)Populates the pullup and pushdown selection controls based on
097         * the current level of the document and the user's approval access for the levels above and below the current level.
098         * 
099         * @see org.kuali.rice.kns.web.struts.action.KualiTransactionalDocumentActionBase#execute(org.apache.struts.action.ActionMapping,
100         *      org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
101         */
102        @Override
103        public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
104            BudgetConstructionForm budgetConstructionForm = (BudgetConstructionForm) form;
105    
106            Boolean isBCHeartBeating = (Boolean) GlobalVariables.getUserSession().retrieveObject(BCConstants.BC_HEARTBEAT_SESSIONFLAG);
107            if (isBCHeartBeating == null) {
108                if (budgetConstructionForm.isPickListMode()) {
109                    budgetConstructionForm.setPickListClose(true);
110                    GlobalVariables.getMessageList().add(BCKeyConstants.MESSAGE_BUDGET_PREVIOUS_SESSION_TIMEOUT);
111                    return mapping.findForward(KFSConstants.MAPPING_BASIC);
112    
113                }
114    
115                Properties parameters = new Properties();
116                parameters.put(KFSConstants.DISPATCH_REQUEST_PARAMETER, BCConstants.LOAD_EXPANSION_SCREEN_METHOD_SESSION_TIMEOUT);
117    
118                String lookupUrl = UrlFactory.parameterizeUrl("/" + BCConstants.BC_SELECTION_ACTION, parameters);
119    
120                return new ActionForward(lookupUrl, true);
121            }
122    
123            // this is only used to indicate to the rules the user has clicked save or close save-yes
124            // which forces tighter checks (nonZeroRequest amount) when access is cleanup mode
125            if (budgetConstructionForm.getBudgetConstructionDocument().isCleanupModeActionForceCheck()) {
126                budgetConstructionForm.getBudgetConstructionDocument().setCleanupModeActionForceCheck(Boolean.FALSE);
127            }
128    
129            // TODO: the catch code comes from KualiDocumentActionBase.loadDocument()
130            // this allows the top portion of the document to be populated and the close button to function
131            // and also displays all the possible error messages for now
132            // we need to update this once KIM is adjusted to handle BC no access cases
133            // and maybe just show the authorization exception message
134            ActionForward forward = null;
135            try {
136                forward = super.execute(mapping, form, request, response);
137            }
138            catch (AuthorizationException e) {
139                forward = mapping.findForward(KFSConstants.MAPPING_BASIC);
140                String docId = budgetConstructionForm.getDocId();
141                Document doc = null;
142                doc = getDocumentService().getByDocumentHeaderId(docId);
143                if (doc == null) {
144                    throw new UnknownDocumentIdException("Document no longer exists.  It may have been cancelled before being saved.");
145                }
146                KualiWorkflowDocument workflowDocument = doc.getDocumentHeader().getWorkflowDocument();
147                // if (!getDocumentHelperService().getDocumentAuthorizer(doc).canOpen(doc,
148                // GlobalVariables.getUserSession().getPerson())) {
149                // throw buildAuthorizationException("open", doc);
150                // }
151                // re-retrieve the document using the current user's session - remove the system user from the WorkflowDcument object
152                if (workflowDocument != doc.getDocumentHeader().getWorkflowDocument()) {
153                    LOG.warn("Workflow document changed via canOpen check");
154                    doc.getDocumentHeader().setWorkflowDocument(workflowDocument);
155                }
156                budgetConstructionForm.setDocument(doc);
157                KualiWorkflowDocument workflowDoc = doc.getDocumentHeader().getWorkflowDocument();
158                budgetConstructionForm.setDocTypeName(workflowDoc.getDocumentType());
159                // KualiDocumentFormBase.populate() needs this updated in the session
160                GlobalVariables.getUserSession().setWorkflowDocument(workflowDoc);
161    
162                budgetConstructionForm.setSecurityNoAccess(true);
163                setBudgetDocumentNoAccessMessage(budgetConstructionForm);
164                budgetConstructionForm.getDocumentActions().put(KNSConstants.KUALI_ACTION_CAN_CLOSE, Boolean.TRUE);
165    
166            }
167    
168            // apprise user of granted access
169            if (budgetConstructionForm.getMethodToCall().equals(BCConstants.BC_DOCUMENT_METHOD) || budgetConstructionForm.getMethodToCall().equals(BCConstants.BC_DOCUMENT_PULLUP_METHOD) || budgetConstructionForm.getMethodToCall().equals(BCConstants.BC_DOCUMENT_PUSHDOWN_METHOD)) {
170    
171                // init the account org hier state on initial load only - this is stored as hiddens
172                if (budgetConstructionForm.getMethodToCall().equals(BCConstants.BC_DOCUMENT_METHOD)) {
173                    budgetConstructionForm.setAccountOrgHierLevels(SpringContext.getBean(BudgetDocumentService.class).getPushPullLevelList(budgetConstructionForm.getBudgetConstructionDocument(), GlobalVariables.getUserSession().getPerson()));
174                }
175    
176                if (budgetConstructionForm.isSystemViewOnly()) {
177                    GlobalVariables.getMessageList().add(BCKeyConstants.MESSAGE_BUDGET_SYSTEM_VIEW_ONLY);
178                }
179    
180                if (!budgetConstructionForm.isEditAllowed()) {
181                    GlobalVariables.getMessageList().add(BCKeyConstants.MESSAGE_BUDGET_VIEW_ONLY);
182                }
183    
184                if (budgetConstructionForm.isEditAllowed()) {
185                    if (budgetConstructionForm.isSystemViewOnly()) {
186                        GlobalVariables.getMessageList().add(BCKeyConstants.MESSAGE_BUDGET_VIEW_ONLY);
187                    }
188                    else {
189    
190                        // tell the user if the document is in not budgetable mode
191                        budgetConstructionForm.getBudgetConstructionDocument().setBudgetableDocument(SpringContext.getBean(BudgetDocumentService.class).isBudgetableDocumentNoWagesCheck(budgetConstructionForm.getBudgetConstructionDocument()));
192                        // budgetConstructionForm.setBudgetableDocument(SpringContext.getBean(BudgetDocumentService.class).isBudgetableDocumentNoWagesCheck(budgetConstructionForm.getBudgetConstructionDocument()));
193                        if (!budgetConstructionForm.isBudgetableDocument()) {
194                            GlobalVariables.getMessageList().add(BCKeyConstants.MESSAGE_BUDGET_DOCUMENT_NOT_BUDGETABLE);
195                        }
196    
197                        GlobalVariables.getMessageList().add(BCKeyConstants.MESSAGE_BUDGET_EDIT_ACCESS);
198                    }
199    
200                    if (!budgetConstructionForm.isSystemViewOnly()) {
201                        LockService lockService = SpringContext.getBean(LockService.class);
202                        HashMap primaryKey = new HashMap();
203                        primaryKey.put(KFSPropertyConstants.DOCUMENT_NUMBER, budgetConstructionForm.getDocument().getDocumentNumber());
204    
205                        BudgetConstructionHeader budgetConstructionHeader = (BudgetConstructionHeader) SpringContext.getBean(BusinessObjectService.class).findByPrimaryKey(BudgetConstructionHeader.class, primaryKey);
206                        if (budgetConstructionHeader != null) {
207                            // BudgetConstructionLockStatus bcLockStatus = lockService.lockAccount(budgetConstructionHeader,
208                            // GlobalVariables.getUserSession().getPerson().getPrincipalId());
209                            BudgetConstructionLockStatus bcLockStatus = lockService.lockAccountAndCommit(budgetConstructionHeader, GlobalVariables.getUserSession().getPerson().getPrincipalId());
210    
211                            if (bcLockStatus.getLockStatus() == LockStatus.SUCCESS) {
212    
213                                // update the document version number
214                                // so the saved lock of header doesn't produce an optimistic lock error
215                                // and has the info we just put in the header so doc save doesn't wipe out the lock
216                                budgetConstructionForm.getBudgetConstructionDocument().setVersionNumber(bcLockStatus.getBudgetConstructionHeader().getVersionNumber());
217                                budgetConstructionForm.getBudgetConstructionDocument().setBudgetLockUserIdentifier(bcLockStatus.getBudgetConstructionHeader().getBudgetLockUserIdentifier());
218                            }
219                            else {
220                                if (bcLockStatus.getLockStatus() == LockStatus.BY_OTHER) {
221                                    String lockerName = SpringContext.getBean(org.kuali.rice.kim.service.PersonService.class).getPerson(bcLockStatus.getAccountLockOwner()).getName();
222                                    this.cleanupForLockError(budgetConstructionForm);
223                                    GlobalVariables.getMessageMap().putError(KFSConstants.GLOBAL_ERRORS, BCKeyConstants.ERROR_BUDGET_DOCUMENT_LOCKED, lockerName);
224                                    return forward;
225                                }
226                                else {
227                                    if (bcLockStatus.getLockStatus() == LockStatus.FLOCK_FOUND) {
228                                        this.cleanupForLockError(budgetConstructionForm);
229                                        GlobalVariables.getMessageMap().putError(KFSConstants.GLOBAL_ERRORS, BCKeyConstants.ERROR_BUDGET_FUNDING_LOCKED);
230                                        return forward;
231                                    }
232                                    else {
233                                        this.cleanupForLockError(budgetConstructionForm);
234                                        GlobalVariables.getMessageMap().putError(KFSConstants.GLOBAL_ERRORS, BCKeyConstants.ERROR_BUDGET_DOCUMENT_OTHER);
235                                        return forward;
236                                    }
237                                }
238                            }
239                        }
240                        else {
241                            // unlikely
242                            throw new BudgetConstructionDocumentAuthorizationException(GlobalVariables.getUserSession().getPerson().getName(), "open", budgetConstructionForm.getDocument().getDocumentHeader().getDocumentNumber(), "(can't find document for locking)", budgetConstructionForm.isPickListMode());
243                        }
244    
245                        // if editing, check if 2plg adjustment needed and calc benefits
246                        // since checkTwoPlugAdjusmtent will only be set in docHandler during initial load
247                        // if document is initially view only we want the 2plg adjustment to happen if the user does a pullup to edit
248                        if (budgetConstructionForm.isCheckTwoPlugAdjustment() && budgetConstructionForm.getBudgetConstructionDocument().isContainsTwoPlug() && !budgetConstructionForm.getBudgetConstructionDocument().isSalarySettingOnly()) {
249                            // do 2plg related benefits calc and adjust 2plg for any diff - reset checkTwoPlugAdjusment
250                            budgetConstructionForm.setCheckTwoPlugAdjustment(false);
251                            this.adjustForSalarySettingChanges(budgetConstructionForm);
252                        }
253                    }
254    
255                    // getting here implies a lock or system view mode, try to build a pullup list
256                    // pushdown is only allowed if user has edit access (regardless of system view only mode)
257                    if (budgetConstructionForm.getBudgetConstructionDocument().getOrganizationLevelCode() != 0) {
258                        if (!budgetConstructionForm.getAccountOrgHierLevels().isEmpty()) {
259                            budgetConstructionForm.populatePushPullLevelKeyLabels(budgetConstructionForm.getBudgetConstructionDocument(), budgetConstructionForm.getAccountOrgHierLevels(), false);
260                        }
261                    }
262                } // FULL_ENTRY
263    
264                // pullup is allowed regardless of editMode
265                if (!budgetConstructionForm.getAccountOrgHierLevels().isEmpty()) {
266                    budgetConstructionForm.populatePushPullLevelKeyLabels(budgetConstructionForm.getBudgetConstructionDocument(), budgetConstructionForm.getAccountOrgHierLevels(), true);
267                }
268            }
269    
270            // cleanup the session edit mode store so we don't side effect organization salary setting
271            if (budgetConstructionForm.isClosingDocument()) {
272                GlobalVariables.getUserSession().removeObject(BCConstants.BC_DOC_AUTHORIZATION_STATUS_SESSIONKEY);
273            }
274    
275            return forward;
276        }
277        
278        /**
279         * Finds the role type service associated with the document viewer role, than calls method on role type service to set the no
280         * access message
281         * 
282         * @param budgetConstructionForm form containing budget document
283         */
284        protected void setBudgetDocumentNoAccessMessage(BudgetConstructionForm budgetConstructionForm) {
285            KimRoleInfo roleInfo = SpringContext.getBean(RoleManagementService.class).getRoleByName(BCConstants.BUDGET_CONSTRUCTION_NAMESPACE, BCConstants.KimConstants.DOCUMENT_VIEWER_ROLE_NAME);
286            KimTypeInfo typeInfo = SpringContext.getBean(KimTypeInfoService.class).getKimType(roleInfo.getKimTypeId());
287    
288            if (StringUtils.isNotBlank(typeInfo.getKimTypeServiceName())) {
289                KimRoleTypeService roleTypeService = (KimRoleTypeService) SpringContext.getService(typeInfo.getKimTypeServiceName());
290                if (roleTypeService instanceof BudgetConstructionNoAccessMessageSetting) {
291                    ((BudgetConstructionNoAccessMessageSetting) roleTypeService).setNoAccessMessage(budgetConstructionForm.getBudgetConstructionDocument(), GlobalVariables.getUserSession().getPerson(), GlobalVariables.getMessageMap());
292                }
293            }
294        }
295    
296        /**
297         * gwp - no call to super, need to work through command we will use randall - This method might be unnecessary, but putting it
298         * here allows URL to be consistent with Document URLs gwp - i think we still want this method, just need to figure out if we
299         * use command initiate or displayDocSearchView or something else. i expect we will get the account/subaccount or docnumber from
300         * the previous form and assume the document will already exist regardless of creation by genesis or
301         * 
302         * @param mapping
303         * @param form
304         * @param request
305         * @param response
306         * @return
307         * @throws IOException
308         * @throws ServletException
309         */
310        @Override
311        public ActionForward docHandler(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
312            BudgetConstructionForm budgetConstructionForm = (BudgetConstructionForm) form;
313    
314            loadDocument(budgetConstructionForm);
315    
316            // set flag to have execute perform 2plug adjusment if the doc goes into edit mode later
317            budgetConstructionForm.setCheckTwoPlugAdjustment(true);
318    
319            this.initAuthorization(budgetConstructionForm);
320    
321            return mapping.findForward(KFSConstants.MAPPING_BASIC);
322        }
323    
324        /**
325         * Initially load the document from the DB Coded this to look like KualiDocumentActionBase.loadDocument()
326         * 
327         * @param budgetConstructionForm
328         * @throws WorkflowException
329         */
330        @Override
331        protected void loadDocument(KualiDocumentFormBase kualiDocumentFormBase) throws WorkflowException {
332            BudgetConstructionForm budgetConstructionForm = (BudgetConstructionForm) kualiDocumentFormBase;
333    
334            BudgetConstructionHeader budgetConstructionHeader;
335            if (budgetConstructionForm.getDocId() != null) {
336                Map<String, Object> primaryKey = new HashMap<String, Object>();
337                primaryKey.put(KFSPropertyConstants.DOCUMENT_NUMBER, budgetConstructionForm.getDocId());
338    
339                budgetConstructionHeader = (BudgetConstructionHeader) SpringContext.getBean(BusinessObjectService.class).findByPrimaryKey(BudgetConstructionHeader.class, primaryKey);
340    
341            }
342            else {
343                // use the passed url autoloaded parms to get the record from DB
344                // BudgetConstructionDaoOjb bcHeaderDao;
345                String chartOfAccountsCode = budgetConstructionForm.getChartOfAccountsCode();
346                String accountNumber = budgetConstructionForm.getAccountNumber();
347                String subAccountNumber = budgetConstructionForm.getSubAccountNumber();
348                Integer universityFiscalYear = budgetConstructionForm.getUniversityFiscalYear();
349    
350                budgetConstructionHeader = (BudgetConstructionHeader) SpringContext.getBean(BudgetDocumentService.class).getByCandidateKey(chartOfAccountsCode, accountNumber, subAccountNumber, universityFiscalYear);
351            }
352    
353            kualiDocumentFormBase.setDocId(budgetConstructionHeader.getDocumentNumber());
354            super.loadDocument(kualiDocumentFormBase);
355    
356            BudgetConstructionDocument budgetConstructionDocument = (BudgetConstructionDocument) kualiDocumentFormBase.getDocument();
357    
358            // init the benefits calc flags
359            budgetConstructionDocument.setBenefitsCalcNeeded(false);
360            budgetConstructionDocument.setMonthlyBenefitsCalcNeeded(false);
361    
362            // init the new line objects
363            budgetConstructionForm.initNewLine(budgetConstructionForm.getNewRevenueLine(), true);
364            budgetConstructionForm.initNewLine(budgetConstructionForm.getNewExpenditureLine(), false);
365    
366            // need this here to do totaling on initial load
367            budgetConstructionForm.populatePBGLLines();
368            budgetConstructionForm.initializePersistedRequestAmounts();
369        }
370    
371        /**
372         * Cleans up state info to handle no access lock errors
373         * 
374         * @param budgetConstructionForm
375         */
376        protected void cleanupForLockError(BudgetConstructionForm budgetConstructionForm) {
377            budgetConstructionForm.setSecurityNoAccess(true);
378            budgetConstructionForm.getDocumentActions().remove(KNSConstants.KUALI_ACTION_CAN_EDIT);
379            budgetConstructionForm.getDocumentActions().remove(KNSConstants.KUALI_ACTION_CAN_SAVE);
380            GlobalVariables.getMessageList().remove(BCKeyConstants.MESSAGE_BUDGET_EDIT_ACCESS);
381        }
382    
383        /**
384         * Override to set authorization Maps from session
385         * 
386         * @see org.kuali.rice.kns.web.struts.action.KualiTransactionalDocumentActionBase#populateAuthorizationFields(org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase)
387         */
388        @Override
389        protected void populateAuthorizationFields(KualiDocumentFormBase formBase) {
390            BudgetConstructionAuthorizationStatus authorizationStatus = (BudgetConstructionAuthorizationStatus) GlobalVariables.getUserSession().retrieveObject(BCConstants.BC_DOC_AUTHORIZATION_STATUS_SESSIONKEY);
391    
392            if (authorizationStatus == null) {
393                // execute handles any session timeout before this is called
394                return;
395            }
396    
397            formBase.setDocumentActions(authorizationStatus.getDocumentActions());
398            formBase.setEditingMode(authorizationStatus.getEditingMode());
399        }
400    
401        /**
402         * Calls authorizer to determine if the user has edit permission and checks if budget construction is active is fiscal year
403         * function table. Then updates the <code>BudgetConstructionEditStatus</code> in session. Finally updates authorization in the
404         * form
405         * 
406         * @param budgetConstructionForm current bc action form that will be updated
407         */
408        protected void initAuthorization(BudgetConstructionForm budgetConstructionForm) {
409            // GlobalVariables.setRequestCache(ROLE_QUALIFICATION_CACHE_NAME, budgetConstructionForm)
410            super.populateAuthorizationFields(budgetConstructionForm);
411    
412            BudgetConstructionAuthorizationStatus editStatus = new BudgetConstructionAuthorizationStatus();
413            editStatus.setDocumentActions(budgetConstructionForm.getDocumentActions());
414            editStatus.setEditingMode(budgetConstructionForm.getEditingMode());
415    
416            GlobalVariables.getUserSession().addObject(BCConstants.BC_DOC_AUTHORIZATION_STATUS_SESSIONKEY, editStatus);
417        }
418    
419        /**
420         * Applies adjustments due to 2plg existence at initial load or detected salary setting changes upon returning from the Quick
421         * Salary Setting screen. The adjustments consist of calculating benefits and adding any expenditure total before/after
422         * difference back into a 2plg row, creating or updating as needed. Then, validation is performed. Sucessful validation removes
423         * the 2plg row if the final, post benefits calc, adjusted amount is zero. This method assumes the set of expenditure rows in
424         * memory currently matches the DB.
425         * 
426         * @param bcForm
427         */
428        protected void adjustForSalarySettingChanges(BudgetConstructionForm bcForm) {
429    
430            BudgetDocumentService budgetDocumentService = SpringContext.getBean(BudgetDocumentService.class);
431            BudgetConstructionDocument bcDoc = (BudgetConstructionDocument) bcForm.getDocument();
432            KualiInteger oldRequestAmount = bcDoc.getExpenditureAccountLineAnnualBalanceAmountTotal();
433    
434            // calc benefits also handles setting persisted amounts and populating reloaded benefit rows
435            budgetDocumentService.calculateBenefits(bcDoc);
436            // bcForm.initializePersistedRequestAmounts();
437    
438            // repop and refresh refs - esp monthly so jsp can properly display state
439            // bcForm.populatePBGLLines();
440    
441            // need 2plg adjustment and save, even if it is zero
442            KualiInteger newRquestAmount = bcForm.getBudgetConstructionDocument().getExpenditureAccountLineAnnualBalanceAmountTotal();
443            PendingBudgetConstructionGeneralLedger twoPlugRow = budgetDocumentService.updatePendingBudgetGeneralLedgerPlug(bcDoc, newRquestAmount.subtract(oldRequestAmount));
444        }
445    
446        /**
447         * @see org.kuali.rice.kns.web.struts.action.KualiDocumentActionBase#close(org.apache.struts.action.ActionMapping,
448         *      org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
449         */
450        @Override
451        public ActionForward close(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
452    
453            // return super.close(mapping, form, request, response);
454            BudgetConstructionForm docForm = (BudgetConstructionForm) form;
455    
456            // only want to prompt them to save if they already can save
457            if (docForm.getDocumentActions().keySet().contains(KNSConstants.KUALI_ACTION_CAN_SAVE)) {
458                Object question = request.getParameter(KFSConstants.QUESTION_INST_ATTRIBUTE_NAME);
459                KualiConfigurationService kualiConfiguration = SpringContext.getBean(KualiConfigurationService.class);
460    
461                // logic for close question
462                if (question == null) {
463                    // ask question if not already asked
464                    return this.performQuestionWithoutInput(mapping, form, request, response, KFSConstants.DOCUMENT_SAVE_BEFORE_CLOSE_QUESTION, kualiConfiguration.getPropertyString(KFSKeyConstants.QUESTION_SAVE_BEFORE_CLOSE), KFSConstants.CONFIRMATION_QUESTION, KFSConstants.MAPPING_CLOSE, "");
465                }
466                else {
467                    Object buttonClicked = request.getParameter(KFSConstants.QUESTION_CLICKED_BUTTON);
468                    if ((KFSConstants.DOCUMENT_SAVE_BEFORE_CLOSE_QUESTION.equals(question)) && ConfirmationQuestion.YES.equals(buttonClicked)) {
469                        // if yes button clicked - save the doc
470                        BudgetConstructionDocument bcDoc = (BudgetConstructionDocument) docForm.getDocument();
471    
472                        // force tighter checks when saving in cleanup mode
473                        if (!bcDoc.isBudgetableDocument()) {
474                            bcDoc.setCleanupModeActionForceCheck(Boolean.TRUE);
475                        }
476    
477                        // SpringContext.getBean(DocumentService.class).updateDocument(docForm.getDocument());
478                        BudgetDocumentService budgetDocumentService = SpringContext.getBean(BudgetDocumentService.class);
479                        budgetDocumentService.saveDocument(bcDoc);
480                        docForm.initializePersistedRequestAmounts();
481                        if (bcDoc.isBenefitsCalcNeeded() || bcDoc.isMonthlyBenefitsCalcNeeded()) {
482                            budgetDocumentService.calculateBenefitsIfNeeded(bcDoc);
483    
484                            GlobalVariables.getMessageList().add(KFSKeyConstants.MESSAGE_SAVED);
485                            return mapping.findForward(KFSConstants.MAPPING_BASIC);
486                        }
487                        else {
488                            // else drop to close logic below
489                        }
490    
491                    }
492                    // else drop to close logic below
493                }
494            }
495    
496            // do the unlock if they have full access and not system view mode
497            if (docForm.isEditAllowed() && !docForm.isSystemViewOnly()) {
498                LockService lockService = SpringContext.getBean(LockService.class);
499                HashMap primaryKey = new HashMap();
500                primaryKey.put(KFSPropertyConstants.DOCUMENT_NUMBER, docForm.getDocument().getDocumentNumber());
501    
502                BudgetConstructionHeader budgetConstructionHeader = (BudgetConstructionHeader) SpringContext.getBean(BusinessObjectService.class).findByPrimaryKey(BudgetConstructionHeader.class, primaryKey);
503                if (budgetConstructionHeader != null) {
504                    LockStatus lockStatus = lockService.unlockAccount(budgetConstructionHeader);
505                }
506                else {
507                    // unlikely, but benign problem here
508                }
509            }
510    
511            // flag to cleanup the session edit mode store so we don't side effect organization salary setting
512            // used in the bottom of execute()
513            docForm.setClosingDocument(Boolean.TRUE);
514    
515            if (docForm.isPickListMode()) {
516                // redisplay with a message
517                GlobalVariables.getMessageList().add(BCKeyConstants.MESSAGE_BUDGET_SUCCESSFUL_CLOSE);
518                docForm.setPickListClose(true);
519    
520                // this is a hack to do our own session document cleanup since refreshCaller=QuestionRefresh
521                // prevents proper cleanup in KualiRequestProcessor.processActionPerform()
522                UserSession userSession = (UserSession) request.getSession().getAttribute(KNSConstants.USER_SESSION_KEY);
523                String docFormKey = docForm.getFormKey();
524                SpringContext.getBean(SessionDocumentService.class).purgeDocumentForm(docForm.getDocument().getDocumentNumber(), docFormKey, userSession, request.getRemoteAddr());
525    
526                return mapping.findForward(KFSConstants.MAPPING_BASIC);
527    
528            }
529            else {
530                if (docForm.getReturnFormKey() == null) {
531    
532                    // assume called from doc search or lost the session - go back to main
533                    return returnToSender(request, mapping, docForm);
534                }
535                else {
536                    // setup the return parms for the document and anchor and go back to doc select
537                    Properties parameters = new Properties();
538                    parameters.put(KFSConstants.DISPATCH_REQUEST_PARAMETER, BCConstants.BC_SELECTION_REFRESH_METHOD);
539                    parameters.put(KFSConstants.DOC_FORM_KEY, docForm.getReturnFormKey());
540                    parameters.put(KFSConstants.ANCHOR, docForm.getReturnAnchor());
541                    parameters.put(KFSConstants.REFRESH_CALLER, BCConstants.BC_DOCUMENT_REFRESH_CALLER);
542    
543                    String lookupUrl = UrlFactory.parameterizeUrl("/" + BCConstants.BC_SELECTION_ACTION, parameters);
544    
545                    // this is a hack to do our own session document cleanup since refreshCaller=QuestionRefresh
546                    // prevents proper cleanup in KualiRequestProcessor.processActionPerform()
547                    UserSession userSession = (UserSession) request.getSession().getAttribute(KNSConstants.USER_SESSION_KEY);
548                    String docFormKey = docForm.getFormKey();
549                    SpringContext.getBean(SessionDocumentService.class).purgeDocumentForm(docForm.getDocument().getDocumentNumber(), docFormKey, userSession, request.getRemoteAddr());
550    
551                    return new ActionForward(lookupUrl, true);
552                }
553            }
554        }
555    
556        /**
557         * @see org.kuali.rice.kns.web.struts.action.KualiDocumentActionBase#save(org.apache.struts.action.ActionMapping,
558         *      org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
559         */
560        @Override
561        public ActionForward save(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
562    
563            BudgetConstructionForm budgetConstructionForm = (BudgetConstructionForm) form;
564            BudgetConstructionDocument bcDocument = (BudgetConstructionDocument) budgetConstructionForm.getDocument();
565            // DocumentService documentService = SpringContext.getBean(DocumentService.class);
566            BudgetDocumentService budgetDocumentService = SpringContext.getBean(BudgetDocumentService.class);
567    
568            // force tighter checks when saving in cleanup mode
569            if (!bcDocument.isBudgetableDocument()) {
570                bcDocument.setCleanupModeActionForceCheck(Boolean.TRUE);
571            }
572    
573            budgetDocumentService.saveDocument(bcDocument);
574            budgetConstructionForm.initializePersistedRequestAmounts();
575            budgetDocumentService.calculateBenefitsIfNeeded(bcDocument);
576            GlobalVariables.getMessageList().add(KFSKeyConstants.MESSAGE_SAVED);
577    
578            budgetConstructionForm.setAnnotation("");
579    
580            // redisplay the document along with document saved message
581            return mapping.findForward(KFSConstants.MAPPING_BASIC);
582        }
583    
584        /**
585         * Calls the single line benefits impact screen by setting up the required parameters and feeding them to the temporary list
586         * lookup action for the expenditure line selected. This is called from the ShowBenefits button on the BC document screen when
587         * an expenditure line is associated with benefits and benefits calculation is enabled.
588         * 
589         * @param mapping
590         * @param form
591         * @param request
592         * @param response
593         * @return
594         * @throws Exception
595         */
596        public ActionForward performShowBenefits(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
597    
598            BudgetConstructionForm tForm = (BudgetConstructionForm) form;
599            BudgetConstructionDocument tDoc = tForm.getBudgetConstructionDocument();
600            int selectIndex = this.getSelectedLine(request);
601            PendingBudgetConstructionGeneralLedger expLine = tDoc.getPendingBudgetConstructionGeneralLedgerExpenditureLines().get(selectIndex);
602    
603            // when we return from the lookup, our next request's method to call is going to be refresh
604            tForm.registerEditableProperty(KNSConstants.DISPATCH_REQUEST_PARAMETER);
605    
606            Properties parameters = new Properties();
607            parameters.put(KFSConstants.DISPATCH_REQUEST_PARAMETER, KFSConstants.START_METHOD);
608    
609            String basePath = SpringContext.getBean(KualiConfigurationService.class).getPropertyString(KFSConstants.APPLICATION_URL_KEY);
610            parameters.put(KFSConstants.BACK_LOCATION, basePath + mapping.getPath() + ".do");
611    
612            if (StringUtils.isNotEmpty(((KualiForm) form).getAnchor())) {
613                parameters.put(BCConstants.RETURN_ANCHOR, ((KualiForm) form).getAnchor());
614            }
615    
616            // this hack sets the return anchor we want to return too after the inquiry
617            // do this here so it gets into the session stored form version
618            // refresh checks for this after and resets the anchor
619            if (form instanceof KualiForm && StringUtils.isNotEmpty(((KualiForm) form).getAnchor())) {
620                tForm.setBalanceInquiryReturnAnchor(((KualiForm) form).getAnchor());
621            }
622    
623            parameters.put(KNSConstants.DOC_FORM_KEY, GlobalVariables.getUserSession().addObject(form, BCConstants.FORMKEY_PREFIX));
624            parameters.put(KFSConstants.BUSINESS_OBJECT_CLASS_ATTRIBUTE, BCConstants.REQUEST_BENEFITS_BO);
625            parameters.put(KFSConstants.HIDE_LOOKUP_RETURN_LINK, "true");
626            parameters.put(KFSConstants.SUPPRESS_ACTIONS, "true");
627            parameters.put(BCConstants.SHOW_INITIAL_RESULTS, "true");
628            parameters.put(BCConstants.TempListLookupMode.TEMP_LIST_LOOKUP_MODE, Integer.toString(BCConstants.TempListLookupMode.SHOW_BENEFITS));
629    
630            parameters.put(KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR, expLine.getUniversityFiscalYear().toString());
631            parameters.put(KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE, expLine.getChartOfAccountsCode());
632            parameters.put(KFSConstants.FINANCIAL_OBJECT_CODE_PROPERTY_NAME, expLine.getFinancialObjectCode());
633            parameters.put(KFSPropertyConstants.ACCOUNT_LINE_ANNUAL_BALANCE_AMOUNT, expLine.getAccountLineAnnualBalanceAmount().toString());
634            parameters.put(KNSConstants.LOOKUP_READ_ONLY_FIELDS, KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR + "," + KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE + "," + KFSConstants.FINANCIAL_OBJECT_CODE_PROPERTY_NAME + "," + KFSPropertyConstants.ACCOUNT_LINE_ANNUAL_BALANCE_AMOUNT);
635    
636            String url = UrlFactory.parameterizeUrl(basePath + "/" + BCConstants.ORG_TEMP_LIST_LOOKUP, parameters);
637            this.setupDocumentExit();
638            return new ActionForward(url, true);
639        }
640    
641        public ActionForward performBalanceInquiryForRevenueLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
642            return performBalanceInquiry(true, mapping, form, request, response);
643        }
644    
645        public ActionForward performBalanceInquiryForExpenditureLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
646            return performBalanceInquiry(false, mapping, form, request, response);
647        }
648    
649        /**
650         * This method is similar to org.kuali.kfs.sys.web.struts.KualiAccountingDocumentActionBase.performBalanceInquiry()
651         * 
652         * @param isRevenue
653         * @param mapping
654         * @param form
655         * @param request
656         * @param response
657         * @return
658         * @throws Exception
659         */
660        public ActionForward performBalanceInquiry(boolean isRevenue, ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
661            final String docNumber;
662    
663            // get the selected line, setup parms and redirect to balance inquiry
664            BudgetConstructionForm budgetConstructionForm = (BudgetConstructionForm) form;
665            BudgetConstructionDocument bcDocument = (BudgetConstructionDocument) budgetConstructionForm.getDocument();
666    
667            // when we return from the lookup, our next request's method to call is going to be refresh
668            budgetConstructionForm.registerEditableProperty(KNSConstants.DISPATCH_REQUEST_PARAMETER);
669    
670            PendingBudgetConstructionGeneralLedger pbglLine;
671            if (isRevenue) {
672                pbglLine = bcDocument.getPendingBudgetConstructionGeneralLedgerRevenueLines().get(this.getSelectedLine(request));
673            }
674            else {
675                pbglLine = bcDocument.getPendingBudgetConstructionGeneralLedgerExpenditureLines().get(this.getSelectedLine(request));
676            }
677    
678            // build out base path for return location, use config service
679            String basePath = SpringContext.getBean(KualiConfigurationService.class).getPropertyString(KFSConstants.APPLICATION_URL_KEY);
680    
681            // this hack sets the return anchor we want to return too after the inquiry
682            // do this here so it gets into the session stored form version
683            // refresh checks for this after and resets the anchor
684            if (form instanceof KualiForm && StringUtils.isNotEmpty(((KualiForm) form).getAnchor())) {
685                budgetConstructionForm.setBalanceInquiryReturnAnchor(((KualiForm) form).getAnchor());
686            }
687    
688            // build out the actual form key that will be used to retrieve the form on refresh
689            String callerDocFormKey = GlobalVariables.getUserSession().addObject(form, BCConstants.FORMKEY_PREFIX);
690    
691            // now add required parameters
692            Properties parameters = new Properties();
693            parameters.put(KFSConstants.DISPATCH_REQUEST_PARAMETER, KFSConstants.START_METHOD);
694            // need this next param b/c the lookup's return back will overwrite
695            // the original doc form key
696            parameters.put(KFSConstants.BALANCE_INQUIRY_REPORT_MENU_CALLER_DOC_FORM_KEY, callerDocFormKey);
697            parameters.put(KFSConstants.DOC_FORM_KEY, callerDocFormKey);
698            parameters.put(KFSConstants.BACK_LOCATION, basePath + mapping.getPath() + ".do");
699    
700            // anchor, if it exists
701            // this doesn't seem to work with the balance inquiry infrastructure, so added hack above
702            if (form instanceof KualiForm && StringUtils.isNotEmpty(((KualiForm) form).getAnchor())) {
703                parameters.put(BCConstants.RETURN_ANCHOR, ((KualiForm) form).getAnchor());
704            }
705    
706            if (StringUtils.isNotBlank(pbglLine.getChartOfAccountsCode())) {
707                parameters.put("chartOfAccountsCode", pbglLine.getChartOfAccountsCode());
708            }
709            if (StringUtils.isNotBlank(pbglLine.getAccountNumber())) {
710                parameters.put("accountNumber", pbglLine.getAccountNumber());
711            }
712            if (StringUtils.isNotBlank(pbglLine.getFinancialObjectCode())) {
713                parameters.put("financialObjectCode", pbglLine.getFinancialObjectCode());
714            }
715            if (StringUtils.isNotBlank(pbglLine.getSubAccountNumber())) {
716                parameters.put("subAccountNumber", pbglLine.getSubAccountNumber());
717            }
718            if (StringUtils.isNotBlank(pbglLine.getFinancialSubObjectCode())) {
719                parameters.put("financialSubObjectCode", pbglLine.getFinancialSubObjectCode());
720            }
721    
722            String lookupUrl = UrlFactory.parameterizeUrl(basePath + "/" + KFSConstants.BALANCE_INQUIRY_REPORT_MENU_ACTION, parameters);
723    
724            this.setupDocumentExit();
725            return new ActionForward(lookupUrl, true);
726        }
727    
728        /**
729         * Calls performMonthlyBudget for the selected revenue line
730         * 
731         * @param mapping
732         * @param form
733         * @param request
734         * @param response
735         * @return
736         * @throws Exception
737         */
738        public ActionForward performMonthlyRevenueBudget(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
739            return performMonthlyBudget(true, mapping, form, request, response);
740        }
741    
742        /**
743         * Calls performMonthlyBudget for the selected expenditure line
744         * 
745         * @param mapping
746         * @param form
747         * @param request
748         * @param response
749         * @return
750         * @throws Exception
751         */
752        public ActionForward performMonthlyExpenditureBudget(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
753            return performMonthlyBudget(false, mapping, form, request, response);
754        }
755    
756        /**
757         * Forwards the user to the monthly budget screen. Doing this in edit mode causes the document to be validated, saved and
758         * benefits calculated (if needed).
759         * 
760         * @param isRevenue
761         * @param mapping
762         * @param form
763         * @param request
764         * @param response
765         * @return
766         * @throws Exception
767         */
768        public ActionForward performMonthlyBudget(boolean isRevenue, ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
769            final String docNumber;
770    
771            // this to checks for 2PLG and turns off SS/monthly RI check if found
772            // the final save after removing 2PLG will catch any monthly discrepencies
773            // also need to save object,subobject key we are operating on so refresh can get the latest row version from DB
774    
775            // validate, save, etc first then goto the monthly screen or redisplay if errors
776            BudgetConstructionForm budgetConstructionForm = (BudgetConstructionForm) form;
777            BudgetConstructionDocument bcDocument = (BudgetConstructionDocument) budgetConstructionForm.getDocument();
778    
779            PendingBudgetConstructionGeneralLedger pbglLine;
780            if (isRevenue) {
781                pbglLine = bcDocument.getPendingBudgetConstructionGeneralLedgerRevenueLines().get(this.getSelectedLine(request));
782            }
783            else {
784                pbglLine = bcDocument.getPendingBudgetConstructionGeneralLedgerExpenditureLines().get(this.getSelectedLine(request));
785            }
786    
787            // when we return from the lookup, our next request's method to call is going to be refresh
788            budgetConstructionForm.registerEditableProperty(KNSConstants.DISPATCH_REQUEST_PARAMETER);
789    
790            if (budgetConstructionForm.isEditAllowed() && !budgetConstructionForm.isSystemViewOnly()) {
791                BudgetDocumentService budgetDocumentService = SpringContext.getBean(BudgetDocumentService.class);
792    
793                // if the doc contains a 2plg line, turn off RI checking to allow cleanup.
794                // The act of attempting to remove a 2plg (delete) forces a complete RI check.
795                // The document is assumed inconsistent as long as a 2plg exists.
796                if (!isRevenue && bcDocument.isContainsTwoPlug()) {
797                    if (pbglLine.getLaborObject() != null && pbglLine.getLaborObject().isDetailPositionRequiredIndicator()) {
798                        budgetDocumentService.saveDocumentNoWorkFlow(bcDocument, MonthSpreadDeleteType.EXPENDITURE, false);
799                    }
800                    else {
801                        budgetDocumentService.saveDocumentNoWorkFlow(bcDocument, MonthSpreadDeleteType.EXPENDITURE, true);
802                    }
803                }
804                else {
805                    budgetDocumentService.saveDocumentNoWorkflow(bcDocument);
806    
807                }
808                budgetConstructionForm.initializePersistedRequestAmounts();
809                budgetDocumentService.calculateBenefitsIfNeeded(bcDocument);
810    
811                // repop and refresh refs - esp monthly so jsp can properly display state
812                // budgetConstructionForm.populatePBGLLines();
813    
814            }
815    
816            // String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() +
817            // request.getContextPath();
818            String basePath = SpringContext.getBean(KualiConfigurationService.class).getPropertyString(KFSConstants.APPLICATION_URL_KEY);
819            Properties parameters = new Properties();
820            parameters.put(KFSConstants.DISPATCH_REQUEST_PARAMETER, BCConstants.MONTHLY_BUDGET_METHOD);
821            parameters.put(KFSConstants.BACK_LOCATION, basePath + mapping.getPath() + ".do");
822            parameters.put("documentNumber", pbglLine.getDocumentNumber());
823            parameters.put("universityFiscalYear", pbglLine.getUniversityFiscalYear().toString());
824            parameters.put("chartOfAccountsCode", pbglLine.getChartOfAccountsCode());
825            parameters.put("accountNumber", pbglLine.getAccountNumber());
826            parameters.put("subAccountNumber", pbglLine.getSubAccountNumber());
827            parameters.put("financialObjectCode", pbglLine.getFinancialObjectCode());
828            parameters.put("financialSubObjectCode", pbglLine.getFinancialSubObjectCode());
829            parameters.put("financialBalanceTypeCode", pbglLine.getFinancialBalanceTypeCode());
830            parameters.put("financialObjectTypeCode", pbglLine.getFinancialObjectTypeCode());
831            parameters.put(BCPropertyConstants.MAIN_WINDOW, (budgetConstructionForm.isMainWindow() ? "true" : "false"));
832    
833            // anchor, if it exists
834            if (form instanceof KualiForm && StringUtils.isNotEmpty(((KualiForm) form).getAnchor())) {
835                parameters.put(BCConstants.RETURN_ANCHOR, ((KualiForm) form).getAnchor());
836            }
837    
838            // the form object is retrieved and removed upon return by KualiRequestProcessor.processActionForm()
839            parameters.put(BCConstants.RETURN_FORM_KEY, GlobalVariables.getUserSession().addObject(form, BCConstants.FORMKEY_PREFIX));
840    
841            String lookupUrl = UrlFactory.parameterizeUrl(basePath + "/" + BCConstants.MONTHLY_BUDGET_ACTION, parameters);
842            this.setupDocumentExit();
843            return new ActionForward(lookupUrl, true);
844        }
845    
846        /**
847         * Forwards the user to the quick salary setting screen. Doing this in edit mode causes the document to be validated, saved and
848         * benefits calculated (if needed).
849         * 
850         * @param mapping
851         * @param form
852         * @param request
853         * @param response
854         * @return
855         * @throws Exception
856         */
857        public ActionForward performSalarySetting(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
858            final String docNumber;
859    
860            // this to checks for 2PLG and turns off monthly RI check if found
861            // the final save after removing 2PLG will catch any monthly discrepencies
862    
863            // validate, save, etc first then goto the SalarySetting screen or redisplay if errors
864            BudgetConstructionForm budgetConstructionForm = (BudgetConstructionForm) form;
865            BudgetConstructionDocument bcDocument = (BudgetConstructionDocument) budgetConstructionForm.getDocument();
866    
867            // when we return from the lookup, our next request's method to call is going to be refresh
868            budgetConstructionForm.registerEditableProperty(KNSConstants.DISPATCH_REQUEST_PARAMETER);
869    
870            if (budgetConstructionForm.isEditAllowed() && !budgetConstructionForm.isSystemViewOnly()) {
871                BudgetDocumentService budgetDocumentService = SpringContext.getBean(BudgetDocumentService.class);
872    
873                // if the doc contains a 2plg line, turn off RI checking to allow cleanup.
874                // The act of attempting to remove a 2plg (delete) forces a complete RI check.
875                // The document is assumed inconsistent as long as a 2plg exists.
876                if (bcDocument.isContainsTwoPlug()) {
877                    budgetDocumentService.saveDocumentNoWorkFlow(bcDocument, MonthSpreadDeleteType.EXPENDITURE, false);
878                }
879                else {
880                    budgetDocumentService.saveDocumentNoWorkflow(bcDocument);
881                }
882    
883                // init persisted property and get the current list of salary setting related rows
884                budgetConstructionForm.initializePersistedRequestAmounts(true);
885    
886                budgetDocumentService.calculateBenefitsIfNeeded(bcDocument);
887    
888                // repop and refresh refs - esp monthly so jsp can properly display state
889                // budgetConstructionForm.populatePBGLLines();
890    
891            }
892            PendingBudgetConstructionGeneralLedger pbglLine;
893            pbglLine = bcDocument.getPendingBudgetConstructionGeneralLedgerExpenditureLines().get(this.getSelectedLine(request));
894    
895            // String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() +
896            // request.getContextPath();
897            String basePath = SpringContext.getBean(KualiConfigurationService.class).getPropertyString(KFSConstants.APPLICATION_URL_KEY);
898            Properties parameters = new Properties();
899            parameters.put(KFSConstants.DISPATCH_REQUEST_PARAMETER, BCConstants.QUICK_SALARY_SETTING_METHOD);
900            parameters.put(KFSConstants.BACK_LOCATION, basePath + mapping.getPath() + ".do");
901    
902            parameters.put("documentNumber", pbglLine.getDocumentNumber());
903            parameters.put("universityFiscalYear", pbglLine.getUniversityFiscalYear().toString());
904            parameters.put("chartOfAccountsCode", pbglLine.getChartOfAccountsCode());
905            parameters.put("accountNumber", pbglLine.getAccountNumber());
906            parameters.put("subAccountNumber", pbglLine.getSubAccountNumber());
907            parameters.put("financialObjectCode", pbglLine.getFinancialObjectCode());
908            parameters.put("financialSubObjectCode", pbglLine.getFinancialSubObjectCode());
909            parameters.put("financialBalanceTypeCode", pbglLine.getFinancialBalanceTypeCode());
910            parameters.put("financialObjectTypeCode", pbglLine.getFinancialObjectTypeCode());
911            parameters.put(BCPropertyConstants.MAIN_WINDOW, (budgetConstructionForm.isMainWindow() ? "true" : "false"));
912    
913            // anchor, if it exists
914            if (form instanceof KualiForm && StringUtils.isNotEmpty(((KualiForm) form).getAnchor())) {
915                parameters.put(BCConstants.RETURN_ANCHOR, ((KualiForm) form).getAnchor());
916            }
917    
918            // the form object is retrieved and removed upon return by KualiRequestProcessor.processActionForm()
919            parameters.put(BCConstants.RETURN_FORM_KEY, GlobalVariables.getUserSession().addObject(form, BCConstants.FORMKEY_PREFIX));
920    
921            String lookupUrl = UrlFactory.parameterizeUrl(basePath + "/" + BCConstants.QUICK_SALARY_SETTING_ACTION, parameters);
922            this.setupDocumentExit();
923            return new ActionForward(lookupUrl, true);
924        }
925    
926        /**
927         * This adds a revenue line to the BC document
928         * 
929         * @param mapping
930         * @param form
931         * @param request
932         * @param response
933         * @return
934         * @throws Exception
935         */
936        public ActionForward insertRevenueLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
937            BudgetConstructionForm budgetConstructionForm = (BudgetConstructionForm) form;
938    
939            PendingBudgetConstructionGeneralLedger line = budgetConstructionForm.getNewRevenueLine();
940    
941            boolean rulePassed = true;
942    
943            rulePassed &= SpringContext.getBean(KualiRuleService.class).applyRules(new AddPendingBudgetGeneralLedgerLineEvent(BCConstants.NEW_REVENUE_LINE_PROPERTY_NAME, budgetConstructionForm.getDocument(), line, true));
944    
945            if (rulePassed) {
946                // add PBGLLine
947                insertPBGLLine(true, budgetConstructionForm, line);
948    
949                // clear the used newRevenueLine
950                budgetConstructionForm.setNewRevenueLine(new PendingBudgetConstructionGeneralLedger());
951                budgetConstructionForm.initNewLine(budgetConstructionForm.getNewRevenueLine(), true);
952            }
953    
954            return mapping.findForward(KFSConstants.MAPPING_BASIC);
955        }
956    
957        /**
958         * This adds an expenditure line to the BC document
959         * 
960         * @param mapping
961         * @param form
962         * @param request
963         * @param response
964         * @return
965         * @throws Exception
966         */
967        public ActionForward insertExpenditureLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
968            BudgetConstructionForm budgetConstructionForm = (BudgetConstructionForm) form;
969    
970            PendingBudgetConstructionGeneralLedger line = budgetConstructionForm.getNewExpenditureLine();
971    
972            boolean rulePassed = true;
973    
974            rulePassed &= SpringContext.getBean(KualiRuleService.class).applyRules(new AddPendingBudgetGeneralLedgerLineEvent(BCConstants.NEW_EXPENDITURE_LINE_PROPERTY_NAME, budgetConstructionForm.getDocument(), line, false));
975    
976            if (rulePassed) {
977    
978                // add PBGLLine
979                insertPBGLLine(false, budgetConstructionForm, line);
980    
981                // clear the used newExpenditureLine
982                budgetConstructionForm.setNewExpenditureLine(new PendingBudgetConstructionGeneralLedger());
983                budgetConstructionForm.initNewLine(budgetConstructionForm.getNewExpenditureLine(), false);
984            }
985    
986            return mapping.findForward(KFSConstants.MAPPING_BASIC);
987        }
988    
989        /**
990         * This inserts a PBGL revenue or expenditure line
991         * 
992         * @param isRevenue
993         * @param budgetConstructionForm
994         * @param line
995         */
996        protected void insertPBGLLine(boolean isRevenue, BudgetConstructionForm budgetConstructionForm, PendingBudgetConstructionGeneralLedger line) {
997    
998            BudgetConstructionDocument bcDoc = (BudgetConstructionDocument) budgetConstructionForm.getDocument();
999    
1000            // null subobj must be set to dashes
1001            if (StringUtils.isBlank(line.getFinancialSubObjectCode())) {
1002                line.setFinancialSubObjectCode(KFSConstants.getDashFinancialSubObjectCode());
1003            }
1004            
1005            // check the DB for an existing persisted version of the line
1006            // and reinstate that with a message to the user indicating such
1007            boolean isReinstated = false;
1008            Map<String, Object> primaryKey = new HashMap<String, Object>();
1009            primaryKey.put(KFSPropertyConstants.DOCUMENT_NUMBER, line.getDocumentNumber());
1010            primaryKey.put(KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR, line.getUniversityFiscalYear());
1011            primaryKey.put(KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE, line.getChartOfAccountsCode());
1012            primaryKey.put(KFSPropertyConstants.ACCOUNT_NUMBER, line.getAccountNumber());
1013            primaryKey.put(KFSPropertyConstants.SUB_ACCOUNT_NUMBER, line.getSubAccountNumber());
1014            primaryKey.put(KFSPropertyConstants.FINANCIAL_BALANCE_TYPE_CODE, line.getFinancialBalanceTypeCode());
1015            primaryKey.put(KFSPropertyConstants.FINANCIAL_OBJECT_TYPE_CODE, line.getFinancialObjectTypeCode());
1016    
1017            primaryKey.put(KFSPropertyConstants.FINANCIAL_OBJECT_CODE, line.getFinancialObjectCode());
1018            primaryKey.put(KFSPropertyConstants.FINANCIAL_SUB_OBJECT_CODE, line.getFinancialSubObjectCode());
1019    
1020            PendingBudgetConstructionGeneralLedger dbLine = (PendingBudgetConstructionGeneralLedger) SpringContext.getBean(BusinessObjectService.class).findByPrimaryKey(PendingBudgetConstructionGeneralLedger.class, primaryKey);
1021            if (dbLine != null){
1022                line = dbLine;
1023                SpringContext.getBean(BudgetDocumentService.class).populatePBGLLine(line);
1024                isReinstated = true;
1025            }
1026    
1027            // add the line in the proper order - assumes already exists check is done in rules
1028            int insertPoint = bcDoc.addPBGLLine(line, isRevenue);
1029            
1030            if (isReinstated){
1031                String errorKey; 
1032                if (isRevenue){
1033                    errorKey = KNSConstants.DOCUMENT_PROPERTY_NAME + "." + BCPropertyConstants.PENDING_BUDGET_CONSTRUCTION_GENERAL_LEDGER_REVENUE_LINES + "[" + insertPoint  + "]." + KFSPropertyConstants.ACCOUNT_LINE_ANNUAL_BALANCE_AMOUNT;
1034                }
1035                else {
1036                    errorKey = KNSConstants.DOCUMENT_PROPERTY_NAME + "." + BCPropertyConstants.PENDING_BUDGET_CONSTRUCTION_GENERAL_LEDGER_EXPENDITURE_LINES + "[" + insertPoint  + "]." + KFSPropertyConstants.ACCOUNT_LINE_ANNUAL_BALANCE_AMOUNT;
1037                }
1038                GlobalVariables.getMessageMap().putError(errorKey, BCKeyConstants.ERROR_BUDGET_LINE_REINSTATED, dbLine.getFinancialObjectCode() + "," + dbLine.getFinancialSubObjectCode());
1039            }
1040    
1041            // adjust totals
1042            if (line.getAccountLineAnnualBalanceAmount() != null && line.getAccountLineAnnualBalanceAmount() != KualiInteger.ZERO) {
1043                if (isRevenue) {
1044                    bcDoc.setRevenueAccountLineAnnualBalanceAmountTotal(bcDoc.getRevenueAccountLineAnnualBalanceAmountTotal().add(line.getAccountLineAnnualBalanceAmount()));
1045                }
1046                else {
1047                    bcDoc.setExpenditureAccountLineAnnualBalanceAmountTotal(bcDoc.getExpenditureAccountLineAnnualBalanceAmountTotal().add(line.getAccountLineAnnualBalanceAmount()));
1048                }
1049            }
1050            if (line.getFinancialBeginningBalanceLineAmount() != null && line.getFinancialBeginningBalanceLineAmount() != KualiInteger.ZERO) {
1051                if (isRevenue) {
1052                    bcDoc.setRevenueFinancialBeginningBalanceLineAmountTotal(bcDoc.getRevenueFinancialBeginningBalanceLineAmountTotal().add(line.getFinancialBeginningBalanceLineAmount()));
1053                }
1054                else {
1055                    bcDoc.setExpenditureFinancialBeginningBalanceLineAmountTotal(bcDoc.getExpenditureFinancialBeginningBalanceLineAmountTotal().add(line.getFinancialBeginningBalanceLineAmount()));
1056                }
1057            }
1058        }
1059    
1060        /**
1061         * Deletes an existing PendingBudgetConstructionGeneralLedger revenue line if rules passed. Any associated monthly budget
1062         * (BudgetConstructionMonthly) is also deleted.
1063         * 
1064         * @param mapping
1065         * @param form
1066         * @param request
1067         * @param response
1068         * @return
1069         * @throws Exception
1070         */
1071        public ActionForward deleteRevenueLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
1072    
1073            BudgetConstructionForm tForm = (BudgetConstructionForm) form;
1074            BudgetConstructionDocument tDoc = tForm.getBudgetConstructionDocument();
1075    
1076            boolean rulePassed = true;
1077            int deleteIndex = this.getLineToDelete(request);
1078    
1079            // check business rule if there is a persisted request amount, otherwise the line can just be removed
1080            PendingBudgetConstructionGeneralLedger revLine = tDoc.getPendingBudgetConstructionGeneralLedgerRevenueLines().get(deleteIndex);
1081            if (revLine.getPersistedAccountLineAnnualBalanceAmount() == null) {
1082                rulePassed = true;
1083            }
1084            else {
1085                // check deletion rules and delete if passed
1086                String errorPath = KFSConstants.DOCUMENT_PROPERTY_NAME + "." + BCPropertyConstants.PENDING_BUDGET_CONSTRUCTION_GENERAL_LEDGER_REVENUE_LINES + "[" + deleteIndex + "]";
1087                rulePassed &= SpringContext.getBean(KualiRuleService.class).applyRules(new DeletePendingBudgetGeneralLedgerLineEvent(errorPath, tDoc, revLine, true));
1088            }
1089    
1090            if (rulePassed) {
1091                deletePBGLLine(true, tForm, deleteIndex, revLine);
1092            }
1093    
1094            return mapping.findForward(KFSConstants.MAPPING_BASIC);
1095        }
1096    
1097        /**
1098         * Deletes an existing PendingBudgetConstructionGeneralLedger expenditure line if rules passed Any associated monthly budget
1099         * (BudgetConstructionMonthly) is also deleted. Check for the special case where the line is a 2PLG line, in which case the
1100         * document is validated and RI checks are forced even if there are no current differences between persisted and request
1101         * amounts.
1102         * 
1103         * @param mapping
1104         * @param form
1105         * @param request
1106         * @param response
1107         * @return
1108         * @throws Exception
1109         */
1110        public ActionForward deleteExpenditureLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
1111    
1112            BudgetConstructionForm tForm = (BudgetConstructionForm) form;
1113            BudgetConstructionDocument tDoc = tForm.getBudgetConstructionDocument();
1114    
1115            boolean rulePassed = true;
1116            int deleteIndex = this.getLineToDelete(request);
1117    
1118            // check business rule if there is a persisted request amount, otherwise the line can just be removed
1119            PendingBudgetConstructionGeneralLedger expLine = tDoc.getPendingBudgetConstructionGeneralLedgerExpenditureLines().get(deleteIndex);
1120            if (expLine.getPersistedAccountLineAnnualBalanceAmount() == null) {
1121                rulePassed = true;
1122            }
1123            else {
1124                // check regular deletion rules and delete if passed
1125                String errorPath = KFSConstants.DOCUMENT_PROPERTY_NAME + "." + BCPropertyConstants.PENDING_BUDGET_CONSTRUCTION_GENERAL_LEDGER_EXPENDITURE_LINES + "[" + deleteIndex + "]";
1126                rulePassed &= SpringContext.getBean(KualiRuleService.class).applyRules(new DeletePendingBudgetGeneralLedgerLineEvent(errorPath, tDoc, expLine, false));
1127            }
1128    
1129            if (rulePassed) {
1130    
1131                // if the line is a 2PLG line do document validation, which forces RI checks in no current change situation
1132                if (expLine.getFinancialObjectCode().equalsIgnoreCase(KFSConstants.BudgetConstructionConstants.OBJECT_CODE_2PLG)) {
1133                    SpringContext.getBean(BudgetDocumentService.class).validateDocument(tDoc);
1134                }
1135                // gets here if line is not 2PLG or doc is valid from 2plg perspective
1136                deletePBGLLine(false, tForm, deleteIndex, expLine);
1137            }
1138    
1139            return mapping.findForward(KFSConstants.MAPPING_BASIC);
1140        }
1141    
1142        /**
1143         * Deletes an existing PendingBudgetConstructionGeneralLedger revenue or expenditure line along with any associated monthly
1144         * budget (BudgetConstructionMonthly)
1145         * 
1146         * @param isRevenue
1147         * @param budgetConstructionForm
1148         * @param deleteIndex
1149         */
1150        protected void deletePBGLLine(boolean isRevenue, BudgetConstructionForm budgetConstructionForm, int deleteIndex, PendingBudgetConstructionGeneralLedger line) {
1151    
1152            BudgetConstructionDocument bcDoc = budgetConstructionForm.getBudgetConstructionDocument();
1153    
1154            // adjust totals
1155            if (line.getAccountLineAnnualBalanceAmount() != null && line.getAccountLineAnnualBalanceAmount() != KualiInteger.ZERO) {
1156                if (isRevenue) {
1157                    bcDoc.setRevenueAccountLineAnnualBalanceAmountTotal(bcDoc.getRevenueAccountLineAnnualBalanceAmountTotal().subtract(line.getAccountLineAnnualBalanceAmount()));
1158                }
1159                else {
1160                    bcDoc.setExpenditureAccountLineAnnualBalanceAmountTotal(bcDoc.getExpenditureAccountLineAnnualBalanceAmountTotal().subtract(line.getAccountLineAnnualBalanceAmount()));
1161                }
1162            }
1163            if (line.getFinancialBeginningBalanceLineAmount() != null && line.getFinancialBeginningBalanceLineAmount() != KualiInteger.ZERO) {
1164                if (isRevenue) {
1165                    bcDoc.setRevenueFinancialBeginningBalanceLineAmountTotal(bcDoc.getRevenueFinancialBeginningBalanceLineAmountTotal().subtract(line.getFinancialBeginningBalanceLineAmount()));
1166                }
1167                else {
1168                    bcDoc.setExpenditureFinancialBeginningBalanceLineAmountTotal(bcDoc.getExpenditureFinancialBeginningBalanceLineAmountTotal().subtract(line.getFinancialBeginningBalanceLineAmount()));
1169                }
1170            }
1171    
1172            // remove the line
1173            if (isRevenue) {
1174                bcDoc.getPendingBudgetConstructionGeneralLedgerRevenueLines().remove(deleteIndex);
1175            }
1176            else {
1177                if (line.getFinancialObjectCode().equalsIgnoreCase(KFSConstants.BudgetConstructionConstants.OBJECT_CODE_2PLG)) {
1178                    bcDoc.setContainsTwoPlug(true);
1179                }
1180                bcDoc.getPendingBudgetConstructionGeneralLedgerExpenditureLines().remove(deleteIndex);
1181            }
1182    
1183        }
1184    
1185        /*
1186         * public ActionForward returnFromMonthly(ActionMapping mapping, ActionForm form, HttpServletRequest request,
1187         * HttpServletResponse response) throws Exception { BudgetConstructionForm budgetConstructionForm = (BudgetConstructionForm)
1188         * form; String documentNumber = request.getParameter("documentNumber"); BudgetConstructionDocument budgetConstructionDocument =
1189         * (BudgetConstructionDocument) SpringContext.getBean(DocumentService.class).getByDocumentHeaderId(documentNumber);
1190         * budgetConstructionForm.setDocument(budgetConstructionDocument); KualiWorkflowDocument workflowDoc =
1191         * budgetConstructionDocument.getDocumentHeader().getWorkflowDocument();
1192         * budgetConstructionForm.setDocTypeName(workflowDoc.getDocumentType()); // KualiDocumentFormBase.populate() needs this updated
1193         * in the session GlobalVariables.getUserSession().setWorkflowDocument(workflowDoc); return
1194         * mapping.findForward(KFSConstants.MAPPING_BASIC); }
1195         */
1196    
1197        /**
1198         * @see org.kuali.rice.kns.web.struts.action.KualiDocumentActionBase#refresh(org.apache.struts.action.ActionMapping,
1199         *      org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
1200         */
1201        @Override
1202        public ActionForward refresh(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
1203            BudgetConstructionForm budgetConstructionForm = (BudgetConstructionForm) form;
1204            budgetConstructionForm.setDerivedValuesOnForm(request);
1205    
1206            // Do specific refresh stuff here based on refreshCaller parameter
1207            // typical refresh callers would be monthlyBudget or salarySetting or lookupable
1208            String refreshCaller = request.getParameter(KFSConstants.REFRESH_CALLER);
1209    
1210            if (refreshCaller != null && refreshCaller.equalsIgnoreCase(BCConstants.MONTHLY_BUDGET_REFRESH_CALLER)) {
1211    
1212                // monthly process applies any changes to the DB and the form session object
1213                // including any override to the request amount which also changes the request total
1214                // it also sets up calc monthly benefits if the line is involved in benefits
1215                BudgetDocumentService budgetDocumentService = SpringContext.getBean(BudgetDocumentService.class);
1216                budgetDocumentService.calculateBenefitsIfNeeded(budgetConstructionForm.getBudgetConstructionDocument());
1217    
1218            }
1219            if (refreshCaller != null && refreshCaller.equalsIgnoreCase(BCConstants.QUICK_SALARY_SETTING_REFRESH_CALLER)) {
1220    
1221    
1222                BudgetDocumentService budgetDocumentService = SpringContext.getBean(BudgetDocumentService.class);
1223    
1224                // if editing - reload expenditure and check for changes to detail salary lines and 2plg request amount
1225                boolean diffFound = false;
1226                if (budgetConstructionForm.isEditAllowed() && !budgetConstructionForm.isSystemViewOnly()) {
1227                    BudgetConstructionDocument currentBCDoc = budgetConstructionForm.getBudgetConstructionDocument();
1228    
1229                    // get the current set of salary setting related rows from DB and compare against preSalarySettingRows
1230    
1231                    List<PendingBudgetConstructionGeneralLedger> dbSalarySettingRows = budgetDocumentService.getPBGLSalarySettingRows(currentBCDoc);
1232                    for (PendingBudgetConstructionGeneralLedger dbSalarySettingRow : dbSalarySettingRows) {
1233                        if (budgetConstructionForm.getPreSalarySettingRows().containsKey(dbSalarySettingRow.getFinancialObjectCode() + dbSalarySettingRow.getFinancialSubObjectCode())) {
1234    
1235                            // update the existing row if a difference is found
1236                            KualiInteger dbReqAmount = dbSalarySettingRow.getAccountLineAnnualBalanceAmount();
1237                            KualiInteger preReqAmount = budgetConstructionForm.getPreSalarySettingRows().get(dbSalarySettingRow.getFinancialObjectCode() + dbSalarySettingRow.getFinancialSubObjectCode()).getAccountLineAnnualBalanceAmount();
1238                            Long dbVersionNumber = dbSalarySettingRow.getVersionNumber();
1239                            Long preReqVersionNumber = budgetConstructionForm.getPreSalarySettingRows().get(dbSalarySettingRow.getFinancialObjectCode() + dbSalarySettingRow.getFinancialSubObjectCode()).getVersionNumber();
1240                            if ((dbVersionNumber.compareTo(preReqVersionNumber) != 0) || (dbReqAmount.compareTo(preReqAmount) != 0)) {
1241                                budgetDocumentService.addOrUpdatePBGLRow(currentBCDoc, dbSalarySettingRow);
1242    
1243                                // only flag for existing line diff when the request amount changes
1244                                // changes in versionNumber implies offsetting updates of some sort
1245                                if (dbReqAmount.compareTo(preReqAmount) != 0) {
1246                                    diffFound = true;
1247                                }
1248                            }
1249                        }
1250                        else {
1251    
1252                            // update the req amount and version or add the new row to the current doc as needed
1253                            // insert the new DB row to the set in memory
1254                            budgetDocumentService.addOrUpdatePBGLRow(currentBCDoc, dbSalarySettingRow);
1255                            diffFound = true;
1256                        }
1257                    }
1258    
1259                    if (diffFound) {
1260                        this.adjustForSalarySettingChanges(budgetConstructionForm);
1261    
1262                    }
1263                }
1264    
1265            }
1266    
1267            if (refreshCaller != null && refreshCaller.equalsIgnoreCase(KFSConstants.KUALI_LOOKUPABLE_IMPL)) {
1268                final List REFRESH_FIELDS = Collections.unmodifiableList(Arrays.asList(new String[] { "financialObject", "financialSubObject" }));
1269                SpringContext.getBean(PersistenceService.class).retrieveReferenceObjects(budgetConstructionForm.getNewRevenueLine(), REFRESH_FIELDS);
1270                SpringContext.getBean(PersistenceService.class).retrieveReferenceObjects(budgetConstructionForm.getNewExpenditureLine(), REFRESH_FIELDS);
1271            }
1272    
1273            // balance inquiry anchor is set before doing a balance inquiry
1274            if (budgetConstructionForm.getBalanceInquiryReturnAnchor() != null) {
1275                budgetConstructionForm.setAnchor(budgetConstructionForm.getBalanceInquiryReturnAnchor());
1276                budgetConstructionForm.setBalanceInquiryReturnAnchor(null);
1277            }
1278            return mapping.findForward(KFSConstants.MAPPING_BASIC);
1279        }
1280    
1281        /**
1282         * This action changes the value of the hide field in the user interface so that when the page is rendered, the UI knows to show
1283         * all of the descriptions and labels for each of the pbgl line values.
1284         * 
1285         * @param mapping
1286         * @param form
1287         * @param request
1288         * @param response
1289         * @return ActionForward
1290         * @throws Exception
1291         */
1292        public ActionForward showDetails(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
1293            BudgetConstructionForm tForm = (BudgetConstructionForm) form;
1294            tForm.setHideDetails(false);
1295            return mapping.findForward(KFSConstants.MAPPING_BASIC);
1296        }
1297    
1298        /**
1299         * This action toggles the value of the hide field in the user interface to "hide" so that when the page is rendered, the UI
1300         * displays values without all of the descriptions and labels for each of the pbgl lines.
1301         * 
1302         * @param mapping
1303         * @param form
1304         * @param request
1305         * @param response
1306         * @return ActionForward
1307         * @throws Exception
1308         */
1309        public ActionForward hideDetails(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
1310            BudgetConstructionForm tForm = (BudgetConstructionForm) form;
1311            tForm.setHideDetails(true);
1312            return mapping.findForward(KFSConstants.MAPPING_BASIC);
1313        }
1314    
1315        public ActionForward toggleAdjustmentMeasurement(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
1316            BudgetConstructionForm docForm = (BudgetConstructionForm) form;
1317    
1318            boolean currentStatus = docForm.isHideAdjustmentMeasurement();
1319            docForm.setHideAdjustmentMeasurement(!currentStatus);
1320    
1321            return mapping.findForward(KFSConstants.MAPPING_BASIC);
1322        }
1323    
1324        public ActionForward adjustRevenueLinePercent(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
1325            BudgetConstructionForm docForm = (BudgetConstructionForm) form;
1326            BudgetConstructionDocument bcDoc = docForm.getBudgetConstructionDocument();
1327            PendingBudgetConstructionGeneralLedger revLine = bcDoc.getPendingBudgetConstructionGeneralLedgerRevenueLines().get(this.getSelectedLine(request));
1328    
1329            if (revLine.getAdjustmentAmount() != null) {
1330                this.adjustRequest(revLine);
1331                docForm.populatePBGLLines();
1332            }
1333    
1334            return mapping.findForward(KFSConstants.MAPPING_BASIC);
1335        }
1336    
1337        public ActionForward adjustExpenditureLinePercent(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
1338            BudgetConstructionForm docForm = (BudgetConstructionForm) form;
1339            BudgetConstructionDocument bcDoc = docForm.getBudgetConstructionDocument();
1340            PendingBudgetConstructionGeneralLedger expLine = bcDoc.getPendingBudgetConstructionGeneralLedgerExpenditureLines().get(this.getSelectedLine(request));
1341    
1342            if (expLine.getAdjustmentAmount() != null) {
1343                this.adjustRequest(expLine);
1344                docForm.populatePBGLLines();
1345            }
1346    
1347            return mapping.findForward(KFSConstants.MAPPING_BASIC);
1348        }
1349    
1350        public ActionForward adjustAllRevenueLinesPercent(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
1351            BudgetConstructionForm docForm = (BudgetConstructionForm) form;
1352            BudgetConstructionDocument bcDoc = docForm.getBudgetConstructionDocument();
1353            List<PendingBudgetConstructionGeneralLedger> revenueLines = bcDoc.getPendingBudgetConstructionGeneralLedgerRevenueLines();
1354    
1355            KualiDecimal adjustmentAmount = docForm.getRevenueAdjustmentAmount();
1356            if (adjustmentAmount != null) {
1357    
1358                // not sure we need this check since the tool isn't displayed in view mode
1359                boolean isEditable = docForm.isEditAllowed() && !docForm.isSystemViewOnly();
1360                for (PendingBudgetConstructionGeneralLedger revenueLine : revenueLines) {
1361                    if (isEditable) {
1362                        revenueLine.setAdjustmentAmount(adjustmentAmount);
1363                        this.adjustRequest(revenueLine);
1364                    }
1365                }
1366                if (isEditable){
1367                    docForm.populatePBGLLines();
1368                }
1369            }
1370    
1371            return mapping.findForward(KFSConstants.MAPPING_BASIC);
1372        }
1373    
1374        public ActionForward adjustAllExpenditureLinesPercent(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
1375            BudgetConstructionForm docForm = (BudgetConstructionForm) form;
1376            BudgetConstructionDocument bcDoc = docForm.getBudgetConstructionDocument();
1377            List<PendingBudgetConstructionGeneralLedger> expenditureLines = bcDoc.getPendingBudgetConstructionGeneralLedgerExpenditureLines();
1378    
1379            KualiDecimal adjustmentAmount = docForm.getExpenditureAdjustmentAmount();
1380            if (adjustmentAmount != null) {
1381    
1382                // not sure we need this check since the tool isn't displayed in view mode
1383                boolean isEditable = docForm.isEditAllowed() && !docForm.isSystemViewOnly();
1384                // (!benecalcDisabled && !empty item.laborObject && item.laborObject.financialObjectFringeOrSalaryCode == 'F')
1385                for (PendingBudgetConstructionGeneralLedger expenditureLine : expenditureLines) {
1386                    boolean isLineEditable = (isEditable && (docForm.isBenefitsCalculationDisabled() || (expenditureLine.getLaborObject() == null) || !expenditureLine.getLaborObject().getFinancialObjectFringeOrSalaryCode().equalsIgnoreCase(BCConstants.LABOR_OBJECT_FRINGE_CODE)));
1387                    if (isLineEditable) {
1388                        expenditureLine.setAdjustmentAmount(adjustmentAmount);
1389                        this.adjustRequest(expenditureLine);
1390                    }
1391                }
1392                if (isEditable){
1393                    docForm.populatePBGLLines();
1394                }
1395            }
1396    
1397            return mapping.findForward(KFSConstants.MAPPING_BASIC);
1398        }
1399    
1400        /**
1401         * Handles the document (account) pullup action, resetting the cached editingMode as appropriate for the new level.
1402         * 
1403         * @param mapping
1404         * @param form
1405         * @param request
1406         * @param response
1407         * @return
1408         * @throws Exception
1409         */
1410        public ActionForward performAccountPullup(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
1411            BudgetConstructionForm tForm = (BudgetConstructionForm) form;
1412    
1413            boolean doAllowPullup = false;
1414            boolean lockNeeded = false;
1415            boolean prePullReadOnlyAccess;
1416    
1417            BudgetDocumentService budgetDocumentService = SpringContext.getBean(BudgetDocumentService.class);
1418    
1419            // if system view only or view only - reload to get the latest status
1420            // and check that the document is still below the selected POV
1421            if (!tForm.isEditAllowed() || tForm.isSystemViewOnly()) {
1422    
1423                prePullReadOnlyAccess = true;
1424    
1425                // now reload the document and get latest status info
1426                loadDocument(tForm);
1427                this.initAuthorization(tForm);
1428                if (tForm.getBudgetConstructionDocument().getOrganizationLevelCode() < Integer.parseInt(tForm.getPullupKeyCode())) {
1429                    doAllowPullup = true;
1430    
1431                    // if not system view only mode, we are in document view mode - we'll need a lock before the pullup
1432                    // since by definition pullup puts the account at a full_entry level
1433                    // and we need exclusive access to perform the pullup
1434                    if (!tForm.isSystemViewOnly()) {
1435                        lockNeeded = true;
1436                    }
1437                }
1438                else {
1439                    // document has been moved and is either at the desired level or above
1440                    doAllowPullup = false;
1441                    lockNeeded = false;
1442    
1443                    // document has been moved above the desired level - let populate through an authorization exception
1444                    if (tForm.getBudgetConstructionDocument().getOrganizationLevelCode() > Integer.parseInt(tForm.getPullupKeyCode())) {
1445                        GlobalVariables.getMessageMap().putError(KFSConstants.GLOBAL_MESSAGES, BCKeyConstants.ERROR_BUDGET_PULLUP_DOCUMENT, "Document has already been moved above the selected level.");
1446                    }
1447                }
1448            }
1449            else {
1450                // we are in document full_entry
1451                // assume we already have a lock, allow the pullup
1452                // and we need to ensure the user can finish editing work if after pullup system goes to system view only
1453                prePullReadOnlyAccess = false;
1454                doAllowPullup = true;
1455            }
1456    
1457            if (lockNeeded || doAllowPullup) {
1458    
1459                // get a fresh header to use for lock and/or pullup
1460                HashMap primaryKey = new HashMap();
1461                primaryKey.put(KFSPropertyConstants.DOCUMENT_NUMBER, tForm.getDocument().getDocumentNumber());
1462                BudgetConstructionHeader budgetConstructionHeader = (BudgetConstructionHeader) SpringContext.getBean(BusinessObjectService.class).findByPrimaryKey(BudgetConstructionHeader.class, primaryKey);
1463                if (budgetConstructionHeader == null) {
1464                    GlobalVariables.getMessageMap().putError(BCConstants.BUDGET_CONSTRUCTION_SYSTEM_INFORMATION_TAB_ERRORS, BCKeyConstants.ERROR_BUDGET_PULLUP_DOCUMENT, "Fatal, Document not found.");
1465                    return mapping.findForward(KFSConstants.MAPPING_BASIC);
1466                }
1467    
1468                if (lockNeeded) {
1469                    // only successful lock allows pullup here
1470                    doAllowPullup = false;
1471    
1472                    LockService lockService = SpringContext.getBean(LockService.class);
1473                    BudgetConstructionLockStatus bcLockStatus = lockService.lockAccount(budgetConstructionHeader, GlobalVariables.getUserSession().getPerson().getPrincipalId());
1474                    LockStatus lockStatus = bcLockStatus.getLockStatus();
1475                    switch (lockStatus) {
1476                        case SUCCESS:
1477                            doAllowPullup = true;
1478                            break;
1479                        case BY_OTHER:
1480                            String lockerName = SpringContext.getBean(org.kuali.rice.kim.service.PersonService.class).getPerson(bcLockStatus.getAccountLockOwner()).getName();
1481                            GlobalVariables.getMessageMap().putError(BCConstants.BUDGET_CONSTRUCTION_SYSTEM_INFORMATION_TAB_ERRORS, BCKeyConstants.ERROR_BUDGET_PULLUP_DOCUMENT, "Locked by " + lockerName);
1482                            break;
1483                        case FLOCK_FOUND:
1484                            GlobalVariables.getMessageMap().putError(BCConstants.BUDGET_CONSTRUCTION_SYSTEM_INFORMATION_TAB_ERRORS, BCKeyConstants.ERROR_BUDGET_PULLUP_DOCUMENT, "Funding lock found.");
1485                            break;
1486                        default:
1487                            GlobalVariables.getMessageMap().putError(BCConstants.BUDGET_CONSTRUCTION_SYSTEM_INFORMATION_TAB_ERRORS, BCKeyConstants.ERROR_BUDGET_PULLUP_DOCUMENT, "Optimistic lock or other failure during lock attempt.");
1488                            break;
1489                    }
1490                }
1491    
1492                // attempt pullup
1493                if (doAllowPullup) {
1494                    budgetConstructionHeader.setOrganizationLevelCode(Integer.parseInt(tForm.getPullupKeyCode()));
1495                    budgetConstructionHeader.setOrganizationLevelChartOfAccountsCode(tForm.getAccountOrgHierLevels().get(Integer.parseInt(tForm.getPullupKeyCode())).getOrganizationChartOfAccountsCode());
1496                    budgetConstructionHeader.setOrganizationLevelOrganizationCode(tForm.getAccountOrgHierLevels().get(Integer.parseInt(tForm.getPullupKeyCode())).getOrganizationCode());
1497                    SpringContext.getBean(BusinessObjectService.class).save(budgetConstructionHeader);
1498    
1499                    // finally refresh the doc with the changed header info
1500                    tForm.getBudgetConstructionDocument().setVersionNumber(budgetConstructionHeader.getVersionNumber());
1501                    tForm.getBudgetConstructionDocument().setOrganizationLevelCode(budgetConstructionHeader.getOrganizationLevelCode());
1502                    tForm.getBudgetConstructionDocument().setOrganizationLevelChartOfAccountsCode(budgetConstructionHeader.getOrganizationLevelChartOfAccountsCode());
1503                    tForm.getBudgetConstructionDocument().setOrganizationLevelOrganizationCode(budgetConstructionHeader.getOrganizationLevelOrganizationCode());
1504    
1505                    // refresh the lock info even though the user may be pulling while in edit mode
1506                    tForm.getBudgetConstructionDocument().setBudgetLockUserIdentifier(budgetConstructionHeader.getBudgetLockUserIdentifier());
1507    
1508                    // refresh organization - so UI shows new level description
1509                    tForm.getBudgetConstructionDocument().refreshReferenceObject("organizationLevelOrganization");
1510    
1511                    this.initAuthorization(tForm);
1512    
1513                    // if before pullup, system was 'not system view only' goes to 'system view only' after pull
1514                    // need to manually remove the system view only editingMode here to allow the user to save work since still
1515                    // full_entry
1516                    if (tForm.isEditAllowed() && !prePullReadOnlyAccess) {
1517                        if (tForm.isSystemViewOnly()) {
1518                            tForm.getEditingMode().remove(BCConstants.EditModes.SYSTEM_VIEW_ONLY);
1519                        }
1520                    }
1521                }
1522            }
1523    
1524            return mapping.findForward(KFSConstants.MAPPING_BASIC);
1525        }
1526    
1527        /**
1528         * Handles the document (account) pushdown action, resetting the cached editingMode as appropriate for the new level.
1529         * 
1530         * @param mapping
1531         * @param form
1532         * @param request
1533         * @param response
1534         * @return
1535         * @throws Exception
1536         */
1537        public ActionForward performAccountPushdown(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
1538    
1539            boolean doAllowPushdown = false;
1540            boolean unlockNeeded = false;
1541            boolean prePushSystemViewOnly;
1542            BudgetConstructionForm tForm = (BudgetConstructionForm) form;
1543            BudgetConstructionDocument bcDocument = tForm.getBudgetConstructionDocument();
1544            BudgetDocumentService budgetDocumentService = SpringContext.getBean(BudgetDocumentService.class);
1545    
1546            // This method is called only if user has edit access and there is somewhere to push to.
1547            // If not system view only and the intended push level is view, we need to validate and save
1548            // Otherwise new level is still allowing editing, just push and keep current lock
1549            if (!tForm.isSystemViewOnly()) {
1550                prePushSystemViewOnly = false;
1551    
1552                // check editing mode at the intended level
1553                if (!hasEditPermission(bcDocument, tForm.getPushdownKeyCode(), GlobalVariables.getUserSession().getPerson())) {
1554                    budgetDocumentService.saveDocumentNoWorkflow(bcDocument);
1555                    tForm.initializePersistedRequestAmounts();
1556                    budgetDocumentService.calculateBenefitsIfNeeded(bcDocument);
1557    
1558                    // repop and refresh refs - esp monthly so jsp can properly display state
1559                    // tForm.populatePBGLLines();
1560    
1561                    unlockNeeded = true;
1562                }
1563                doAllowPushdown = true;
1564            }
1565            else {
1566                prePushSystemViewOnly = true;
1567    
1568                // reload document to get most up-to-date status and recheck that we still have FULL_ENTRY access
1569                // anything else means the document was moved by someone else and we may no longer even have read access
1570                loadDocument(tForm);
1571                this.initAuthorization(tForm);
1572                if (tForm.isEditAllowed()) {
1573                    doAllowPushdown = true;
1574                }
1575                else {
1576                    // document has moved
1577                    GlobalVariables.getMessageMap().putError(KFSConstants.GLOBAL_MESSAGES, BCKeyConstants.ERROR_BUDGET_PUSHDOWN_DOCUMENT, "Full Access Control Lost.");
1578                }
1579            }
1580    
1581            // gets here if editing and pushing to view and doc is valid and persisted
1582            // or we are pushing from edit to edit
1583            // or we are in system view only
1584            if (doAllowPushdown) {
1585    
1586                HashMap primaryKey = new HashMap();
1587                primaryKey.put(KFSPropertyConstants.DOCUMENT_NUMBER, tForm.getDocument().getDocumentNumber());
1588    
1589                BudgetConstructionHeader budgetConstructionHeader = (BudgetConstructionHeader) SpringContext.getBean(BusinessObjectService.class).findByPrimaryKey(BudgetConstructionHeader.class, primaryKey);
1590                if (budgetConstructionHeader != null) {
1591                    budgetConstructionHeader.setOrganizationLevelCode(Integer.parseInt(tForm.getPushdownKeyCode()));
1592                    if (Integer.parseInt(tForm.getPushdownKeyCode()) == 0) {
1593                        budgetConstructionHeader.setOrganizationLevelChartOfAccountsCode(null);
1594                        budgetConstructionHeader.setOrganizationLevelOrganizationCode(null);
1595                    }
1596                    else {
1597                        budgetConstructionHeader.setOrganizationLevelChartOfAccountsCode(tForm.getAccountOrgHierLevels().get(Integer.parseInt(tForm.getPushdownKeyCode())).getOrganizationChartOfAccountsCode());
1598                        budgetConstructionHeader.setOrganizationLevelOrganizationCode(tForm.getAccountOrgHierLevels().get(Integer.parseInt(tForm.getPushdownKeyCode())).getOrganizationCode());
1599                    }
1600                }
1601    
1602                // unlock if needed (which stores) - otherwise store the new level
1603                if (unlockNeeded) {
1604    
1605                    LockService lockService = SpringContext.getBean(LockService.class);
1606                    lockService.unlockAccount(budgetConstructionHeader);
1607                }
1608                else {
1609                    SpringContext.getBean(BusinessObjectService.class).save(budgetConstructionHeader);
1610    
1611                }
1612    
1613                // finally refresh the doc with the changed header info
1614                tForm.getBudgetConstructionDocument().setVersionNumber(budgetConstructionHeader.getVersionNumber());
1615                tForm.getBudgetConstructionDocument().setOrganizationLevelCode(budgetConstructionHeader.getOrganizationLevelCode());
1616                tForm.getBudgetConstructionDocument().setOrganizationLevelChartOfAccountsCode(budgetConstructionHeader.getOrganizationLevelChartOfAccountsCode());
1617                tForm.getBudgetConstructionDocument().setOrganizationLevelOrganizationCode(budgetConstructionHeader.getOrganizationLevelOrganizationCode());
1618                tForm.getBudgetConstructionDocument().setBudgetLockUserIdentifier(budgetConstructionHeader.getBudgetLockUserIdentifier());
1619    
1620                // refresh organization - so UI shows new level description
1621                tForm.getBudgetConstructionDocument().refreshReferenceObject("organizationLevelOrganization");
1622    
1623                this.initAuthorization(tForm);
1624    
1625                // if before push, system is 'not system view only' goes to 'system view only' after push
1626                // need to manually remove the system view only editingMode here to allow the user to save work if still full_entry
1627                if (tForm.isEditAllowed()) {
1628                    if (tForm.isSystemViewOnly() && !prePushSystemViewOnly) {
1629                        tForm.getEditingMode().remove(BCConstants.EditModes.SYSTEM_VIEW_ONLY);
1630                    }
1631                }
1632    
1633            }
1634    
1635            return mapping.findForward(KFSConstants.MAPPING_BASIC);
1636        }
1637    
1638        /**
1639         * Checks whether the current user would have access for the given budget document for the given organization level code
1640         * 
1641         * @param document current bc document
1642         * @param orgLevelCode organization level code for access check
1643         * @param user user to check access for
1644         * @return true if user would have edit permission, false otherwise
1645         */
1646        protected boolean hasEditPermission(BudgetConstructionDocument document, String orgLevelCode, Person user) {
1647            TransactionalDocumentAuthorizer documentAuthorizer = (TransactionalDocumentAuthorizer) SpringContext.getBean(DocumentHelperService.class).getDocumentAuthorizer(document);
1648    
1649            AttributeSet roleQualifiers = new AttributeSet();
1650            roleQualifiers.put(BCPropertyConstants.ORGANIZATION_LEVEL_CODE, orgLevelCode);
1651    
1652            List<BudgetConstructionAccountOrganizationHierarchy> accountOrganizationHierarchy = (List<BudgetConstructionAccountOrganizationHierarchy>) SpringContext.getBean(BudgetDocumentService.class).retrieveOrBuildAccountOrganizationHierarchy(document.getUniversityFiscalYear(), document.getChartOfAccountsCode(), document.getAccountNumber());
1653            for (BudgetConstructionAccountOrganizationHierarchy accountOrganization : accountOrganizationHierarchy) {
1654                if (accountOrganization.getOrganizationLevelCode().intValue() == Integer.parseInt(orgLevelCode)) {
1655                    roleQualifiers.put(BCPropertyConstants.ORGANIZATION_CHART_OF_ACCOUNTS_CODE, accountOrganization.getOrganizationChartOfAccountsCode());
1656                    roleQualifiers.put(KfsKimAttributes.ORGANIZATION_CODE, accountOrganization.getOrganizationCode());
1657                }
1658            }
1659    
1660            return documentAuthorizer.isAuthorizedByTemplate(document, KNSConstants.KNS_NAMESPACE, KimConstants.PermissionTemplateNames.EDIT_DOCUMENT, user.getPrincipalId(), null, roleQualifiers);
1661        }
1662    
1663        public ActionForward performReportDump(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
1664    
1665            BudgetConstructionForm tForm = (BudgetConstructionForm) form;
1666            BudgetConstructionDocument bcDocument = tForm.getBudgetConstructionDocument();
1667    
1668            // when we return from the lookup, our next request's method to call is going to be refresh
1669            tForm.registerEditableProperty(KNSConstants.DISPATCH_REQUEST_PARAMETER);
1670    
1671            if (tForm.isEditAllowed() && !tForm.isSystemViewOnly()) {
1672                BudgetDocumentService budgetDocumentService = SpringContext.getBean(BudgetDocumentService.class);
1673    
1674                budgetDocumentService.saveDocumentNoWorkflow(bcDocument);
1675                budgetDocumentService.calculateBenefitsIfNeeded(bcDocument);
1676                tForm.initializePersistedRequestAmounts();
1677    
1678                // repop and refresh refs - esp monthly so jsp can properly display state
1679                // tForm.populatePBGLLines();
1680            }
1681    
1682            // gets here if rules passed and doc detail gets persisted and refreshed
1683            String basePath = SpringContext.getBean(KualiConfigurationService.class).getPropertyString(KFSConstants.APPLICATION_URL_KEY);
1684            Properties parameters = new Properties();
1685            parameters.put(KFSConstants.DISPATCH_REQUEST_PARAMETER, BCConstants.MONTHLY_BUDGET_METHOD);
1686            parameters.put(KFSConstants.BACK_LOCATION, basePath + mapping.getPath() + ".do");
1687            parameters.put("documentNumber", tForm.getDocument().getDocumentNumber());
1688            parameters.put("universityFiscalYear", tForm.getUniversityFiscalYear().toString());
1689            parameters.put("chartOfAccountsCode", tForm.getChartOfAccountsCode());
1690            parameters.put("accountNumber", tForm.getAccountNumber());
1691            parameters.put("subAccountNumber", tForm.getSubAccountNumber());
1692            parameters.put(BCPropertyConstants.MAIN_WINDOW, (tForm.isMainWindow() ? "true" : "false"));
1693    
1694            // anchor, if it exists
1695            if (form instanceof KualiForm && StringUtils.isNotEmpty(((KualiForm) form).getAnchor())) {
1696                parameters.put(BCConstants.RETURN_ANCHOR, ((KualiForm) form).getAnchor());
1697            }
1698    
1699            // the form object is retrieved and removed upon return by KualiRequestProcessor.processActionForm()
1700            parameters.put(BCConstants.RETURN_FORM_KEY, GlobalVariables.getUserSession().addObject(form, BCConstants.FORMKEY_PREFIX));
1701    
1702            String lookupUrl = UrlFactory.parameterizeUrl(basePath + "/" + BCConstants.REPORT_RUNNER_ACTION, parameters);
1703            this.setupDocumentExit();
1704            return new ActionForward(lookupUrl, true);
1705        }
1706    
1707        public ActionForward performPercentChange(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
1708    
1709            BudgetConstructionForm tForm = (BudgetConstructionForm) form;
1710            GlobalVariables.getMessageMap().putError(KFSConstants.GLOBAL_MESSAGES, KFSKeyConstants.ERROR_UNIMPLEMENTED, "Percent Change");
1711    
1712            return mapping.findForward(KFSConstants.MAPPING_BASIC);
1713        }
1714    
1715        public ActionForward performRevMonthSpread(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
1716            return this.performMonthSpread(mapping, form, request, response, true);
1717        }
1718    
1719        public ActionForward performExpMonthSpread(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
1720            return this.performMonthSpread(mapping, form, request, response, false);
1721        }
1722    
1723        public ActionForward performMonthSpread(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response, boolean isRevenue) throws Exception {
1724    
1725            // no check for full_entry and system edit mode since this control is not displayed for this case
1726    
1727            // need to validate, save and calc benefits first
1728            // this is different than client/server model - need to always keep DB consistent
1729            BudgetConstructionForm tForm = (BudgetConstructionForm) form;
1730            BudgetConstructionDocument bcDocument = tForm.getBudgetConstructionDocument();
1731    
1732            BudgetDocumentService budgetDocumentService = SpringContext.getBean(BudgetDocumentService.class);
1733    
1734            // validate and save without checking monthly RI since the spread will keep things consistent
1735            if (isRevenue) {
1736                budgetDocumentService.saveDocumentNoWorkFlow(bcDocument, MonthSpreadDeleteType.REVENUE, false);
1737            }
1738            else {
1739                budgetDocumentService.saveDocumentNoWorkFlow(bcDocument, MonthSpreadDeleteType.EXPENDITURE, false);
1740            }
1741            tForm.initializePersistedRequestAmounts();
1742            // budgetDocumentService.calculateBenefitsIfNeeded(bcDocument);
1743    
1744            BudgetConstructionMonthlyBudgetsCreateDeleteService monthlyBudgetService = SpringContext.getBean(BudgetConstructionMonthlyBudgetsCreateDeleteService.class);
1745    
1746            if (isRevenue) {
1747                monthlyBudgetService.spreadBudgetConstructionMonthlyBudgetsRevenue(bcDocument.getDocumentNumber(), bcDocument.getUniversityFiscalYear(), bcDocument.getChartOfAccountsCode(), bcDocument.getAccountNumber(), bcDocument.getSubAccountNumber());
1748            }
1749            else {
1750                // service returns true if benefit eligible monthly lines exist
1751                if (monthlyBudgetService.spreadBudgetConstructionMonthlyBudgetsExpenditure(bcDocument.getDocumentNumber(), bcDocument.getUniversityFiscalYear(), bcDocument.getChartOfAccountsCode(), bcDocument.getAccountNumber(), bcDocument.getSubAccountNumber())) {
1752                    bcDocument.setMonthlyBenefitsCalcNeeded(true);
1753                    // budgetDocumentService.calculateBenefitsIfNeeded(bcDocument);
1754                }
1755            }
1756            budgetDocumentService.calculateBenefitsIfNeeded(bcDocument);
1757    
1758            // repop and refresh refs - esp monthly so jsp can properly display state
1759            tForm.populatePBGLLines();
1760    
1761            return mapping.findForward(KFSConstants.MAPPING_BASIC);
1762        }
1763    
1764        public ActionForward performRevMonthDelete(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
1765            return this.performMonthDelete(mapping, form, request, response, true);
1766        }
1767    
1768        public ActionForward performExpMonthDelete(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
1769            return this.performMonthDelete(mapping, form, request, response, false);
1770        }
1771    
1772        public ActionForward performMonthDelete(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response, boolean isRevenue) throws Exception {
1773    
1774            // no check for full_entry and system edit mode since this control is not displayed for this case
1775    
1776            // need to validate, save and calc benefits first
1777            // this is different than client/server model - need to always keep DB consistent
1778            BudgetConstructionForm tForm = (BudgetConstructionForm) form;
1779            BudgetConstructionDocument bcDocument = tForm.getBudgetConstructionDocument();
1780    
1781            BudgetDocumentService budgetDocumentService = SpringContext.getBean(BudgetDocumentService.class);
1782    
1783            // validate and save without checking monthly RI since the delete will make RI check moot
1784            if (isRevenue) {
1785                budgetDocumentService.saveDocumentNoWorkFlow(bcDocument, MonthSpreadDeleteType.REVENUE, false);
1786            }
1787            else {
1788                budgetDocumentService.saveDocumentNoWorkFlow(bcDocument, MonthSpreadDeleteType.EXPENDITURE, false);
1789            }
1790            tForm.initializePersistedRequestAmounts();
1791            // budgetDocumentService.calculateBenefitsIfNeeded(bcDocument);
1792    
1793            BudgetConstructionMonthlyBudgetsCreateDeleteService monthlyBudgetService = SpringContext.getBean(BudgetConstructionMonthlyBudgetsCreateDeleteService.class);
1794    
1795            if (isRevenue) {
1796                monthlyBudgetService.deleteBudgetConstructionMonthlyBudgetsRevenue(bcDocument.getDocumentNumber(), bcDocument.getUniversityFiscalYear(), bcDocument.getChartOfAccountsCode(), bcDocument.getAccountNumber(), bcDocument.getSubAccountNumber());
1797            }
1798            else {
1799                monthlyBudgetService.deleteBudgetConstructionMonthlyBudgetsExpenditure(bcDocument.getDocumentNumber(), bcDocument.getUniversityFiscalYear(), bcDocument.getChartOfAccountsCode(), bcDocument.getAccountNumber(), bcDocument.getSubAccountNumber());
1800            }
1801            budgetDocumentService.calculateBenefitsIfNeeded(bcDocument);
1802    
1803            // repop and refresh refs - esp monthly so jsp can properly display state
1804            tForm.populatePBGLLines();
1805    
1806            return mapping.findForward(KFSConstants.MAPPING_BASIC);
1807        }
1808    
1809        public ActionForward performCalculateBenefits(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
1810    
1811            // no check for full_entry and system edit mode since this control is not displayed for this case
1812            BudgetConstructionForm tForm = (BudgetConstructionForm) form;
1813            BudgetConstructionDocument bcDocument = (BudgetConstructionDocument) tForm.getDocument();
1814    
1815            // allow benecalc if account is not salary only and benefits calc not disabled
1816            if (!tForm.isBenefitsCalculationDisabled() && !bcDocument.isSalarySettingOnly()) {
1817                BudgetDocumentService budgetDocumentService = SpringContext.getBean(BudgetDocumentService.class);
1818    
1819                budgetDocumentService.saveDocumentNoWorkflow(bcDocument);
1820                tForm.initializePersistedRequestAmounts();
1821                budgetDocumentService.calculateBenefits(bcDocument);
1822    
1823                // repop and refresh refs - esp monthly so jsp can properly display state
1824                // tForm.populatePBGLLines();
1825    
1826            }
1827    
1828            return mapping.findForward(KFSConstants.MAPPING_BASIC);
1829        }
1830    
1831        protected void adjustRequest(PendingBudgetConstructionGeneralLedger pbglLine) {
1832    
1833            KualiInteger baseAmount = pbglLine.getFinancialBeginningBalanceLineAmount();
1834            if (baseAmount.isNonZero()) {
1835                KualiDecimal percent = pbglLine.getAdjustmentAmount();
1836                BigDecimal adjustedAmount = baseAmount.multiply(percent).divide(KFSConstants.ONE_HUNDRED);
1837    
1838                KualiInteger requestAmount = new KualiInteger(adjustedAmount).add(baseAmount);
1839                pbglLine.setAccountLineAnnualBalanceAmount(requestAmount);
1840            }
1841    
1842        }
1843    }