/* * The Kuali Financial System, a comprehensive financial management system for higher education. * * Copyright 2005-2014 The Kuali Foundation * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.kuali.kfs.module.bc.document.web.struts; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.xml.namespace.QName; import org.apache.commons.lang.StringUtils; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; import org.kuali.kfs.module.bc.BCConstants; import org.kuali.kfs.module.bc.BCKeyConstants; import org.kuali.kfs.module.bc.BCPropertyConstants; import org.kuali.kfs.module.bc.BCConstants.LockStatus; import org.kuali.kfs.module.bc.BCConstants.MonthSpreadDeleteType; import org.kuali.kfs.module.bc.businessobject.BCKeyLabelPair; import org.kuali.kfs.module.bc.businessobject.BudgetConstructionAccountOrganizationHierarchy; import org.kuali.kfs.module.bc.businessobject.BudgetConstructionAuthorizationStatus; import org.kuali.kfs.module.bc.businessobject.BudgetConstructionHeader; import org.kuali.kfs.module.bc.businessobject.BudgetConstructionLockStatus; import org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionGeneralLedger; import org.kuali.kfs.module.bc.document.BudgetConstructionDocument; import org.kuali.kfs.module.bc.document.service.BudgetConstructionMonthlyBudgetsCreateDeleteService; import org.kuali.kfs.module.bc.document.service.BudgetDocumentService; import org.kuali.kfs.module.bc.document.service.LockService; import org.kuali.kfs.module.bc.document.validation.event.AddPendingBudgetGeneralLedgerLineEvent; import org.kuali.kfs.module.bc.document.validation.event.DeletePendingBudgetGeneralLedgerLineEvent; import org.kuali.kfs.module.bc.exception.BudgetConstructionDocumentAuthorizationException; import org.kuali.kfs.module.bc.identity.BudgetConstructionNoAccessMessageSetting; import org.kuali.kfs.sys.KFSConstants; import org.kuali.kfs.sys.KFSKeyConstants; import org.kuali.kfs.sys.KFSPropertyConstants; import org.kuali.kfs.sys.context.SpringContext; import org.kuali.kfs.sys.identity.KfsKimAttributes; import org.kuali.rice.core.api.config.property.ConfigurationService; import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader; import org.kuali.rice.core.api.util.type.KualiDecimal; import org.kuali.rice.core.api.util.type.KualiInteger; import org.kuali.rice.kew.api.WorkflowDocument; import org.kuali.rice.kew.api.exception.WorkflowException; import org.kuali.rice.kim.api.KimConstants; import org.kuali.rice.kim.api.identity.Person; import org.kuali.rice.kim.api.identity.principal.Principal; import org.kuali.rice.kim.api.role.Role; import org.kuali.rice.kim.api.services.KimApiServiceLocator; import org.kuali.rice.kim.api.type.KimType; import org.kuali.rice.kim.framework.role.RoleTypeService; import org.kuali.rice.kns.document.authorization.TransactionalDocumentAuthorizer; import org.kuali.rice.kns.question.ConfirmationQuestion; import org.kuali.rice.kns.service.DocumentHelperService; import org.kuali.rice.kns.util.KNSGlobalVariables; import org.kuali.rice.kns.web.struts.action.KualiTransactionalDocumentActionBase; import org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase; import org.kuali.rice.kns.web.struts.form.KualiForm; import org.kuali.rice.krad.UserSession; import org.kuali.rice.krad.document.Document; import org.kuali.rice.krad.exception.AuthorizationException; import org.kuali.rice.krad.exception.UnknownDocumentIdException; import org.kuali.rice.krad.service.BusinessObjectService; import org.kuali.rice.krad.service.KualiRuleService; import org.kuali.rice.krad.service.PersistenceService; import org.kuali.rice.krad.service.SessionDocumentService; import org.kuali.rice.krad.util.GlobalVariables; import org.kuali.rice.krad.util.KRADConstants; import org.kuali.rice.krad.util.UrlFactory; /** * need to figure out if this should extend KualiAction, KualiDocumentActionBase or KualiTransactionDocumentActionBase */ public class BudgetConstructionAction extends KualiTransactionalDocumentActionBase { protected static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(BudgetConstructionAction.class); /** * Entry point to all actions Checks for cases where methodToCall is loadDocument, performAccountPullup or * performAccountPushdown and creates global messages to describe the new editingMode state. Also handles document locking if * the editingMode is BudgetConstructionEditMode.FULL_ENTRY. (Re)Populates the pullup and pushdown selection controls based on * the current level of the document and the user's approval access for the levels above and below the current level. * * @see org.kuali.rice.kns.web.struts.action.KualiTransactionalDocumentActionBase#execute(org.apache.struts.action.ActionMapping, * org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) */ @Override public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { BudgetConstructionForm budgetConstructionForm = (BudgetConstructionForm) form; // this is only used to indicate to the rules the user has clicked save or close save-yes // which forces tighter checks (nonZeroRequest amount) when access is cleanup mode if (budgetConstructionForm.getBudgetConstructionDocument().isCleanupModeActionForceCheck()) { budgetConstructionForm.getBudgetConstructionDocument().setCleanupModeActionForceCheck(Boolean.FALSE); } // TODO: the catch code comes from KualiDocumentActionBase.loadDocument() // this allows the top portion of the document to be populated and the close button to function // and also displays all the possible error messages for now // we need to update this once KIM is adjusted to handle BC no access cases // and maybe just show the authorization exception message ActionForward forward = null; try { forward = super.execute(mapping, form, request, response); } catch (AuthorizationException e) { forward = mapping.findForward(KFSConstants.MAPPING_BASIC); String docId = budgetConstructionForm.getDocId(); Document doc = null; doc = getDocumentService().getByDocumentHeaderId(docId); if (doc == null) { throw new UnknownDocumentIdException("Document no longer exists. It may have been cancelled before being saved."); } WorkflowDocument workflowDocument = doc.getDocumentHeader().getWorkflowDocument(); // if (!getDocumentHelperService().getDocumentAuthorizer(doc).canOpen(doc, // GlobalVariables.getUserSession().getPerson())) { // throw buildAuthorizationException("open", doc); // } // re-retrieve the document using the current user's session - remove the system user from the WorkflowDcument object if (workflowDocument != doc.getDocumentHeader().getWorkflowDocument()) { LOG.warn("Workflow document changed via canOpen check"); doc.getDocumentHeader().setWorkflowDocument(workflowDocument); } budgetConstructionForm.setDocument(doc); WorkflowDocument workflowDoc = doc.getDocumentHeader().getWorkflowDocument(); budgetConstructionForm.setDocTypeName(workflowDoc.getDocumentTypeName()); // KualiDocumentFormBase.populate() needs this updated in the session SpringContext.getBean(SessionDocumentService.class).addDocumentToUserSession(GlobalVariables.getUserSession(), workflowDoc); budgetConstructionForm.setSecurityNoAccess(true); setBudgetDocumentNoAccessMessage(budgetConstructionForm); budgetConstructionForm.getDocumentActions().put(KRADConstants.KUALI_ACTION_CAN_CLOSE, Boolean.TRUE); } // apprise user of granted access if (budgetConstructionForm.getMethodToCall().equals(BCConstants.BC_DOCUMENT_METHOD) || budgetConstructionForm.getMethodToCall().equals(BCConstants.BC_DOCUMENT_PULLUP_METHOD) || budgetConstructionForm.getMethodToCall().equals(BCConstants.BC_DOCUMENT_PUSHDOWN_METHOD)) { // init the account org hier state on initial load only - this is stored as hiddens if (budgetConstructionForm.getMethodToCall().equals(BCConstants.BC_DOCUMENT_METHOD)) { budgetConstructionForm.setAccountOrgHierLevels(SpringContext.getBean(BudgetDocumentService.class).getPushPullLevelList(budgetConstructionForm.getBudgetConstructionDocument(), GlobalVariables.getUserSession().getPerson())); } if (budgetConstructionForm.isSystemViewOnly()) { KNSGlobalVariables.getMessageList().add(BCKeyConstants.MESSAGE_BUDGET_SYSTEM_VIEW_ONLY); } if (!budgetConstructionForm.isEditAllowed()) { KNSGlobalVariables.getMessageList().add(BCKeyConstants.MESSAGE_BUDGET_VIEW_ONLY); } if (budgetConstructionForm.isEditAllowed()) { if (budgetConstructionForm.isSystemViewOnly()) { KNSGlobalVariables.getMessageList().add(BCKeyConstants.MESSAGE_BUDGET_VIEW_ONLY); } else { // tell the user if the document is in not budgetable mode budgetConstructionForm.getBudgetConstructionDocument().setBudgetableDocument(SpringContext.getBean(BudgetDocumentService.class).isBudgetableDocumentNoWagesCheck(budgetConstructionForm.getBudgetConstructionDocument())); // budgetConstructionForm.setBudgetableDocument(SpringContext.getBean(BudgetDocumentService.class).isBudgetableDocumentNoWagesCheck(budgetConstructionForm.getBudgetConstructionDocument())); if (!budgetConstructionForm.isBudgetableDocument()) { KNSGlobalVariables.getMessageList().add(BCKeyConstants.MESSAGE_BUDGET_DOCUMENT_NOT_BUDGETABLE); } KNSGlobalVariables.getMessageList().add(BCKeyConstants.MESSAGE_BUDGET_EDIT_ACCESS); } if (!budgetConstructionForm.isSystemViewOnly()) { LockService lockService = SpringContext.getBean(LockService.class); HashMap primaryKey = new HashMap(); primaryKey.put(KFSPropertyConstants.DOCUMENT_NUMBER, budgetConstructionForm.getDocument().getDocumentNumber()); BudgetConstructionHeader budgetConstructionHeader = SpringContext.getBean(BusinessObjectService.class).findByPrimaryKey(BudgetConstructionHeader.class, primaryKey); if (budgetConstructionHeader != null) { // BudgetConstructionLockStatus bcLockStatus = lockService.lockAccount(budgetConstructionHeader, // GlobalVariables.getUserSession().getPerson().getPrincipalId()); BudgetConstructionLockStatus bcLockStatus = lockService.lockAccountAndCommit(budgetConstructionHeader, GlobalVariables.getUserSession().getPerson().getPrincipalId()); if (bcLockStatus.getLockStatus() == LockStatus.SUCCESS) { // update the document version number // so the saved lock of header doesn't produce an optimistic lock error // and has the info we just put in the header so doc save doesn't wipe out the lock budgetConstructionForm.getBudgetConstructionDocument().setVersionNumber(bcLockStatus.getBudgetConstructionHeader().getVersionNumber()); budgetConstructionForm.getBudgetConstructionDocument().setBudgetLockUserIdentifier(bcLockStatus.getBudgetConstructionHeader().getBudgetLockUserIdentifier()); } else { if (bcLockStatus.getLockStatus() == LockStatus.BY_OTHER) { Principal principal = KimApiServiceLocator.getIdentityService().getPrincipal(bcLockStatus.getAccountLockOwner()); String lockerName = principal.getPrincipalName(); this.cleanupForLockError(budgetConstructionForm); GlobalVariables.getMessageMap().putError(KFSConstants.GLOBAL_ERRORS, BCKeyConstants.ERROR_BUDGET_DOCUMENT_LOCKED, lockerName); return forward; } else { if (bcLockStatus.getLockStatus() == LockStatus.FLOCK_FOUND) { this.cleanupForLockError(budgetConstructionForm); GlobalVariables.getMessageMap().putError(KFSConstants.GLOBAL_ERRORS, BCKeyConstants.ERROR_BUDGET_FUNDING_LOCKED); return forward; } else { this.cleanupForLockError(budgetConstructionForm); GlobalVariables.getMessageMap().putError(KFSConstants.GLOBAL_ERRORS, BCKeyConstants.ERROR_BUDGET_DOCUMENT_OTHER); return forward; } } } } else { // unlikely throw new BudgetConstructionDocumentAuthorizationException(GlobalVariables.getUserSession().getPerson().getName(), "open", budgetConstructionForm.getDocument().getDocumentHeader().getDocumentNumber(), "(can't find document for locking)", budgetConstructionForm.isPickListMode()); } // if editing, check if 2plg adjustment needed and calc benefits // since checkTwoPlugAdjusmtent will only be set in docHandler during initial load // if document is initially view only we want the 2plg adjustment to happen if the user does a pullup to edit if (budgetConstructionForm.isCheckTwoPlugAdjustment() && budgetConstructionForm.getBudgetConstructionDocument().isContainsTwoPlug() && !budgetConstructionForm.getBudgetConstructionDocument().isSalarySettingOnly()) { // do 2plg related benefits calc and adjust 2plg for any diff - reset checkTwoPlugAdjusment budgetConstructionForm.setCheckTwoPlugAdjustment(false); this.adjustForSalarySettingChanges(budgetConstructionForm); } } // getting here implies a lock or system view mode, try to build a pullup list // pushdown is only allowed if user has edit access (regardless of system view only mode) if (budgetConstructionForm.getBudgetConstructionDocument().getOrganizationLevelCode() != 0) { if (!budgetConstructionForm.getAccountOrgHierLevels().isEmpty()) { budgetConstructionForm.populatePushPullLevelKeyLabels(budgetConstructionForm.getBudgetConstructionDocument(), budgetConstructionForm.getAccountOrgHierLevels(), false); } } else { // document(account) at level zero - clear out the pushdownKeyLabels widget for any previous performPush case budgetConstructionForm.setPushdownLevelKeyLabels(new ArrayList<BCKeyLabelPair>()); } } // FULL_ENTRY // pullup is allowed regardless of editMode if (!budgetConstructionForm.getAccountOrgHierLevels().isEmpty()) { budgetConstructionForm.populatePushPullLevelKeyLabels(budgetConstructionForm.getBudgetConstructionDocument(), budgetConstructionForm.getAccountOrgHierLevels(), true); } } // cleanup the session edit mode store so we don't side effect organization salary setting if (budgetConstructionForm.isClosingDocument()) { GlobalVariables.getUserSession().removeObject(BCConstants.BC_DOC_AUTHORIZATION_STATUS_SESSIONKEY); } return forward; } /** * Finds the role type service associated with the document viewer role, than calls method on role type service to set the no * access message * * @param budgetConstructionForm form containing budget document */ protected void setBudgetDocumentNoAccessMessage(BudgetConstructionForm budgetConstructionForm) { Role roleInfo = KimApiServiceLocator.getRoleService().getRoleByNamespaceCodeAndName(BCConstants.BUDGET_CONSTRUCTION_NAMESPACE, BCConstants.KimApiConstants.DOCUMENT_VIEWER_ROLE_NAME); KimType typeInfo = KimApiServiceLocator.getKimTypeInfoService().getKimType(roleInfo.getKimTypeId()); if (StringUtils.isNotBlank(typeInfo.getServiceName())) { // fix to find service using new prefixed name if so configured // RoleTypeService roleTypeService = (RoleTypeService) SpringContext.getService(typeInfo.getServiceName()); RoleTypeService roleTypeService; String svcName = typeInfo.getServiceName(); String[] svcNameParts = svcName.split("\\{|\\}"); if (svcNameParts.length == 3){ String nameSpaceURI = svcNameParts[1]; String remoteServiceName = svcNameParts[2]; roleTypeService = (RoleTypeService)GlobalResourceLoader.getService(new QName(nameSpaceURI, remoteServiceName)); } else { roleTypeService = (RoleTypeService) SpringContext.getService(typeInfo.getServiceName()); } if (roleTypeService instanceof BudgetConstructionNoAccessMessageSetting) { ((BudgetConstructionNoAccessMessageSetting) roleTypeService).setNoAccessMessage(budgetConstructionForm.getBudgetConstructionDocument(), GlobalVariables.getUserSession().getPerson(), GlobalVariables.getMessageMap()); } else { LOG.warn(String.format("typeInfo.getServiceName() = %s is not an instance of BudgetConstructionNoAccessMessageSetting for role %s/%s", typeInfo.getServiceName(), BCConstants.BUDGET_CONSTRUCTION_NAMESPACE, BCConstants.KimApiConstants.DOCUMENT_VIEWER_ROLE_NAME)); } } else { LOG.warn(String.format("typeInfo.getServiceName() returned a blank service name for role %s/%s", BCConstants.BUDGET_CONSTRUCTION_NAMESPACE, BCConstants.KimApiConstants.DOCUMENT_VIEWER_ROLE_NAME)); } } /** * gwp - no call to super, need to work through command we will use randall - This method might be unnecessary, but putting it * here allows URL to be consistent with Document URLs gwp - i think we still want this method, just need to figure out if we * use command initiate or displayDocSearchView or something else. i expect we will get the account/subaccount or docnumber from * the previous form and assume the document will already exist regardless of creation by genesis or * * @param mapping * @param form * @param request * @param response * @return * @throws IOException * @throws ServletException */ @Override public ActionForward docHandler(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { BudgetConstructionForm budgetConstructionForm = (BudgetConstructionForm) form; loadDocument(budgetConstructionForm); // set flag to have execute perform 2plug adjusment if the doc goes into edit mode later budgetConstructionForm.setCheckTwoPlugAdjustment(true); this.initAuthorization(budgetConstructionForm); return mapping.findForward(KFSConstants.MAPPING_BASIC); } /** * Initially load the document from the DB Coded this to look like KualiDocumentActionBase.loadDocument() * * @param budgetConstructionForm * @throws WorkflowException */ @Override protected void loadDocument(KualiDocumentFormBase kualiDocumentFormBase) throws WorkflowException { BudgetConstructionForm budgetConstructionForm = (BudgetConstructionForm) kualiDocumentFormBase; BudgetConstructionHeader budgetConstructionHeader; if (budgetConstructionForm.getDocId() != null) { Map<String, Object> primaryKey = new HashMap<String, Object>(); primaryKey.put(KFSPropertyConstants.DOCUMENT_NUMBER, budgetConstructionForm.getDocId()); budgetConstructionHeader = SpringContext.getBean(BusinessObjectService.class).findByPrimaryKey(BudgetConstructionHeader.class, primaryKey); // getting called from doc search? if (budgetConstructionForm.getMethodToCall().equalsIgnoreCase(KFSConstants.DOC_HANDLER_METHOD)){ // now fill in the form parms normally passed from BC selection or Account selection screens budgetConstructionForm.setChartOfAccountsCode(budgetConstructionHeader.getChartOfAccountsCode()); budgetConstructionForm.setAccountNumber(budgetConstructionHeader.getAccountNumber()); budgetConstructionForm.setSubAccountNumber(budgetConstructionHeader.getSubAccountNumber()); budgetConstructionForm.setUniversityFiscalYear(budgetConstructionHeader.getUniversityFiscalYear()); budgetConstructionForm.setPickListMode(Boolean.TRUE); } } else { // use the passed url autoloaded parms to get the record from DB // BudgetConstructionDaoOjb bcHeaderDao; String chartOfAccountsCode = budgetConstructionForm.getChartOfAccountsCode(); String accountNumber = budgetConstructionForm.getAccountNumber(); String subAccountNumber = budgetConstructionForm.getSubAccountNumber(); Integer universityFiscalYear = budgetConstructionForm.getUniversityFiscalYear(); budgetConstructionHeader = SpringContext.getBean(BudgetDocumentService.class).getByCandidateKey(chartOfAccountsCode, accountNumber, subAccountNumber, universityFiscalYear); } kualiDocumentFormBase.setDocId(budgetConstructionHeader.getDocumentNumber()); super.loadDocument(kualiDocumentFormBase); BudgetConstructionDocument budgetConstructionDocument = (BudgetConstructionDocument) kualiDocumentFormBase.getDocument(); // init the benefits calc flags budgetConstructionDocument.setBenefitsCalcNeeded(false); budgetConstructionDocument.setMonthlyBenefitsCalcNeeded(false); // init the new line objects budgetConstructionForm.initNewLine(budgetConstructionForm.getNewRevenueLine(), true); budgetConstructionForm.initNewLine(budgetConstructionForm.getNewExpenditureLine(), false); // need this here to do totaling on initial load budgetConstructionForm.populatePBGLLines(); budgetConstructionForm.initializePersistedRequestAmounts(); } /** * Cleans up state info to handle no access lock errors * * @param budgetConstructionForm */ protected void cleanupForLockError(BudgetConstructionForm budgetConstructionForm) { budgetConstructionForm.setSecurityNoAccess(true); budgetConstructionForm.getDocumentActions().remove(KRADConstants.KUALI_ACTION_CAN_EDIT); budgetConstructionForm.getDocumentActions().remove(KRADConstants.KUALI_ACTION_CAN_SAVE); KNSGlobalVariables.getMessageList().remove(BCKeyConstants.MESSAGE_BUDGET_EDIT_ACCESS); } /** * Override to set authorization Maps from session * * @see org.kuali.rice.kns.web.struts.action.KualiTransactionalDocumentActionBase#populateAuthorizationFields(org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase) */ @Override protected void populateAuthorizationFields(KualiDocumentFormBase formBase) { BudgetConstructionAuthorizationStatus authorizationStatus = (BudgetConstructionAuthorizationStatus) GlobalVariables.getUserSession().retrieveObject(BCConstants.BC_DOC_AUTHORIZATION_STATUS_SESSIONKEY); if (authorizationStatus == null) { // execute handles any session timeout before this is called return; } formBase.setDocumentActions(authorizationStatus.getDocumentActions()); formBase.setEditingMode(authorizationStatus.getEditingMode()); } /** * Calls authorizer to determine if the user has edit permission and checks if budget construction is active is fiscal year * function table. Then updates the <code>BudgetConstructionEditStatus</code> in session. Finally updates authorization in the * form * * @param budgetConstructionForm current bc action form that will be updated */ protected void initAuthorization(BudgetConstructionForm budgetConstructionForm) { // GlobalVariables.setRequestCache(ROLE_QUALIFICATION_CACHE_NAME, budgetConstructionForm) super.populateAuthorizationFields(budgetConstructionForm); BudgetConstructionAuthorizationStatus editStatus = new BudgetConstructionAuthorizationStatus(); editStatus.setDocumentActions(budgetConstructionForm.getDocumentActions()); editStatus.setEditingMode(budgetConstructionForm.getEditingMode()); GlobalVariables.getUserSession().addObject(BCConstants.BC_DOC_AUTHORIZATION_STATUS_SESSIONKEY, editStatus); } /** * Applies adjustments due to 2plg existence at initial load or detected salary setting changes upon returning from the Quick * Salary Setting screen. The adjustments consist of calculating benefits and adding any expenditure total before/after * difference back into a 2plg row, creating or updating as needed. Then, validation is performed. Sucessful validation removes * the 2plg row if the final, post benefits calc, adjusted amount is zero. This method assumes the set of expenditure rows in * memory currently matches the DB. * * @param bcForm */ protected void adjustForSalarySettingChanges(BudgetConstructionForm bcForm) { BudgetDocumentService budgetDocumentService = SpringContext.getBean(BudgetDocumentService.class); BudgetConstructionDocument bcDoc = (BudgetConstructionDocument) bcForm.getDocument(); KualiInteger oldRequestAmount = bcDoc.getExpenditureAccountLineAnnualBalanceAmountTotal(); // calc benefits also handles setting persisted amounts and populating reloaded benefit rows budgetDocumentService.calculateBenefits(bcDoc); // bcForm.initializePersistedRequestAmounts(); // repop and refresh refs - esp monthly so jsp can properly display state // bcForm.populatePBGLLines(); // need 2plg adjustment and save, even if it is zero KualiInteger newRquestAmount = bcForm.getBudgetConstructionDocument().getExpenditureAccountLineAnnualBalanceAmountTotal(); PendingBudgetConstructionGeneralLedger twoPlugRow = budgetDocumentService.updatePendingBudgetGeneralLedgerPlug(bcDoc, newRquestAmount.subtract(oldRequestAmount)); } /** * @see org.kuali.rice.kns.web.struts.action.KualiDocumentActionBase#close(org.apache.struts.action.ActionMapping, * org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) */ @Override public ActionForward close(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { // return super.close(mapping, form, request, response); BudgetConstructionForm docForm = (BudgetConstructionForm) form; // only want to prompt them to save if they already can save if (docForm.getDocumentActions().keySet().contains(KRADConstants.KUALI_ACTION_CAN_SAVE)) { Object question = request.getParameter(KFSConstants.QUESTION_INST_ATTRIBUTE_NAME); ConfigurationService kualiConfiguration = SpringContext.getBean(ConfigurationService.class); // logic for close question if (question == null) { // ask question if not already asked return this.performQuestionWithoutInput(mapping, form, request, response, KFSConstants.DOCUMENT_SAVE_BEFORE_CLOSE_QUESTION, kualiConfiguration.getPropertyValueAsString(KFSKeyConstants.QUESTION_SAVE_BEFORE_CLOSE), KFSConstants.CONFIRMATION_QUESTION, KFSConstants.MAPPING_CLOSE, ""); } else { Object buttonClicked = request.getParameter(KFSConstants.QUESTION_CLICKED_BUTTON); if ((KFSConstants.DOCUMENT_SAVE_BEFORE_CLOSE_QUESTION.equals(question)) && ConfirmationQuestion.YES.equals(buttonClicked)) { // if yes button clicked - save the doc BudgetConstructionDocument bcDoc = (BudgetConstructionDocument) docForm.getDocument(); // force tighter checks when saving in cleanup mode if (!bcDoc.isBudgetableDocument()) { bcDoc.setCleanupModeActionForceCheck(Boolean.TRUE); } // SpringContext.getBean(DocumentService.class).updateDocument(docForm.getDocument()); BudgetDocumentService budgetDocumentService = SpringContext.getBean(BudgetDocumentService.class); budgetDocumentService.saveDocument(bcDoc); docForm.initializePersistedRequestAmounts(); if (bcDoc.isBenefitsCalcNeeded() || bcDoc.isMonthlyBenefitsCalcNeeded()) { budgetDocumentService.calculateBenefitsIfNeeded(bcDoc); KNSGlobalVariables.getMessageList().add(KFSKeyConstants.MESSAGE_SAVED); return mapping.findForward(KFSConstants.MAPPING_BASIC); } else { // else drop to close logic below } } // else drop to close logic below } } // do the unlock if they have full access and not system view mode if (docForm.isEditAllowed() && !docForm.isSystemViewOnly()) { LockService lockService = SpringContext.getBean(LockService.class); HashMap primaryKey = new HashMap(); primaryKey.put(KFSPropertyConstants.DOCUMENT_NUMBER, docForm.getDocument().getDocumentNumber()); BudgetConstructionHeader budgetConstructionHeader = SpringContext.getBean(BusinessObjectService.class).findByPrimaryKey(BudgetConstructionHeader.class, primaryKey); if (budgetConstructionHeader != null) { LockStatus lockStatus = lockService.unlockAccount(budgetConstructionHeader); } else { // unlikely, but benign problem here } } // flag to cleanup the session edit mode store so we don't side effect organization salary setting // used in the bottom of execute() docForm.setClosingDocument(Boolean.TRUE); if (docForm.isPickListMode()) { // redisplay with a message KNSGlobalVariables.getMessageList().add(BCKeyConstants.MESSAGE_BUDGET_SUCCESSFUL_CLOSE); docForm.setPickListClose(true); // this is a hack to do our own session document cleanup since refreshCaller=QuestionRefresh // prevents proper cleanup in KualiRequestProcessor.processActionPerform() UserSession userSession = (UserSession) request.getSession().getAttribute(KRADConstants.USER_SESSION_KEY); String docFormKey = docForm.getFormKey(); SpringContext.getBean(SessionDocumentService.class).purgeDocumentForm(docForm.getDocument().getDocumentNumber(), docFormKey, userSession, request.getRemoteAddr()); return mapping.findForward(KFSConstants.MAPPING_BASIC); } else { if (docForm.getReturnFormKey() == null) { // assume called from doc search or lost the session - go back to main return returnToSender(request, mapping, docForm); } else { // setup the return parms for the document and anchor and go back to doc select Properties parameters = new Properties(); parameters.put(KFSConstants.DISPATCH_REQUEST_PARAMETER, BCConstants.BC_SELECTION_REFRESH_METHOD); parameters.put(KFSConstants.DOC_FORM_KEY, docForm.getReturnFormKey()); parameters.put(KFSConstants.ANCHOR, docForm.getReturnAnchor()); parameters.put(KFSConstants.REFRESH_CALLER, BCConstants.BC_DOCUMENT_REFRESH_CALLER); String lookupUrl = UrlFactory.parameterizeUrl("/" + BCConstants.BC_SELECTION_ACTION, parameters); // this is a hack to do our own session document cleanup since refreshCaller=QuestionRefresh // prevents proper cleanup in KualiRequestProcessor.processActionPerform() UserSession userSession = (UserSession) request.getSession().getAttribute(KRADConstants.USER_SESSION_KEY); String docFormKey = docForm.getFormKey(); SpringContext.getBean(SessionDocumentService.class).purgeDocumentForm(docForm.getDocument().getDocumentNumber(), docFormKey, userSession, request.getRemoteAddr()); return new ActionForward(lookupUrl, true); } } } /** * @see org.kuali.rice.kns.web.struts.action.KualiDocumentActionBase#save(org.apache.struts.action.ActionMapping, * org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) */ @Override public ActionForward save(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { BudgetConstructionForm budgetConstructionForm = (BudgetConstructionForm) form; BudgetConstructionDocument bcDocument = (BudgetConstructionDocument) budgetConstructionForm.getDocument(); // DocumentService documentService = SpringContext.getBean(DocumentService.class); BudgetDocumentService budgetDocumentService = SpringContext.getBean(BudgetDocumentService.class); // force tighter checks when saving in cleanup mode if (!bcDocument.isBudgetableDocument()) { bcDocument.setCleanupModeActionForceCheck(Boolean.TRUE); } budgetDocumentService.saveDocument(bcDocument); budgetConstructionForm.initializePersistedRequestAmounts(); budgetDocumentService.calculateBenefitsIfNeeded(bcDocument); KNSGlobalVariables.getMessageList().add(KFSKeyConstants.MESSAGE_SAVED); budgetConstructionForm.setAnnotation(""); // redisplay the document along with document saved message return mapping.findForward(KFSConstants.MAPPING_BASIC); } /** * Calls the single line benefits impact screen by setting up the required parameters and feeding them to the temporary list * lookup action for the expenditure line selected. This is called from the ShowBenefits button on the BC document screen when * an expenditure line is associated with benefits and benefits calculation is enabled. * * @param mapping * @param form * @param request * @param response * @return * @throws Exception */ public ActionForward performShowBenefits(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { BudgetConstructionForm tForm = (BudgetConstructionForm) form; BudgetConstructionDocument tDoc = tForm.getBudgetConstructionDocument(); int selectIndex = this.getSelectedLine(request); PendingBudgetConstructionGeneralLedger expLine = tDoc.getPendingBudgetConstructionGeneralLedgerExpenditureLines().get(selectIndex); // when we return from the lookup, our next request's method to call is going to be refresh tForm.registerEditableProperty(KRADConstants.DISPATCH_REQUEST_PARAMETER); Properties parameters = new Properties(); parameters.put(KFSConstants.DISPATCH_REQUEST_PARAMETER, KFSConstants.START_METHOD); String basePath = SpringContext.getBean(ConfigurationService.class).getPropertyValueAsString(KFSConstants.APPLICATION_URL_KEY); parameters.put(KFSConstants.BACK_LOCATION, basePath + mapping.getPath() + ".do"); if (StringUtils.isNotEmpty(((KualiForm) form).getAnchor())) { parameters.put(BCConstants.RETURN_ANCHOR, ((KualiForm) form).getAnchor()); } // this hack sets the return anchor we want to return too after the inquiry // do this here so it gets into the session stored form version // refresh checks for this after and resets the anchor if (form instanceof KualiForm && StringUtils.isNotEmpty(((KualiForm) form).getAnchor())) { tForm.setBalanceInquiryReturnAnchor(((KualiForm) form).getAnchor()); } parameters.put(KRADConstants.DOC_FORM_KEY, GlobalVariables.getUserSession().addObjectWithGeneratedKey(form, BCConstants.FORMKEY_PREFIX)); parameters.put(KFSConstants.BUSINESS_OBJECT_CLASS_ATTRIBUTE, BCConstants.REQUEST_BENEFITS_BO); parameters.put(KFSConstants.HIDE_LOOKUP_RETURN_LINK, "true"); parameters.put(KFSConstants.SUPPRESS_ACTIONS, "true"); parameters.put(BCConstants.SHOW_INITIAL_RESULTS, "true"); parameters.put(BCConstants.TempListLookupMode.TEMP_LIST_LOOKUP_MODE, Integer.toString(BCConstants.TempListLookupMode.SHOW_BENEFITS)); parameters.put(KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR, expLine.getUniversityFiscalYear().toString()); parameters.put(KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE, expLine.getChartOfAccountsCode()); parameters.put(KFSConstants.FINANCIAL_OBJECT_CODE_PROPERTY_NAME, expLine.getFinancialObjectCode()); parameters.put(KFSPropertyConstants.ACCOUNT_LINE_ANNUAL_BALANCE_AMOUNT, expLine.getAccountLineAnnualBalanceAmount().toString()); parameters.put(KFSPropertyConstants.ACCOUNT_NUMBER, expLine.getAccountNumber()); parameters.put(KRADConstants.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); String url = UrlFactory.parameterizeUrl(basePath + "/" + BCConstants.ORG_TEMP_LIST_LOOKUP, parameters); this.setupDocumentExit(); return new ActionForward(url, true); } public ActionForward performBalanceInquiryForRevenueLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { return performBalanceInquiry(true, mapping, form, request, response); } public ActionForward performBalanceInquiryForExpenditureLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { return performBalanceInquiry(false, mapping, form, request, response); } /** * This method is similar to org.kuali.kfs.sys.web.struts.KualiAccountingDocumentActionBase.performBalanceInquiry() * * @param isRevenue * @param mapping * @param form * @param request * @param response * @return * @throws Exception */ public ActionForward performBalanceInquiry(boolean isRevenue, ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { final String docNumber; // get the selected line, setup parms and redirect to balance inquiry BudgetConstructionForm budgetConstructionForm = (BudgetConstructionForm) form; BudgetConstructionDocument bcDocument = (BudgetConstructionDocument) budgetConstructionForm.getDocument(); // when we return from the lookup, our next request's method to call is going to be refresh budgetConstructionForm.registerEditableProperty(KRADConstants.DISPATCH_REQUEST_PARAMETER); PendingBudgetConstructionGeneralLedger pbglLine; if (isRevenue) { pbglLine = bcDocument.getPendingBudgetConstructionGeneralLedgerRevenueLines().get(this.getSelectedLine(request)); } else { pbglLine = bcDocument.getPendingBudgetConstructionGeneralLedgerExpenditureLines().get(this.getSelectedLine(request)); } // build out base path for return location, use config service String basePath = SpringContext.getBean(ConfigurationService.class).getPropertyValueAsString(KFSConstants.APPLICATION_URL_KEY); // this hack sets the return anchor we want to return too after the inquiry // do this here so it gets into the session stored form version // refresh checks for this after and resets the anchor if (form instanceof KualiForm && StringUtils.isNotEmpty(((KualiForm) form).getAnchor())) { budgetConstructionForm.setBalanceInquiryReturnAnchor(((KualiForm) form).getAnchor()); } // build out the actual form key that will be used to retrieve the form on refresh String callerDocFormKey = GlobalVariables.getUserSession().addObjectWithGeneratedKey(form, BCConstants.FORMKEY_PREFIX); // now add required parameters Properties parameters = new Properties(); parameters.put(KFSConstants.DISPATCH_REQUEST_PARAMETER, KFSConstants.START_METHOD); // need this next param b/c the lookup's return back will overwrite // the original doc form key parameters.put(KFSConstants.BALANCE_INQUIRY_REPORT_MENU_CALLER_DOC_FORM_KEY, callerDocFormKey); parameters.put(KFSConstants.DOC_FORM_KEY, callerDocFormKey); parameters.put(KFSConstants.BACK_LOCATION, basePath + mapping.getPath() + ".do"); // anchor, if it exists // this doesn't seem to work with the balance inquiry infrastructure, so added hack above if (form instanceof KualiForm && StringUtils.isNotEmpty(((KualiForm) form).getAnchor())) { parameters.put(BCConstants.RETURN_ANCHOR, ((KualiForm) form).getAnchor()); } if (StringUtils.isNotBlank(pbglLine.getChartOfAccountsCode())) { parameters.put("chartOfAccountsCode", pbglLine.getChartOfAccountsCode()); } if (StringUtils.isNotBlank(pbglLine.getAccountNumber())) { parameters.put("accountNumber", pbglLine.getAccountNumber()); } if (StringUtils.isNotBlank(pbglLine.getFinancialObjectCode())) { parameters.put("financialObjectCode", pbglLine.getFinancialObjectCode()); } if (StringUtils.isNotBlank(pbglLine.getSubAccountNumber())) { parameters.put("subAccountNumber", pbglLine.getSubAccountNumber()); } if (StringUtils.isNotBlank(pbglLine.getFinancialSubObjectCode())) { parameters.put("financialSubObjectCode", pbglLine.getFinancialSubObjectCode()); } String lookupUrl = UrlFactory.parameterizeUrl(basePath + "/" + KFSConstants.BALANCE_INQUIRY_REPORT_MENU_ACTION, parameters); this.setupDocumentExit(); return new ActionForward(lookupUrl, true); } /** * Calls performMonthlyBudget for the selected revenue line * * @param mapping * @param form * @param request * @param response * @return * @throws Exception */ public ActionForward performMonthlyRevenueBudget(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { return performMonthlyBudget(true, mapping, form, request, response); } /** * Calls performMonthlyBudget for the selected expenditure line * * @param mapping * @param form * @param request * @param response * @return * @throws Exception */ public ActionForward performMonthlyExpenditureBudget(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { return performMonthlyBudget(false, mapping, form, request, response); } /** * Forwards the user to the monthly budget screen. Doing this in edit mode causes the document to be validated, saved and * benefits calculated (if needed). * * @param isRevenue * @param mapping * @param form * @param request * @param response * @return * @throws Exception */ public ActionForward performMonthlyBudget(boolean isRevenue, ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { final String docNumber; // this to checks for 2PLG and turns off SS/monthly RI check if found // the final save after removing 2PLG will catch any monthly discrepencies // also need to save object,subobject key we are operating on so refresh can get the latest row version from DB // validate, save, etc first then goto the monthly screen or redisplay if errors BudgetConstructionForm budgetConstructionForm = (BudgetConstructionForm) form; BudgetConstructionDocument bcDocument = (BudgetConstructionDocument) budgetConstructionForm.getDocument(); PendingBudgetConstructionGeneralLedger pbglLine; if (isRevenue) { pbglLine = bcDocument.getPendingBudgetConstructionGeneralLedgerRevenueLines().get(this.getSelectedLine(request)); } else { pbglLine = bcDocument.getPendingBudgetConstructionGeneralLedgerExpenditureLines().get(this.getSelectedLine(request)); } // when we return from the lookup, our next request's method to call is going to be refresh budgetConstructionForm.registerEditableProperty(KRADConstants.DISPATCH_REQUEST_PARAMETER); if (budgetConstructionForm.isEditAllowed() && !budgetConstructionForm.isSystemViewOnly()) { BudgetDocumentService budgetDocumentService = SpringContext.getBean(BudgetDocumentService.class); // if the doc contains a 2plg line, turn off RI checking to allow cleanup. // The act of attempting to remove a 2plg (delete) forces a complete RI check. // The document is assumed inconsistent as long as a 2plg exists. if (!isRevenue && bcDocument.isContainsTwoPlug()) { if (pbglLine.getLaborObject() != null && pbglLine.getLaborObject().isDetailPositionRequiredIndicator()) { budgetDocumentService.saveDocumentNoWorkFlow(bcDocument, MonthSpreadDeleteType.EXPENDITURE, false); } else { budgetDocumentService.saveDocumentNoWorkFlow(bcDocument, MonthSpreadDeleteType.EXPENDITURE, true); } } else { budgetDocumentService.saveDocumentNoWorkflow(bcDocument); } budgetConstructionForm.initializePersistedRequestAmounts(); budgetDocumentService.calculateBenefitsIfNeeded(bcDocument); // repop and refresh refs - esp monthly so jsp can properly display state // budgetConstructionForm.populatePBGLLines(); } // String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + // request.getContextPath(); String basePath = SpringContext.getBean(ConfigurationService.class).getPropertyValueAsString(KFSConstants.APPLICATION_URL_KEY); Properties parameters = new Properties(); parameters.put(KFSConstants.DISPATCH_REQUEST_PARAMETER, BCConstants.MONTHLY_BUDGET_METHOD); parameters.put(KFSConstants.BACK_LOCATION, basePath + mapping.getPath() + ".do"); parameters.put("documentNumber", pbglLine.getDocumentNumber()); parameters.put("universityFiscalYear", pbglLine.getUniversityFiscalYear().toString()); parameters.put("chartOfAccountsCode", pbglLine.getChartOfAccountsCode()); parameters.put("accountNumber", pbglLine.getAccountNumber()); parameters.put("subAccountNumber", pbglLine.getSubAccountNumber()); parameters.put("financialObjectCode", pbglLine.getFinancialObjectCode()); parameters.put("financialSubObjectCode", pbglLine.getFinancialSubObjectCode()); parameters.put("financialBalanceTypeCode", pbglLine.getFinancialBalanceTypeCode()); parameters.put("financialObjectTypeCode", pbglLine.getFinancialObjectTypeCode()); parameters.put("revenue", (isRevenue ? "true" : "false")); parameters.put(BCPropertyConstants.MAIN_WINDOW, (budgetConstructionForm.isMainWindow() ? "true" : "false")); // anchor, if it exists if (form instanceof KualiForm && StringUtils.isNotEmpty(((KualiForm) form).getAnchor())) { parameters.put(BCConstants.RETURN_ANCHOR, ((KualiForm) form).getAnchor()); } // the form object is retrieved and removed upon return by KualiRequestProcessor.processActionForm() parameters.put(BCConstants.RETURN_FORM_KEY, GlobalVariables.getUserSession().addObjectWithGeneratedKey(form, BCConstants.FORMKEY_PREFIX)); String lookupUrl = UrlFactory.parameterizeUrl(basePath + "/" + BCConstants.MONTHLY_BUDGET_ACTION, parameters); this.setupDocumentExit(); return new ActionForward(lookupUrl, true); } /** * Forwards the user to the quick salary setting screen. Doing this in edit mode causes the document to be validated, saved and * benefits calculated (if needed). * * @param mapping * @param form * @param request * @param response * @return * @throws Exception */ public ActionForward performSalarySetting(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { final String docNumber; // this to checks for 2PLG and turns off monthly RI check if found // the final save after removing 2PLG will catch any monthly discrepencies // validate, save, etc first then goto the SalarySetting screen or redisplay if errors BudgetConstructionForm budgetConstructionForm = (BudgetConstructionForm) form; BudgetConstructionDocument bcDocument = (BudgetConstructionDocument) budgetConstructionForm.getDocument(); // when we return from the lookup, our next request's method to call is going to be refresh budgetConstructionForm.registerEditableProperty(KRADConstants.DISPATCH_REQUEST_PARAMETER); if (budgetConstructionForm.isEditAllowed() && !budgetConstructionForm.isSystemViewOnly()) { BudgetDocumentService budgetDocumentService = SpringContext.getBean(BudgetDocumentService.class); // Regardless if the doc contains a 2plg line, turn off RI checking to allow cleanup. // The act of attempting to remove a 2plg (delete) forces a complete RI check. // The document is assumed inconsistent as long as a 2plg exists. budgetDocumentService.saveDocumentNoWorkFlow(bcDocument, MonthSpreadDeleteType.EXPENDITURE, false); // init persisted property and get the current list of salary setting related rows budgetConstructionForm.initializePersistedRequestAmounts(true); budgetDocumentService.calculateBenefitsIfNeeded(bcDocument); // repop and refresh refs - esp monthly so jsp can properly display state // budgetConstructionForm.populatePBGLLines(); } PendingBudgetConstructionGeneralLedger pbglLine; pbglLine = bcDocument.getPendingBudgetConstructionGeneralLedgerExpenditureLines().get(this.getSelectedLine(request)); // String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + // request.getContextPath(); String basePath = SpringContext.getBean(ConfigurationService.class).getPropertyValueAsString(KFSConstants.APPLICATION_URL_KEY); Properties parameters = new Properties(); parameters.put(KFSConstants.DISPATCH_REQUEST_PARAMETER, BCConstants.QUICK_SALARY_SETTING_METHOD); parameters.put(KFSConstants.BACK_LOCATION, basePath + mapping.getPath() + ".do"); parameters.put("documentNumber", pbglLine.getDocumentNumber()); parameters.put("universityFiscalYear", pbglLine.getUniversityFiscalYear().toString()); parameters.put("chartOfAccountsCode", pbglLine.getChartOfAccountsCode()); parameters.put("accountNumber", pbglLine.getAccountNumber()); parameters.put("subAccountNumber", pbglLine.getSubAccountNumber()); parameters.put("financialObjectCode", pbglLine.getFinancialObjectCode()); parameters.put("financialSubObjectCode", pbglLine.getFinancialSubObjectCode()); parameters.put("financialBalanceTypeCode", pbglLine.getFinancialBalanceTypeCode()); parameters.put("financialObjectTypeCode", pbglLine.getFinancialObjectTypeCode()); parameters.put(BCPropertyConstants.MAIN_WINDOW, (budgetConstructionForm.isMainWindow() ? "true" : "false")); // anchor, if it exists if (form instanceof KualiForm && StringUtils.isNotEmpty(((KualiForm) form).getAnchor())) { parameters.put(BCConstants.RETURN_ANCHOR, ((KualiForm) form).getAnchor()); } // the form object is retrieved and removed upon return by KualiRequestProcessor.processActionForm() parameters.put(BCConstants.RETURN_FORM_KEY, GlobalVariables.getUserSession().addObjectWithGeneratedKey(form, BCConstants.FORMKEY_PREFIX)); String lookupUrl = UrlFactory.parameterizeUrl(basePath + "/" + BCConstants.QUICK_SALARY_SETTING_ACTION, parameters); this.setupDocumentExit(); return new ActionForward(lookupUrl, true); } /** * This adds a revenue line to the BC document * * @param mapping * @param form * @param request * @param response * @return * @throws Exception */ public ActionForward insertRevenueLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { BudgetConstructionForm budgetConstructionForm = (BudgetConstructionForm) form; PendingBudgetConstructionGeneralLedger line = budgetConstructionForm.getNewRevenueLine(); boolean rulePassed = true; rulePassed &= SpringContext.getBean(KualiRuleService.class).applyRules(new AddPendingBudgetGeneralLedgerLineEvent(BCConstants.NEW_REVENUE_LINE_PROPERTY_NAME, budgetConstructionForm.getDocument(), line, true)); if (rulePassed) { // add PBGLLine insertPBGLLine(true, budgetConstructionForm, line); // clear the used newRevenueLine budgetConstructionForm.setNewRevenueLine(new PendingBudgetConstructionGeneralLedger()); budgetConstructionForm.initNewLine(budgetConstructionForm.getNewRevenueLine(), true); } return mapping.findForward(KFSConstants.MAPPING_BASIC); } /** * This adds an expenditure line to the BC document * * @param mapping * @param form * @param request * @param response * @return * @throws Exception */ public ActionForward insertExpenditureLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { BudgetConstructionForm budgetConstructionForm = (BudgetConstructionForm) form; PendingBudgetConstructionGeneralLedger line = budgetConstructionForm.getNewExpenditureLine(); boolean rulePassed = true; rulePassed &= SpringContext.getBean(KualiRuleService.class).applyRules(new AddPendingBudgetGeneralLedgerLineEvent(BCConstants.NEW_EXPENDITURE_LINE_PROPERTY_NAME, budgetConstructionForm.getDocument(), line, false)); if (rulePassed) { // add PBGLLine insertPBGLLine(false, budgetConstructionForm, line); // clear the used newExpenditureLine budgetConstructionForm.setNewExpenditureLine(new PendingBudgetConstructionGeneralLedger()); budgetConstructionForm.initNewLine(budgetConstructionForm.getNewExpenditureLine(), false); } return mapping.findForward(KFSConstants.MAPPING_BASIC); } /** * This inserts a PBGL revenue or expenditure line * * @param isRevenue * @param budgetConstructionForm * @param line */ protected void insertPBGLLine(boolean isRevenue, BudgetConstructionForm budgetConstructionForm, PendingBudgetConstructionGeneralLedger line) { BudgetConstructionDocument bcDoc = (BudgetConstructionDocument) budgetConstructionForm.getDocument(); // KFSMI-7828 reset object type from object code table line.setFinancialObjectTypeCode(line.getFinancialObject().getFinancialObjectTypeCode()); // null subobj must be set to dashes if (StringUtils.isBlank(line.getFinancialSubObjectCode())) { line.setFinancialSubObjectCode(KFSConstants.getDashFinancialSubObjectCode()); } // check the DB for an existing persisted version of the line // and reinstate that with a message to the user indicating such boolean isReinstated = false; Map<String, Object> primaryKey = new HashMap<String, Object>(); primaryKey.put(KFSPropertyConstants.DOCUMENT_NUMBER, line.getDocumentNumber()); primaryKey.put(KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR, line.getUniversityFiscalYear()); primaryKey.put(KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE, line.getChartOfAccountsCode()); primaryKey.put(KFSPropertyConstants.ACCOUNT_NUMBER, line.getAccountNumber()); primaryKey.put(KFSPropertyConstants.SUB_ACCOUNT_NUMBER, line.getSubAccountNumber()); primaryKey.put(KFSPropertyConstants.FINANCIAL_BALANCE_TYPE_CODE, line.getFinancialBalanceTypeCode()); primaryKey.put(KFSPropertyConstants.FINANCIAL_OBJECT_TYPE_CODE, line.getFinancialObjectTypeCode()); primaryKey.put(KFSPropertyConstants.FINANCIAL_OBJECT_CODE, line.getFinancialObjectCode()); primaryKey.put(KFSPropertyConstants.FINANCIAL_SUB_OBJECT_CODE, line.getFinancialSubObjectCode()); PendingBudgetConstructionGeneralLedger dbLine = SpringContext.getBean(BusinessObjectService.class).findByPrimaryKey(PendingBudgetConstructionGeneralLedger.class, primaryKey); if (dbLine != null){ line = dbLine; SpringContext.getBean(BudgetDocumentService.class).populatePBGLLine(line); isReinstated = true; } // add the line in the proper order - assumes already exists check is done in rules int insertPoint = bcDoc.addPBGLLine(line, isRevenue); if (isReinstated){ String errorKey; if (isRevenue){ errorKey = KRADConstants.DOCUMENT_PROPERTY_NAME + "." + BCPropertyConstants.PENDING_BUDGET_CONSTRUCTION_GENERAL_LEDGER_REVENUE_LINES + "[" + insertPoint + "]." + KFSPropertyConstants.ACCOUNT_LINE_ANNUAL_BALANCE_AMOUNT; } else { errorKey = KRADConstants.DOCUMENT_PROPERTY_NAME + "." + BCPropertyConstants.PENDING_BUDGET_CONSTRUCTION_GENERAL_LEDGER_EXPENDITURE_LINES + "[" + insertPoint + "]." + KFSPropertyConstants.ACCOUNT_LINE_ANNUAL_BALANCE_AMOUNT; } GlobalVariables.getMessageMap().putError(errorKey, BCKeyConstants.ERROR_BUDGET_LINE_REINSTATED, dbLine.getFinancialObjectCode() + "," + dbLine.getFinancialSubObjectCode()); } // adjust totals if (line.getAccountLineAnnualBalanceAmount() != null && line.getAccountLineAnnualBalanceAmount() != KualiInteger.ZERO) { if (isRevenue) { bcDoc.setRevenueAccountLineAnnualBalanceAmountTotal(bcDoc.getRevenueAccountLineAnnualBalanceAmountTotal().add(line.getAccountLineAnnualBalanceAmount())); } else { bcDoc.setExpenditureAccountLineAnnualBalanceAmountTotal(bcDoc.getExpenditureAccountLineAnnualBalanceAmountTotal().add(line.getAccountLineAnnualBalanceAmount())); } } if (line.getFinancialBeginningBalanceLineAmount() != null && line.getFinancialBeginningBalanceLineAmount() != KualiInteger.ZERO) { if (isRevenue) { bcDoc.setRevenueFinancialBeginningBalanceLineAmountTotal(bcDoc.getRevenueFinancialBeginningBalanceLineAmountTotal().add(line.getFinancialBeginningBalanceLineAmount())); } else { bcDoc.setExpenditureFinancialBeginningBalanceLineAmountTotal(bcDoc.getExpenditureFinancialBeginningBalanceLineAmountTotal().add(line.getFinancialBeginningBalanceLineAmount())); } } } /** * Deletes an existing PendingBudgetConstructionGeneralLedger revenue line if rules passed. Any associated monthly budget * (BudgetConstructionMonthly) is also deleted. * * @param mapping * @param form * @param request * @param response * @return * @throws Exception */ public ActionForward deleteRevenueLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { BudgetConstructionForm tForm = (BudgetConstructionForm) form; BudgetConstructionDocument tDoc = tForm.getBudgetConstructionDocument(); boolean rulePassed = true; int deleteIndex = this.getLineToDelete(request); // check business rule if there is a persisted request amount, otherwise the line can just be removed PendingBudgetConstructionGeneralLedger revLine = tDoc.getPendingBudgetConstructionGeneralLedgerRevenueLines().get(deleteIndex); if (revLine.getPersistedAccountLineAnnualBalanceAmount() == null) { rulePassed = true; } else { // check deletion rules and delete if passed String errorPath = KFSConstants.DOCUMENT_PROPERTY_NAME + "." + BCPropertyConstants.PENDING_BUDGET_CONSTRUCTION_GENERAL_LEDGER_REVENUE_LINES + "[" + deleteIndex + "]"; rulePassed &= SpringContext.getBean(KualiRuleService.class).applyRules(new DeletePendingBudgetGeneralLedgerLineEvent(errorPath, tDoc, revLine, true)); } if (rulePassed) { deletePBGLLine(true, tForm, deleteIndex, revLine); } return mapping.findForward(KFSConstants.MAPPING_BASIC); } /** * Deletes an existing PendingBudgetConstructionGeneralLedger expenditure line if rules passed Any associated monthly budget * (BudgetConstructionMonthly) is also deleted. Check for the special case where the line is a 2PLG line, in which case the * document is validated and RI checks are forced even if there are no current differences between persisted and request * amounts. * * @param mapping * @param form * @param request * @param response * @return * @throws Exception */ public ActionForward deleteExpenditureLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { BudgetConstructionForm tForm = (BudgetConstructionForm) form; BudgetConstructionDocument tDoc = tForm.getBudgetConstructionDocument(); boolean rulePassed = true; int deleteIndex = this.getLineToDelete(request); // check business rule if there is a persisted request amount, otherwise the line can just be removed PendingBudgetConstructionGeneralLedger expLine = tDoc.getPendingBudgetConstructionGeneralLedgerExpenditureLines().get(deleteIndex); if (expLine.getPersistedAccountLineAnnualBalanceAmount() == null) { rulePassed = true; } else { // check regular deletion rules and delete if passed String errorPath = KFSConstants.DOCUMENT_PROPERTY_NAME + "." + BCPropertyConstants.PENDING_BUDGET_CONSTRUCTION_GENERAL_LEDGER_EXPENDITURE_LINES + "[" + deleteIndex + "]"; rulePassed &= SpringContext.getBean(KualiRuleService.class).applyRules(new DeletePendingBudgetGeneralLedgerLineEvent(errorPath, tDoc, expLine, false)); } if (rulePassed) { // if the line is a 2PLG line do document validation, which forces RI checks in no current change situation if (expLine.getFinancialObjectCode().equalsIgnoreCase(KFSConstants.BudgetConstructionConstants.OBJECT_CODE_2PLG)) { SpringContext.getBean(BudgetDocumentService.class).validateDocument(tDoc); } // gets here if line is not 2PLG or doc is valid from 2plg perspective deletePBGLLine(false, tForm, deleteIndex, expLine); } return mapping.findForward(KFSConstants.MAPPING_BASIC); } /** * Deletes an existing PendingBudgetConstructionGeneralLedger revenue or expenditure line along with any associated monthly * budget (BudgetConstructionMonthly) * * @param isRevenue * @param budgetConstructionForm * @param deleteIndex */ protected void deletePBGLLine(boolean isRevenue, BudgetConstructionForm budgetConstructionForm, int deleteIndex, PendingBudgetConstructionGeneralLedger line) { BudgetConstructionDocument bcDoc = budgetConstructionForm.getBudgetConstructionDocument(); // adjust totals if (line.getAccountLineAnnualBalanceAmount() != null && line.getAccountLineAnnualBalanceAmount() != KualiInteger.ZERO) { if (isRevenue) { bcDoc.setRevenueAccountLineAnnualBalanceAmountTotal(bcDoc.getRevenueAccountLineAnnualBalanceAmountTotal().subtract(line.getAccountLineAnnualBalanceAmount())); } else { bcDoc.setExpenditureAccountLineAnnualBalanceAmountTotal(bcDoc.getExpenditureAccountLineAnnualBalanceAmountTotal().subtract(line.getAccountLineAnnualBalanceAmount())); } } if (line.getFinancialBeginningBalanceLineAmount() != null && line.getFinancialBeginningBalanceLineAmount() != KualiInteger.ZERO) { if (isRevenue) { bcDoc.setRevenueFinancialBeginningBalanceLineAmountTotal(bcDoc.getRevenueFinancialBeginningBalanceLineAmountTotal().subtract(line.getFinancialBeginningBalanceLineAmount())); } else { bcDoc.setExpenditureFinancialBeginningBalanceLineAmountTotal(bcDoc.getExpenditureFinancialBeginningBalanceLineAmountTotal().subtract(line.getFinancialBeginningBalanceLineAmount())); } } // remove the line if (isRevenue) { bcDoc.getPendingBudgetConstructionGeneralLedgerRevenueLines().remove(deleteIndex); } else { if (line.getFinancialObjectCode().equalsIgnoreCase(KFSConstants.BudgetConstructionConstants.OBJECT_CODE_2PLG)) { bcDoc.setContainsTwoPlug(false); } bcDoc.getPendingBudgetConstructionGeneralLedgerExpenditureLines().remove(deleteIndex); } } /* * public ActionForward returnFromMonthly(ActionMapping mapping, ActionForm form, HttpServletRequest request, * HttpServletResponse response) throws Exception { BudgetConstructionForm budgetConstructionForm = (BudgetConstructionForm) * form; String documentNumber = request.getParameter("documentNumber"); BudgetConstructionDocument budgetConstructionDocument = * (BudgetConstructionDocument) SpringContext.getBean(DocumentService.class).getByDocumentHeaderId(documentNumber); * budgetConstructionForm.setDocument(budgetConstructionDocument); KualiWorkflowDocument workflowDoc = * budgetConstructionDocument.getDocumentHeader().getWorkflowDocument(); * budgetConstructionForm.setDocTypeName(workflowDoc.getDocumentType()); // KualiDocumentFormBase.populate() needs this updated * in the session GlobalVariables.getUserSession().setWorkflowDocument(workflowDoc); return * mapping.findForward(KFSConstants.MAPPING_BASIC); } */ /** * @see org.kuali.rice.kns.web.struts.action.KualiDocumentActionBase#refresh(org.apache.struts.action.ActionMapping, * org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) */ @Override public ActionForward refresh(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { BudgetConstructionForm budgetConstructionForm = (BudgetConstructionForm) form; budgetConstructionForm.setDerivedValuesOnForm(request); // Do specific refresh stuff here based on refreshCaller parameter // typical refresh callers would be monthlyBudget or salarySetting or lookupable String refreshCaller = request.getParameter(KFSConstants.REFRESH_CALLER); if (refreshCaller != null && refreshCaller.endsWith(BCConstants.MONTHLY_BUDGET_REFRESH_CALLER)) { // monthly process applies any changes to the DB and the form session object // including any override to the request amount which also changes the request total // it also sets up calc monthly benefits if the line is involved in benefits BudgetDocumentService budgetDocumentService = SpringContext.getBean(BudgetDocumentService.class); budgetDocumentService.calculateBenefitsIfNeeded(budgetConstructionForm.getBudgetConstructionDocument()); } if (refreshCaller != null && refreshCaller.endsWith(BCConstants.QUICK_SALARY_SETTING_REFRESH_CALLER)) { BudgetDocumentService budgetDocumentService = SpringContext.getBean(BudgetDocumentService.class); // if editing - reload expenditure and check for changes to detail salary lines and 2plg request amount boolean diffFound = false; if (budgetConstructionForm.isEditAllowed() && !budgetConstructionForm.isSystemViewOnly()) { BudgetConstructionDocument currentBCDoc = budgetConstructionForm.getBudgetConstructionDocument(); // get the current set of salary setting related rows from DB and compare against preSalarySettingRows List<PendingBudgetConstructionGeneralLedger> dbSalarySettingRows = budgetDocumentService.getPBGLSalarySettingRows(currentBCDoc); for (PendingBudgetConstructionGeneralLedger dbSalarySettingRow : dbSalarySettingRows) { if (budgetConstructionForm.getPreSalarySettingRows().containsKey(dbSalarySettingRow.getFinancialObjectCode() + dbSalarySettingRow.getFinancialSubObjectCode())) { // update the existing row if a difference is found KualiInteger dbReqAmount = dbSalarySettingRow.getAccountLineAnnualBalanceAmount(); KualiInteger preReqAmount = budgetConstructionForm.getPreSalarySettingRows().get(dbSalarySettingRow.getFinancialObjectCode() + dbSalarySettingRow.getFinancialSubObjectCode()).getAccountLineAnnualBalanceAmount(); Long dbVersionNumber = dbSalarySettingRow.getVersionNumber(); Long preReqVersionNumber = budgetConstructionForm.getPreSalarySettingRows().get(dbSalarySettingRow.getFinancialObjectCode() + dbSalarySettingRow.getFinancialSubObjectCode()).getVersionNumber(); if ((dbVersionNumber.compareTo(preReqVersionNumber) != 0) || (dbReqAmount.compareTo(preReqAmount) != 0)) { budgetDocumentService.addOrUpdatePBGLRow(currentBCDoc, dbSalarySettingRow, Boolean.FALSE); // only flag for existing line diff when the request amount changes // changes in versionNumber implies offsetting updates of some sort if (dbReqAmount.compareTo(preReqAmount) != 0) { diffFound = true; } } } else { // update the req amount and version or add the new row to the current doc as needed // insert the new DB row to the set in memory budgetDocumentService.addOrUpdatePBGLRow(currentBCDoc, dbSalarySettingRow, Boolean.FALSE); diffFound = true; } } if (diffFound) { this.adjustForSalarySettingChanges(budgetConstructionForm); } } } if (refreshCaller != null && refreshCaller.endsWith(KFSConstants.LOOKUPABLE_SUFFIX)) { this.checkAndFixReturnedLookupValues(budgetConstructionForm.getNewRevenueLine(), budgetConstructionForm.getBudgetConstructionDocument()); this.checkAndFixReturnedLookupValues(budgetConstructionForm.getNewExpenditureLine(), budgetConstructionForm.getBudgetConstructionDocument()); final List REFRESH_FIELDS = Collections.unmodifiableList(Arrays.asList(new String[] { "financialObject", "financialSubObject" })); SpringContext.getBean(PersistenceService.class).retrieveReferenceObjects(budgetConstructionForm.getNewRevenueLine(), REFRESH_FIELDS); SpringContext.getBean(PersistenceService.class).retrieveReferenceObjects(budgetConstructionForm.getNewExpenditureLine(), REFRESH_FIELDS); } // balance inquiry anchor is set before doing a balance inquiry if (budgetConstructionForm.getBalanceInquiryReturnAnchor() != null) { budgetConstructionForm.setAnchor(budgetConstructionForm.getBalanceInquiryReturnAnchor()); budgetConstructionForm.setBalanceInquiryReturnAnchor(null); } return mapping.findForward(KFSConstants.MAPPING_BASIC); } /** * checks the passed in newLine for any data consistency problems compared to the currently loaded document * this is typically called after return from a lookup since the user can potentially select from other fiscal years, etc. * * @param newLine * @param bcDoc */ protected void checkAndFixReturnedLookupValues(PendingBudgetConstructionGeneralLedger newLine, BudgetConstructionDocument bcDoc){ if (!newLine.getUniversityFiscalYear().equals(bcDoc.getUniversityFiscalYear())){ newLine.setUniversityFiscalYear(bcDoc.getUniversityFiscalYear()); } if (!newLine.getChartOfAccountsCode().equals(bcDoc.getChartOfAccountsCode())){ newLine.setChartOfAccountsCode(bcDoc.getChartOfAccountsCode()); } if (!newLine.getAccountNumber().equals(bcDoc.getAccountNumber())){ newLine.setAccountNumber(bcDoc.getAccountNumber()); } } /** * 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 * all of the descriptions and labels for each of the pbgl line values. * * @param mapping * @param form * @param request * @param response * @return ActionForward * @throws Exception */ public ActionForward showDetails(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { BudgetConstructionForm tForm = (BudgetConstructionForm) form; tForm.setHideDetails(false); return mapping.findForward(KFSConstants.MAPPING_BASIC); } /** * This action toggles the value of the hide field in the user interface to "hide" so that when the page is rendered, the UI * displays values without all of the descriptions and labels for each of the pbgl lines. * * @param mapping * @param form * @param request * @param response * @return ActionForward * @throws Exception */ public ActionForward hideDetails(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { BudgetConstructionForm tForm = (BudgetConstructionForm) form; tForm.setHideDetails(true); return mapping.findForward(KFSConstants.MAPPING_BASIC); } public ActionForward toggleAdjustmentMeasurement(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { BudgetConstructionForm docForm = (BudgetConstructionForm) form; boolean currentStatus = docForm.isHideAdjustmentMeasurement(); docForm.setHideAdjustmentMeasurement(!currentStatus); return mapping.findForward(KFSConstants.MAPPING_BASIC); } public ActionForward adjustRevenueLinePercent(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { BudgetConstructionForm docForm = (BudgetConstructionForm) form; BudgetConstructionDocument bcDoc = docForm.getBudgetConstructionDocument(); PendingBudgetConstructionGeneralLedger revLine = bcDoc.getPendingBudgetConstructionGeneralLedgerRevenueLines().get(this.getSelectedLine(request)); if (revLine.getAdjustmentAmount() != null) { this.adjustRequest(revLine); docForm.populatePBGLLines(); } return mapping.findForward(KFSConstants.MAPPING_BASIC); } public ActionForward adjustExpenditureLinePercent(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { BudgetConstructionForm docForm = (BudgetConstructionForm) form; BudgetConstructionDocument bcDoc = docForm.getBudgetConstructionDocument(); PendingBudgetConstructionGeneralLedger expLine = bcDoc.getPendingBudgetConstructionGeneralLedgerExpenditureLines().get(this.getSelectedLine(request)); if (expLine.getAdjustmentAmount() != null) { this.adjustRequest(expLine); docForm.populatePBGLLines(); } return mapping.findForward(KFSConstants.MAPPING_BASIC); } public ActionForward adjustAllRevenueLinesPercent(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { BudgetConstructionForm docForm = (BudgetConstructionForm) form; BudgetConstructionDocument bcDoc = docForm.getBudgetConstructionDocument(); List<PendingBudgetConstructionGeneralLedger> revenueLines = bcDoc.getPendingBudgetConstructionGeneralLedgerRevenueLines(); KualiDecimal adjustmentAmount = docForm.getRevenueAdjustmentAmount(); if (adjustmentAmount != null) { // not sure we need this check since the tool isn't displayed in view mode boolean isEditable = docForm.isEditAllowed() && !docForm.isSystemViewOnly(); for (PendingBudgetConstructionGeneralLedger revenueLine : revenueLines) { if (isEditable) { revenueLine.setAdjustmentAmount(adjustmentAmount); this.adjustRequest(revenueLine); } } if (isEditable){ docForm.populatePBGLLines(); } } return mapping.findForward(KFSConstants.MAPPING_BASIC); } public ActionForward adjustAllExpenditureLinesPercent(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { BudgetConstructionForm docForm = (BudgetConstructionForm) form; BudgetConstructionDocument bcDoc = docForm.getBudgetConstructionDocument(); List<PendingBudgetConstructionGeneralLedger> expenditureLines = bcDoc.getPendingBudgetConstructionGeneralLedgerExpenditureLines(); KualiDecimal adjustmentAmount = docForm.getExpenditureAdjustmentAmount(); if (adjustmentAmount != null) { // not sure we need this check since the tool isn't displayed in view mode boolean isEditable = docForm.isEditAllowed() && !docForm.isSystemViewOnly(); // (!benecalcDisabled && !empty item.laborObject && item.laborObject.financialObjectFringeOrSalaryCode == 'F') for (PendingBudgetConstructionGeneralLedger expenditureLine : expenditureLines) { boolean isLineEditable = (isEditable && (docForm.isBenefitsCalculationDisabled() || (expenditureLine.getLaborObject() == null) || !expenditureLine.getLaborObject().getFinancialObjectFringeOrSalaryCode().equalsIgnoreCase(BCConstants.LABOR_OBJECT_FRINGE_CODE))); if (isLineEditable) { expenditureLine.setAdjustmentAmount(adjustmentAmount); this.adjustRequest(expenditureLine); } } if (isEditable){ docForm.populatePBGLLines(); } } return mapping.findForward(KFSConstants.MAPPING_BASIC); } /** * Handles the document (account) pullup action, resetting the cached editingMode as appropriate for the new level. * * @param mapping * @param form * @param request * @param response * @return * @throws Exception */ public ActionForward performAccountPullup(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { BudgetConstructionForm tForm = (BudgetConstructionForm) form; boolean doAllowPullup = false; boolean lockNeeded = false; boolean prePullReadOnlyAccess; BudgetDocumentService budgetDocumentService = SpringContext.getBean(BudgetDocumentService.class); // if system view only or view only - reload to get the latest status // and check that the document is still below the selected POV if (!tForm.isEditAllowed() || tForm.isSystemViewOnly()) { prePullReadOnlyAccess = true; // now reload the document and get latest status info loadDocument(tForm); this.initAuthorization(tForm); if (tForm.getBudgetConstructionDocument().getOrganizationLevelCode() < Integer.parseInt(tForm.getPullupKeyCode())) { doAllowPullup = true; // if not system view only mode, we are in document view mode - we'll need a lock before the pullup // since by definition pullup puts the account at a full_entry level // and we need exclusive access to perform the pullup if (!tForm.isSystemViewOnly()) { lockNeeded = true; } } else { // document has been moved and is either at the desired level or above doAllowPullup = false; lockNeeded = false; // document has been moved above the desired level - let populate through an authorization exception if (tForm.getBudgetConstructionDocument().getOrganizationLevelCode() > Integer.parseInt(tForm.getPullupKeyCode())) { GlobalVariables.getMessageMap().putError(KFSConstants.GLOBAL_MESSAGES, BCKeyConstants.ERROR_BUDGET_PULLUP_DOCUMENT, "Document has already been moved above the selected level."); } } } else { // we are in document full_entry // assume we already have a lock, allow the pullup // and we need to ensure the user can finish editing work if after pullup system goes to system view only prePullReadOnlyAccess = false; doAllowPullup = true; } if (lockNeeded || doAllowPullup) { // get a fresh header to use for lock and/or pullup HashMap primaryKey = new HashMap(); primaryKey.put(KFSPropertyConstants.DOCUMENT_NUMBER, tForm.getDocument().getDocumentNumber()); BudgetConstructionHeader budgetConstructionHeader = SpringContext.getBean(BusinessObjectService.class).findByPrimaryKey(BudgetConstructionHeader.class, primaryKey); if (budgetConstructionHeader == null) { GlobalVariables.getMessageMap().putError(BCConstants.BUDGET_CONSTRUCTION_SYSTEM_INFORMATION_TAB_ERRORS, BCKeyConstants.ERROR_BUDGET_PULLUP_DOCUMENT, "Fatal, Document not found."); return mapping.findForward(KFSConstants.MAPPING_BASIC); } if (lockNeeded) { // only successful lock allows pullup here doAllowPullup = false; LockService lockService = SpringContext.getBean(LockService.class); BudgetConstructionLockStatus bcLockStatus = lockService.lockAccount(budgetConstructionHeader, GlobalVariables.getUserSession().getPerson().getPrincipalId()); LockStatus lockStatus = bcLockStatus.getLockStatus(); switch (lockStatus) { case SUCCESS: doAllowPullup = true; break; case BY_OTHER: Principal principal = KimApiServiceLocator.getIdentityService().getPrincipal(bcLockStatus.getAccountLockOwner()); String lockerName = principal.getPrincipalName(); GlobalVariables.getMessageMap().putError(BCConstants.BUDGET_CONSTRUCTION_SYSTEM_INFORMATION_TAB_ERRORS, BCKeyConstants.ERROR_BUDGET_PULLUP_DOCUMENT, "Locked by " + lockerName); break; case FLOCK_FOUND: GlobalVariables.getMessageMap().putError(BCConstants.BUDGET_CONSTRUCTION_SYSTEM_INFORMATION_TAB_ERRORS, BCKeyConstants.ERROR_BUDGET_PULLUP_DOCUMENT, "Funding lock found."); break; default: GlobalVariables.getMessageMap().putError(BCConstants.BUDGET_CONSTRUCTION_SYSTEM_INFORMATION_TAB_ERRORS, BCKeyConstants.ERROR_BUDGET_PULLUP_DOCUMENT, "Optimistic lock or other failure during lock attempt."); break; } } // attempt pullup if (doAllowPullup) { budgetConstructionHeader.setOrganizationLevelCode(Integer.parseInt(tForm.getPullupKeyCode())); budgetConstructionHeader.setOrganizationLevelChartOfAccountsCode(tForm.getAccountOrgHierLevels().get(Integer.parseInt(tForm.getPullupKeyCode())).getOrganizationChartOfAccountsCode()); budgetConstructionHeader.setOrganizationLevelOrganizationCode(tForm.getAccountOrgHierLevels().get(Integer.parseInt(tForm.getPullupKeyCode())).getOrganizationCode()); SpringContext.getBean(BusinessObjectService.class).save(budgetConstructionHeader); // finally refresh the doc with the changed header info tForm.getBudgetConstructionDocument().setVersionNumber(budgetConstructionHeader.getVersionNumber()); tForm.getBudgetConstructionDocument().setOrganizationLevelCode(budgetConstructionHeader.getOrganizationLevelCode()); tForm.getBudgetConstructionDocument().setOrganizationLevelChartOfAccountsCode(budgetConstructionHeader.getOrganizationLevelChartOfAccountsCode()); tForm.getBudgetConstructionDocument().setOrganizationLevelOrganizationCode(budgetConstructionHeader.getOrganizationLevelOrganizationCode()); // refresh the lock info even though the user may be pulling while in edit mode tForm.getBudgetConstructionDocument().setBudgetLockUserIdentifier(budgetConstructionHeader.getBudgetLockUserIdentifier()); // refresh organization - so UI shows new level description tForm.getBudgetConstructionDocument().refreshReferenceObject("organizationLevelOrganization"); this.initAuthorization(tForm); // if before pullup, system was 'not system view only' goes to 'system view only' after pull // need to manually remove the system view only editingMode here to allow the user to save work since still // full_entry if (tForm.isEditAllowed() && !prePullReadOnlyAccess) { if (tForm.isSystemViewOnly()) { tForm.getEditingMode().remove(BCConstants.EditModes.SYSTEM_VIEW_ONLY); } } } } return mapping.findForward(KFSConstants.MAPPING_BASIC); } /** * Handles the document (account) pushdown action, resetting the cached editingMode as appropriate for the new level. * * @param mapping * @param form * @param request * @param response * @return * @throws Exception */ public ActionForward performAccountPushdown(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { boolean doAllowPushdown = false; boolean unlockNeeded = false; boolean prePushSystemViewOnly; BudgetConstructionForm tForm = (BudgetConstructionForm) form; BudgetConstructionDocument bcDocument = tForm.getBudgetConstructionDocument(); BudgetDocumentService budgetDocumentService = SpringContext.getBean(BudgetDocumentService.class); // This method is called only if user has edit access and there is somewhere to push to. // If not system view only and the intended push level is view, we need to validate and save // Otherwise new level is still allowing editing, just push and keep current lock if (!tForm.isSystemViewOnly()) { prePushSystemViewOnly = false; // check editing mode at the intended level if (!hasEditPermission(bcDocument, tForm.getPushdownKeyCode(), GlobalVariables.getUserSession().getPerson())) { budgetDocumentService.saveDocumentNoWorkflow(bcDocument); tForm.initializePersistedRequestAmounts(); budgetDocumentService.calculateBenefitsIfNeeded(bcDocument); // repop and refresh refs - esp monthly so jsp can properly display state // tForm.populatePBGLLines(); unlockNeeded = true; } doAllowPushdown = true; } else { prePushSystemViewOnly = true; // reload document to get most up-to-date status and recheck that we still have FULL_ENTRY access // anything else means the document was moved by someone else and we may no longer even have read access loadDocument(tForm); this.initAuthorization(tForm); if (tForm.isEditAllowed()) { doAllowPushdown = true; } else { // document has moved GlobalVariables.getMessageMap().putError(KFSConstants.GLOBAL_MESSAGES, BCKeyConstants.ERROR_BUDGET_PUSHDOWN_DOCUMENT, "Full Access Control Lost."); } } // gets here if editing and pushing to view and doc is valid and persisted // or we are pushing from edit to edit // or we are in system view only if (doAllowPushdown) { HashMap primaryKey = new HashMap(); primaryKey.put(KFSPropertyConstants.DOCUMENT_NUMBER, tForm.getDocument().getDocumentNumber()); BudgetConstructionHeader budgetConstructionHeader = SpringContext.getBean(BusinessObjectService.class).findByPrimaryKey(BudgetConstructionHeader.class, primaryKey); if (budgetConstructionHeader != null) { budgetConstructionHeader.setOrganizationLevelCode(Integer.parseInt(tForm.getPushdownKeyCode())); if (Integer.parseInt(tForm.getPushdownKeyCode()) == 0) { budgetConstructionHeader.setOrganizationLevelChartOfAccountsCode(null); budgetConstructionHeader.setOrganizationLevelOrganizationCode(null); } else { budgetConstructionHeader.setOrganizationLevelChartOfAccountsCode(tForm.getAccountOrgHierLevels().get(Integer.parseInt(tForm.getPushdownKeyCode())).getOrganizationChartOfAccountsCode()); budgetConstructionHeader.setOrganizationLevelOrganizationCode(tForm.getAccountOrgHierLevels().get(Integer.parseInt(tForm.getPushdownKeyCode())).getOrganizationCode()); } } // unlock if needed (which stores) - otherwise store the new level if (unlockNeeded) { LockService lockService = SpringContext.getBean(LockService.class); lockService.unlockAccount(budgetConstructionHeader); } else { SpringContext.getBean(BusinessObjectService.class).save(budgetConstructionHeader); } // finally refresh the doc with the changed header info tForm.getBudgetConstructionDocument().setVersionNumber(budgetConstructionHeader.getVersionNumber()); tForm.getBudgetConstructionDocument().setOrganizationLevelCode(budgetConstructionHeader.getOrganizationLevelCode()); tForm.getBudgetConstructionDocument().setOrganizationLevelChartOfAccountsCode(budgetConstructionHeader.getOrganizationLevelChartOfAccountsCode()); tForm.getBudgetConstructionDocument().setOrganizationLevelOrganizationCode(budgetConstructionHeader.getOrganizationLevelOrganizationCode()); tForm.getBudgetConstructionDocument().setBudgetLockUserIdentifier(budgetConstructionHeader.getBudgetLockUserIdentifier()); // refresh organization - so UI shows new level description tForm.getBudgetConstructionDocument().refreshReferenceObject("organizationLevelOrganization"); this.initAuthorization(tForm); // if before push, system is 'not system view only' goes to 'system view only' after push // need to manually remove the system view only editingMode here to allow the user to save work if still full_entry if (tForm.isEditAllowed()) { if (tForm.isSystemViewOnly() && !prePushSystemViewOnly) { tForm.getEditingMode().remove(BCConstants.EditModes.SYSTEM_VIEW_ONLY); } } } return mapping.findForward(KFSConstants.MAPPING_BASIC); } /** * Checks whether the current user would have access for the given budget document for the given organization level code * * @param document current bc document * @param orgLevelCode organization level code for access check * @param user user to check access for * @return true if user would have edit permission, false otherwise */ protected boolean hasEditPermission(BudgetConstructionDocument document, String orgLevelCode, Person user) { TransactionalDocumentAuthorizer documentAuthorizer = (TransactionalDocumentAuthorizer) SpringContext.getBean(DocumentHelperService.class).getDocumentAuthorizer(document); Map<String,String> roleQualifiers = new HashMap<String,String>(); roleQualifiers.put(BCPropertyConstants.ORGANIZATION_LEVEL_CODE, orgLevelCode); List<BudgetConstructionAccountOrganizationHierarchy> accountOrganizationHierarchy = SpringContext.getBean(BudgetDocumentService.class).retrieveOrBuildAccountOrganizationHierarchy(document.getUniversityFiscalYear(), document.getChartOfAccountsCode(), document.getAccountNumber()); for (BudgetConstructionAccountOrganizationHierarchy accountOrganization : accountOrganizationHierarchy) { if (accountOrganization.getOrganizationLevelCode().intValue() == Integer.parseInt(orgLevelCode)) { roleQualifiers.put(BCPropertyConstants.ORGANIZATION_CHART_OF_ACCOUNTS_CODE, accountOrganization.getOrganizationChartOfAccountsCode()); roleQualifiers.put(KfsKimAttributes.ORGANIZATION_CODE, accountOrganization.getOrganizationCode()); } } return documentAuthorizer.isAuthorizedByTemplate(document, KRADConstants.KNS_NAMESPACE, KimConstants.PermissionTemplateNames.EDIT_DOCUMENT, user.getPrincipalId(), null, roleQualifiers); } public ActionForward performReportDump(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { BudgetConstructionForm tForm = (BudgetConstructionForm) form; BudgetConstructionDocument bcDocument = tForm.getBudgetConstructionDocument(); // when we return from the lookup, our next request's method to call is going to be refresh tForm.registerEditableProperty(KRADConstants.DISPATCH_REQUEST_PARAMETER); if (tForm.isEditAllowed() && !tForm.isSystemViewOnly()) { BudgetDocumentService budgetDocumentService = SpringContext.getBean(BudgetDocumentService.class); budgetDocumentService.saveDocumentNoWorkflow(bcDocument); budgetDocumentService.calculateBenefitsIfNeeded(bcDocument); tForm.initializePersistedRequestAmounts(); // repop and refresh refs - esp monthly so jsp can properly display state // tForm.populatePBGLLines(); } // gets here if rules passed and doc detail gets persisted and refreshed String basePath = SpringContext.getBean(ConfigurationService.class).getPropertyValueAsString(KFSConstants.APPLICATION_URL_KEY); Properties parameters = new Properties(); parameters.put(KFSConstants.DISPATCH_REQUEST_PARAMETER, BCConstants.MONTHLY_BUDGET_METHOD); parameters.put(KFSConstants.BACK_LOCATION, basePath + mapping.getPath() + ".do"); parameters.put("documentNumber", tForm.getDocument().getDocumentNumber()); parameters.put("universityFiscalYear", tForm.getUniversityFiscalYear().toString()); parameters.put("chartOfAccountsCode", tForm.getChartOfAccountsCode()); parameters.put("accountNumber", tForm.getAccountNumber()); parameters.put("subAccountNumber", tForm.getSubAccountNumber()); parameters.put(BCPropertyConstants.MAIN_WINDOW, (tForm.isMainWindow() ? "true" : "false")); // anchor, if it exists if (form instanceof KualiForm && StringUtils.isNotEmpty(((KualiForm) form).getAnchor())) { parameters.put(BCConstants.RETURN_ANCHOR, ((KualiForm) form).getAnchor()); } // the form object is retrieved and removed upon return by KualiRequestProcessor.processActionForm() parameters.put(BCConstants.RETURN_FORM_KEY, GlobalVariables.getUserSession().addObjectWithGeneratedKey(form, BCConstants.FORMKEY_PREFIX)); String lookupUrl = UrlFactory.parameterizeUrl(basePath + "/" + BCConstants.REPORT_RUNNER_ACTION, parameters); this.setupDocumentExit(); return new ActionForward(lookupUrl, true); } public ActionForward performPercentChange(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { BudgetConstructionForm tForm = (BudgetConstructionForm) form; GlobalVariables.getMessageMap().putError(KFSConstants.GLOBAL_MESSAGES, KFSKeyConstants.ERROR_UNIMPLEMENTED, "Percent Change"); return mapping.findForward(KFSConstants.MAPPING_BASIC); } public ActionForward performRevMonthSpread(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { return this.performMonthSpread(mapping, form, request, response, true); } public ActionForward performExpMonthSpread(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { return this.performMonthSpread(mapping, form, request, response, false); } public ActionForward performMonthSpread(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response, boolean isRevenue) throws Exception { // no check for full_entry and system edit mode since this control is not displayed for this case // need to validate, save and calc benefits first // this is different than client/server model - need to always keep DB consistent BudgetConstructionForm tForm = (BudgetConstructionForm) form; BudgetConstructionDocument bcDocument = tForm.getBudgetConstructionDocument(); BudgetDocumentService budgetDocumentService = SpringContext.getBean(BudgetDocumentService.class); // validate and save without checking monthly RI since the spread will keep things consistent if (isRevenue) { budgetDocumentService.saveDocumentNoWorkFlow(bcDocument, MonthSpreadDeleteType.REVENUE, false); } else { budgetDocumentService.saveDocumentNoWorkFlow(bcDocument, MonthSpreadDeleteType.EXPENDITURE, false); } tForm.initializePersistedRequestAmounts(); // budgetDocumentService.calculateBenefitsIfNeeded(bcDocument); BudgetConstructionMonthlyBudgetsCreateDeleteService monthlyBudgetService = SpringContext.getBean(BudgetConstructionMonthlyBudgetsCreateDeleteService.class); if (isRevenue) { monthlyBudgetService.spreadBudgetConstructionMonthlyBudgetsRevenue(bcDocument.getDocumentNumber(), bcDocument.getUniversityFiscalYear(), bcDocument.getChartOfAccountsCode(), bcDocument.getAccountNumber(), bcDocument.getSubAccountNumber()); } else { // service returns true if benefit eligible monthly lines exist if (monthlyBudgetService.spreadBudgetConstructionMonthlyBudgetsExpenditure(bcDocument.getDocumentNumber(), bcDocument.getUniversityFiscalYear(), bcDocument.getChartOfAccountsCode(), bcDocument.getAccountNumber(), bcDocument.getSubAccountNumber())) { bcDocument.setMonthlyBenefitsCalcNeeded(true); // budgetDocumentService.calculateBenefitsIfNeeded(bcDocument); } } budgetDocumentService.calculateBenefitsIfNeeded(bcDocument); // repop and refresh refs - esp monthly so jsp can properly display state tForm.populatePBGLLines(); return mapping.findForward(KFSConstants.MAPPING_BASIC); } public ActionForward performRevMonthDelete(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { return this.performMonthDelete(mapping, form, request, response, true); } public ActionForward performExpMonthDelete(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { return this.performMonthDelete(mapping, form, request, response, false); } public ActionForward performMonthDelete(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response, boolean isRevenue) throws Exception { // no check for full_entry and system edit mode since this control is not displayed for this case // need to validate, save and calc benefits first // this is different than client/server model - need to always keep DB consistent BudgetConstructionForm tForm = (BudgetConstructionForm) form; BudgetConstructionDocument bcDocument = tForm.getBudgetConstructionDocument(); BudgetDocumentService budgetDocumentService = SpringContext.getBean(BudgetDocumentService.class); // validate and save without checking monthly RI since the delete will make RI check moot if (isRevenue) { budgetDocumentService.saveDocumentNoWorkFlow(bcDocument, MonthSpreadDeleteType.REVENUE, false); } else { budgetDocumentService.saveDocumentNoWorkFlow(bcDocument, MonthSpreadDeleteType.EXPENDITURE, false); } tForm.initializePersistedRequestAmounts(); // budgetDocumentService.calculateBenefitsIfNeeded(bcDocument); BudgetConstructionMonthlyBudgetsCreateDeleteService monthlyBudgetService = SpringContext.getBean(BudgetConstructionMonthlyBudgetsCreateDeleteService.class); if (isRevenue) { monthlyBudgetService.deleteBudgetConstructionMonthlyBudgetsRevenue(bcDocument.getDocumentNumber(), bcDocument.getUniversityFiscalYear(), bcDocument.getChartOfAccountsCode(), bcDocument.getAccountNumber(), bcDocument.getSubAccountNumber()); } else { monthlyBudgetService.deleteBudgetConstructionMonthlyBudgetsExpenditure(bcDocument.getDocumentNumber(), bcDocument.getUniversityFiscalYear(), bcDocument.getChartOfAccountsCode(), bcDocument.getAccountNumber(), bcDocument.getSubAccountNumber()); } budgetDocumentService.calculateBenefitsIfNeeded(bcDocument); // repop and refresh refs - esp monthly so jsp can properly display state tForm.populatePBGLLines(); return mapping.findForward(KFSConstants.MAPPING_BASIC); } public ActionForward performCalculateBenefits(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { // no check for full_entry and system edit mode since this control is not displayed for this case BudgetConstructionForm tForm = (BudgetConstructionForm) form; BudgetConstructionDocument bcDocument = (BudgetConstructionDocument) tForm.getDocument(); // allow benecalc if account is not salary only and benefits calc not disabled if (!tForm.isBenefitsCalculationDisabled() && !bcDocument.isSalarySettingOnly()) { BudgetDocumentService budgetDocumentService = SpringContext.getBean(BudgetDocumentService.class); budgetDocumentService.saveDocumentNoWorkflow(bcDocument); tForm.initializePersistedRequestAmounts(); budgetDocumentService.calculateBenefits(bcDocument); // repop and refresh refs - esp monthly so jsp can properly display state // tForm.populatePBGLLines(); } return mapping.findForward(KFSConstants.MAPPING_BASIC); } protected void adjustRequest(PendingBudgetConstructionGeneralLedger pbglLine) { KualiInteger baseAmount = pbglLine.getFinancialBeginningBalanceLineAmount(); if (baseAmount.isNonZero()) { KualiDecimal percent = pbglLine.getAdjustmentAmount(); BigDecimal adjustedAmount = baseAmount.multiply(percent).divide(KFSConstants.ONE_HUNDRED); KualiInteger requestAmount = new KualiInteger(adjustedAmount).add(baseAmount); pbglLine.setAccountLineAnnualBalanceAmount(requestAmount); } } }