/*
* 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.purap.document.web.struts;
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.purap.PurapConstants;
import org.kuali.kfs.module.purap.PurapConstants.AccountsPayableDocumentStrings;
import org.kuali.kfs.module.purap.PurapConstants.CMDocumentsStrings;
import org.kuali.kfs.module.purap.PurapConstants.PODocumentsStrings;
import org.kuali.kfs.module.purap.PurapConstants.PaymentRequestStatuses;
import org.kuali.kfs.module.purap.PurapConstants.PurchaseOrderDocTypes;
import org.kuali.kfs.module.purap.PurapConstants.PurchaseOrderStatuses;
import org.kuali.kfs.module.purap.PurapKeyConstants;
import org.kuali.kfs.module.purap.PurapPropertyConstants;
import org.kuali.kfs.module.purap.SingleConfirmationQuestion;
import org.kuali.kfs.module.purap.businessobject.PurApAccountingLine;
import org.kuali.kfs.module.purap.businessobject.PurApItem;
import org.kuali.kfs.module.purap.document.AccountsPayableDocument;
import org.kuali.kfs.module.purap.document.AccountsPayableDocumentBase;
import org.kuali.kfs.module.purap.document.PaymentRequestDocument;
import org.kuali.kfs.module.purap.document.PurchaseOrderDocument;
import org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument;
import org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocumentBase;
import org.kuali.kfs.module.purap.document.service.AccountsPayableService;
import org.kuali.kfs.module.purap.document.service.LogicContainer;
import org.kuali.kfs.module.purap.document.service.PaymentRequestService;
import org.kuali.kfs.module.purap.document.service.PurapService;
import org.kuali.kfs.module.purap.document.service.PurchaseOrderService;
import org.kuali.kfs.module.purap.document.validation.event.AttributedCalculateAccountsPayableEvent;
import org.kuali.kfs.module.purap.document.validation.event.AttributedPreCalculateAccountsPayableEvent;
import org.kuali.kfs.module.purap.service.PurapAccountingService;
import org.kuali.kfs.module.purap.util.PurQuestionCallback;
import org.kuali.kfs.sys.KFSConstants;
import org.kuali.kfs.sys.KFSPropertyConstants;
import org.kuali.kfs.sys.context.SpringContext;
import org.kuali.kfs.vnd.VendorConstants;
import org.kuali.kfs.vnd.businessobject.VendorAddress;
import org.kuali.rice.core.api.config.property.ConfigurationService;
import org.kuali.rice.core.api.util.RiceKeyConstants;
import org.kuali.rice.core.api.util.type.KualiDecimal;
import org.kuali.rice.kew.api.exception.WorkflowException;
import org.kuali.rice.kns.question.ConfirmationQuestion;
import org.kuali.rice.kns.service.DataDictionaryService;
import org.kuali.rice.kns.util.KNSGlobalVariables;
import org.kuali.rice.kns.util.MessageList;
import org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase;
import org.kuali.rice.krad.bo.Note;
import org.kuali.rice.krad.exception.ValidationException;
import org.kuali.rice.krad.service.BusinessObjectService;
import org.kuali.rice.krad.service.KualiRuleService;
import org.kuali.rice.krad.service.NoteService;
import org.kuali.rice.krad.util.GlobalVariables;
import org.kuali.rice.krad.util.ObjectUtils;
import org.kuali.rice.krad.util.UrlFactory;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.TreeMap;
/**
* Struts Action for Accounts Payable documents.
*/
public class AccountsPayableActionBase extends PurchasingAccountsPayableActionBase {
protected static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(AccountsPayableActionBase.class);
@Override
public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
PurchasingAccountsPayableFormBase baseForm = (PurchasingAccountsPayableFormBase) form;
ActionForward fwd = super.execute(mapping, form, request, response);
AccountsPayableDocumentBase document = (AccountsPayableDocumentBase) baseForm.getDocument();
boolean foundAccountExpiredWarning = false;
for(int i=0;i<KNSGlobalVariables.getMessageList().size();i++){
if (StringUtils.equals(KNSGlobalVariables.getMessageList().get(i).getErrorKey(),PurapKeyConstants.MESSAGE_CLOSED_OR_EXPIRED_ACCOUNTS_REPLACED)){
foundAccountExpiredWarning = true;
}
}
if (!foundAccountExpiredWarning){
SpringContext.getBean(AccountsPayableService.class).generateExpiredOrClosedAccountWarning(document);
}
return fwd;
}
/**
* Performs refresh of objects after a lookup.
*
* @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 {
PurchasingAccountsPayableFormBase baseForm = (PurchasingAccountsPayableFormBase) form;
AccountsPayableDocumentBase document = (AccountsPayableDocumentBase) baseForm.getDocument();
if (StringUtils.equals(baseForm.getRefreshCaller(), VendorConstants.VENDOR_ADDRESS_LOOKUPABLE_IMPL)) {
if (StringUtils.isNotBlank(request.getParameter(KFSPropertyConstants.DOCUMENT + "." + PurapPropertyConstants.VENDOR_ADDRESS_ID))) {
Integer vendorAddressGeneratedId = document.getVendorAddressGeneratedIdentifier();
VendorAddress refreshVendorAddress = new VendorAddress();
refreshVendorAddress.setVendorAddressGeneratedIdentifier(vendorAddressGeneratedId);
refreshVendorAddress = (VendorAddress) SpringContext.getBean(BusinessObjectService.class).retrieve(refreshVendorAddress);
document.templateVendorAddress(refreshVendorAddress);
}
}
return super.refresh(mapping, form, request, response);
}
/**
* Checks the continuation account indicator and generates warnings if continuation accounts were used to replace original
* accounts on the document.
*
* @see org.kuali.rice.kns.web.struts.action.KualiDocumentActionBase#loadDocument(org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase)
*/
@Override
protected void loadDocument(KualiDocumentFormBase kualiDocumentFormBase) throws WorkflowException {
super.loadDocument(kualiDocumentFormBase);
AccountsPayableDocument document = (AccountsPayableDocument) kualiDocumentFormBase.getDocument();
SpringContext.getBean(AccountsPayableService.class).generateExpiredOrClosedAccountWarning(document);
SpringContext.getBean(AccountsPayableService.class).updateItemList(document);
((AccountsPayableFormBase) kualiDocumentFormBase).updateItemCounts();
}
/**
* Perform calculation on item line.
*
* @param mapping An ActionMapping
* @param form An ActionForm
* @param request The HttpServletRequest
* @param response The HttpServletResponse
* @return An ActionForward
*/
@Override
public ActionForward calculate(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
AccountsPayableFormBase apForm = (AccountsPayableFormBase) form;
AccountsPayableDocument apDoc = (AccountsPayableDocument) apForm.getDocument();
// //recalculate the amounts and percents on the accounting line.
SpringContext.getBean(PurapAccountingService.class).updateAccountAmounts(apDoc);
// call precalculate
if (SpringContext.getBean(KualiRuleService.class).applyRules(new AttributedPreCalculateAccountsPayableEvent(apDoc))) {
customCalculate(apDoc);
// set calculated flag according to document type and status
if (apForm instanceof PaymentRequestForm && apDoc.getApplicationDocumentStatus().equals(PaymentRequestStatuses.APPDOC_AWAITING_TAX_REVIEW)) {
// set calculated tax flag for tax area calculation
PaymentRequestForm preqForm = (PaymentRequestForm)apForm;
preqForm.setCalculatedTax(true);
}
else {
// set calculated flag for document calculation, whether or not the process calculation rule passes, since it only gives warning
apForm.setCalculated(true);
}
}
return super.calculate(mapping, form, request, response);
}
@Override
public ActionForward clearAllTaxes(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
AccountsPayableFormBase payableForm = (AccountsPayableFormBase) form;
AccountsPayableDocument apDoc = (AccountsPayableDocument) payableForm.getDocument();
SpringContext.getBean(PurapService.class).clearAllTaxes(apDoc);
return super.clearAllTaxes(mapping, form, request, response);
}
/**
* Checks if calculation is required. Currently it is required when it has not already been calculated and full document entry
* status has not already passed.
*
* @param apForm A Form, which must inherit from <code>AccountsPayableFormBase</code>
* @return true if calculation is required, false otherwise
*/
protected boolean requiresCaculate(AccountsPayableFormBase apForm) {
boolean requiresCalculate = true;
PurchasingAccountsPayableDocument purapDocument = (PurchasingAccountsPayableDocument) apForm.getDocument();
requiresCalculate = !apForm.isCalculated() && !SpringContext.getBean(PurapService.class).isFullDocumentEntryCompleted(purapDocument);
return requiresCalculate;
}
/**
* Returns the current action name.
*
* @return A String. Set to null!
*/
public String getActionName() {
return null;
}
/**
* @see org.kuali.rice.kns.web.struts.action.KualiDocumentActionBase#route(org.apache.struts.action.ActionMapping,
* org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
@Override
public ActionForward route(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
AccountsPayableFormBase apForm = (AccountsPayableFormBase) form;
// set the last update user id
AccountsPayableDocumentBase document = (AccountsPayableDocumentBase) apForm.getDocument();
document.setLastActionPerformedByPersonId(GlobalVariables.getUserSession().getPerson().getPrincipalId());
// if form is not yet calculated, return and prompt user to calculate
if (requiresCaculate(apForm)) {
GlobalVariables.getMessageMap().putError(KFSConstants.DOCUMENT_ERRORS, PurapKeyConstants.ERROR_APPROVE_REQUIRES_CALCULATE);
return mapping.findForward(KFSConstants.MAPPING_BASIC);
}
// recalculate
customCalculate((AccountsPayableDocument) apForm.getDocument());
// route
ActionForward forward = super.route(mapping, form, request, response);
// if successful, then redirect back to init
boolean successMessageFound = false;
MessageList messageList = KNSGlobalVariables.getMessageList();
for (int i = 0; i < messageList.size(); i++) {
if (StringUtils.equals(messageList.get(i).getErrorKey(), RiceKeyConstants.MESSAGE_ROUTE_SUCCESSFUL)) {
successMessageFound = true;
break;
}
}
if (successMessageFound) {
String basePath = SpringContext.getBean(ConfigurationService.class).getPropertyValueAsString(KFSConstants.APPLICATION_URL_KEY);
Properties parameters = new Properties();
parameters.put(KFSConstants.DISPATCH_REQUEST_PARAMETER, KFSConstants.DOC_HANDLER_METHOD);
parameters.put(KFSConstants.PARAMETER_COMMAND, "initiate");
parameters.put(KFSConstants.DOCUMENT_TYPE_NAME, apForm.getDocTypeName());
String lookupUrl = UrlFactory.parameterizeUrl(basePath + "/" + "purap" + this.getActionName() + ".do", parameters);
forward = new ActionForward(lookupUrl, true);
}
return forward;
}
/**
* @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 {
AccountsPayableFormBase apForm = (AccountsPayableFormBase) form;
if (!requiresCaculate(apForm)) {
return super.save(mapping, form, request, response);
}
GlobalVariables.getMessageMap().putError(KFSConstants.DOCUMENT_ERRORS, PurapKeyConstants.ERROR_SAVE_REQUIRES_CALCULATE);
return mapping.findForward(KFSConstants.MAPPING_BASIC);
}
/**
* A wrapper method which prompts for a reason to hold a payment request or credit memo.
*
* @param mapping An ActionMapping
* @param form An ActionForm
* @param request The HttpServletRequest
* @param response The HttpServletResponse
* @param questionType A String used to distinguish which question is being asked
* @param notePrefix A String explaining what action was taken, to be prepended to the note containing the reason, which gets
* written to the document
* @param operation A one-word String description of the action to be taken, to be substituted into the message. (Can be an
* empty String for some messages.)
* @param messageKey A key to the message which will appear on the question screen
* @param callback A PurQuestionCallback
* @return An ActionForward
* @throws Exception
*/
protected ActionForward askQuestionWithInput(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response, String questionType, String notePrefix, String operation, String messageKey, PurQuestionCallback callback) throws Exception {
TreeMap<String, PurQuestionCallback> questionsAndCallbacks = new TreeMap<String, PurQuestionCallback>();
questionsAndCallbacks.put(questionType, callback);
return askQuestionWithInput(mapping, form, request, response, questionType, notePrefix, operation, messageKey, questionsAndCallbacks, "", mapping.findForward(KFSConstants.MAPPING_BASIC));
}
/**
* Builds and asks questions which require text input by the user for a payment request or a credit memo.
*
* @param mapping An ActionMapping
* @param form An ActionForm
* @param request The HttpServletRequest
* @param response The HttpServletResponse
* @param questionType A String used to distinguish which question is being asked
* @param notePrefix A String explaining what action was taken, to be prepended to the note containing the reason, which gets
* written to the document
* @param operation A one-word String description of the action to be taken, to be substituted into the message. (Can be an
* empty String for some messages.)
* @param messageKey A (whole) key to the message which will appear on the question screen
* @param questionsAndCallbacks A TreeMap associating the type of question to be asked and the type of callback which should
* happen in that case
* @param messagePrefix The most general part of a key to a message text to be retrieved from ConfigurationService,
* Describes a collection of questions.
* @param redirect An ActionForward to return to if done with questions
* @return An ActionForward
* @throws Exception
*/
protected ActionForward askQuestionWithInput(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response, String questionType, String notePrefix, String operation, String messageKey, TreeMap<String, PurQuestionCallback> questionsAndCallbacks, String messagePrefix, ActionForward redirect) throws Exception {
KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form;
AccountsPayableDocumentBase apDocument = (AccountsPayableDocumentBase) kualiDocumentFormBase.getDocument();
String question = request.getParameter(KFSConstants.QUESTION_INST_ATTRIBUTE_NAME);
String reason = request.getParameter(KFSConstants.QUESTION_REASON_ATTRIBUTE_NAME);
String noteText = "";
ConfigurationService kualiConfiguration = SpringContext.getBean(ConfigurationService.class);
String firstQuestion = questionsAndCallbacks.firstKey();
PurQuestionCallback callback = null;
Iterator questions = questionsAndCallbacks.keySet().iterator();
String mapQuestion = null;
String key = null;
// Start in logic for confirming the close.
if (question == null) {
key = getQuestionProperty(messageKey, messagePrefix, kualiConfiguration, firstQuestion);
String message = StringUtils.replace(key, "{0}", operation);
// Ask question if not already asked.
return this.performQuestionWithInput(mapping, form, request, response, firstQuestion, message, KFSConstants.CONFIRMATION_QUESTION, questionType, "");
}
else {
// find callback for this question
while (questions.hasNext()) {
mapQuestion = (String) questions.next();
if (StringUtils.equals(mapQuestion, question)) {
callback = questionsAndCallbacks.get(mapQuestion);
break;
}
}
key = getQuestionProperty(messageKey, messagePrefix, kualiConfiguration, mapQuestion);
Object buttonClicked = request.getParameter(KFSConstants.QUESTION_CLICKED_BUTTON);
if (question.equals(mapQuestion) && buttonClicked.equals(ConfirmationQuestion.NO)) {
// If 'No' is the button clicked, just reload the doc
String nextQuestion = null;
// ask another question if more left
if (questions.hasNext()) {
nextQuestion = (String) questions.next();
key = getQuestionProperty(messageKey, messagePrefix, kualiConfiguration, nextQuestion);
return this.performQuestionWithInput(mapping, form, request, response, nextQuestion, key, KFSConstants.CONFIRMATION_QUESTION, questionType, "");
}
else {
return mapping.findForward(KFSConstants.MAPPING_BASIC);
}
}
// Have to check length on value entered.
String introNoteMessage = notePrefix + KFSConstants.BLANK_SPACE;
// Build out full message.
noteText = introNoteMessage + reason;
int noteTextLength = noteText.length();
// Get note text max length from DD.
int noteTextMaxLength = SpringContext.getBean(DataDictionaryService.class).getAttributeMaxLength(Note.class, KFSConstants.NOTE_TEXT_PROPERTY_NAME).intValue();
if (StringUtils.isBlank(reason) || (noteTextLength > noteTextMaxLength)) {
// Figure out exact number of characters that the user can enter.
int reasonLimit = noteTextMaxLength - noteTextLength;
if (reason == null) {
// Prevent a NPE by setting the reason to a blank string.
reason = "";
}
return this.performQuestionWithInputAgainBecauseOfErrors(mapping, form, request, response, mapQuestion, key, KFSConstants.CONFIRMATION_QUESTION, questionType, "", reason, PurapKeyConstants.ERROR_PAYMENT_REQUEST_REASON_REQUIRED, KFSConstants.QUESTION_REASON_ATTRIBUTE_NAME, new Integer(reasonLimit).toString());
}
}
// make callback
if (ObjectUtils.isNotNull(callback)) {
AccountsPayableDocument refreshedApDocument = callback.doPostQuestion(apDocument, noteText);
kualiDocumentFormBase.setDocument(refreshedApDocument);
}
String nextQuestion = null;
// ask another question if more left
if (questions.hasNext()) {
nextQuestion = (String) questions.next();
key = getQuestionProperty(messageKey, messagePrefix, kualiConfiguration, nextQuestion);
return this.performQuestionWithInput(mapping, form, request, response, nextQuestion, key, KFSConstants.CONFIRMATION_QUESTION, questionType, "");
}
return redirect;
}
/**
* Used to look up messages to be displayed, from the ConfigurationService, given either a whole key or two parts of a key
* that may be concatenated together.
*
* @param messageKey String. One of the message keys in PurapKeyConstants.
* @param messagePrefix String. A prefix to the question key, such as "ap.question." that, concatenated with the question,
* comprises the whole key of the message.
* @param kualiConfiguration An instance of ConfigurationService
* @param question String. The most specific part of the message key in PurapKeyConstants.
* @return The message to be displayed given the key
*/
protected String getQuestionProperty(String messageKey, String messagePrefix, ConfigurationService kualiConfiguration, String question) {
return kualiConfiguration.getPropertyValueAsString((StringUtils.isEmpty(messagePrefix)) ? messageKey : messagePrefix + question);
}
public ActionForward reopenPo(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
LOG.debug("Reopen PO started");
return askQuestionsAndPerformReopenPurchaseOrder(mapping, form, request, response);
}
/**
* @see org.kuali.rice.kns.web.struts.action.KualiDocumentActionBase#cancel(org.apache.struts.action.ActionMapping,
* org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
@Override
public ActionForward cancel(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
return askCancelQuestion(mapping, form, request, response);
}
/**
* Constructs and asks the question as to whether the user wants to cancel, for payment requests and credit memos.
*
* @param mapping An ActionMapping
* @param form An ActionForm
* @param request The HttpServletRequest
* @param response The HttpServletResponse
* @return An ActionForward
* @throws Exception
*/
protected ActionForward askCancelQuestion(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
PurchasingAccountsPayableFormBase apForm = (PurchasingAccountsPayableFormBase) form;
String operation = "Cancel ";
PurQuestionCallback callback = cancelCallbackMethod();
TreeMap<String, PurQuestionCallback> questionsAndCallbacks = new TreeMap<String, PurQuestionCallback>();
questionsAndCallbacks.put("cancelAP", callback);
return askQuestionWithInput(mapping, form, request, response, CMDocumentsStrings.CANCEL_CM_QUESTION, AccountsPayableDocumentStrings.CANCEL_NOTE_PREFIX, operation, PurapKeyConstants.CREDIT_MEMO_QUESTION_CANCEL_DOCUMENT, questionsAndCallbacks, PurapKeyConstants.AP_QUESTION_PREFIX, mapping.findForward(KFSConstants.MAPPING_PORTAL));
}
/**
* Returns a question callback for the Cancel Purchase Order action.
*
* @return A PurQuestionCallback with a post-question activity appropriate to the Cancel PO action
*/
protected PurQuestionCallback cancelPOActionCallbackMethod() {
return new PurQuestionCallback() {
public AccountsPayableDocument doPostQuestion(AccountsPayableDocument document, String noteText) throws Exception {
// base impl do nothing
return document;
}
};
}
/**
* Returns a question callback for the Cancel action.
*
* @return A PurQuestionCallback which does post-question tasks appropriate to Cancellation.
*/
protected PurQuestionCallback cancelCallbackMethod() {
return new PurQuestionCallback() {
public AccountsPayableDocument doPostQuestion(AccountsPayableDocument document, String noteText) throws Exception {
SpringContext.getBean(AccountsPayableService.class).cancelAccountsPayableDocumentByCheckingDocumentStatus(document, noteText);
return document;
}
};
}
protected ActionForward askQuestionsAndPerformReopenPurchaseOrder(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
LOG.debug("askQuestionsAndPerformDocumentAction started.");
KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form;
AccountsPayableDocumentBase apDoc = (AccountsPayableDocumentBase) kualiDocumentFormBase.getDocument();
Object question = request.getParameter(KFSConstants.QUESTION_INST_ATTRIBUTE_NAME);
String questionType = PODocumentsStrings.REOPEN_PO_QUESTION;
String confirmType = PODocumentsStrings.CONFIRM_REOPEN_QUESTION;
String messageType = PurapKeyConstants.PURCHASE_ORDER_MESSAGE_REOPEN_DOCUMENT;
String operation = "Reopen ";
try {
ConfigurationService kualiConfiguration = SpringContext.getBean(ConfigurationService.class);
// Start in logic for confirming the proposed operation.
if (ObjectUtils.isNull(question)) {
String key = kualiConfiguration.getPropertyValueAsString(PurapKeyConstants.PURCHASE_ORDER_QUESTION_DOCUMENT);
String message = StringUtils.replace(key, "{0}", operation);
return this.performQuestionWithoutInput(mapping, form, request, response, questionType, message, KFSConstants.CONFIRMATION_QUESTION, questionType, "");
}
else {
Object buttonClicked = request.getParameter(KFSConstants.QUESTION_CLICKED_BUTTON);
if (question.equals(questionType) && buttonClicked.equals(ConfirmationQuestion.NO)) {
// If 'No' is the button clicked, just reload the doc
return mapping.findForward(KFSConstants.MAPPING_BASIC);
}
else if (question.equals(confirmType) && buttonClicked.equals(SingleConfirmationQuestion.OK)) {
// This is the case when the user clicks on "OK" in the end; redirect to the preq doc
return mapping.findForward(KFSConstants.MAPPING_BASIC);
}
}
PurchaseOrderDocument po = apDoc.getPurchaseOrderDocument();
if (!po.isPendingActionIndicator() && PurapConstants.PurchaseOrderStatuses.APPDOC_CLOSED.equals(po.getApplicationDocumentStatus())) {
/*
* Below if-else code block calls PurchaseOrderService methods that will throw ValidationException objects if errors
* occur during any process in the attempt to perform its actions. Assume, if these return successfully, that the
* PurchaseOrderDocument object returned from each is the newly created document and that all actions in the method
* were run correctly. NOTE: IF BELOW IF-ELSE IS EDITED THE NEW METHODS CALLED MUST THROW ValidationException OBJECT
* IF AN ERROR IS ADDED TO THE GlobalVariables
*/
po = initiateReopenPurchaseOrder(po, kualiDocumentFormBase.getAnnotation());
if (!GlobalVariables.getMessageMap().hasNoErrors()) {
throw new ValidationException("errors occurred during new PO creation");
}
if (StringUtils.isNotEmpty(messageType)) {
KNSGlobalVariables.getMessageList().add(messageType);
}
return this.performQuestionWithoutInput(mapping, form, request, response, confirmType, kualiConfiguration.getPropertyValueAsString(messageType), PODocumentsStrings.SINGLE_CONFIRMATION_QUESTION, questionType, "");
}
else {
return this.performQuestionWithoutInput(mapping, form, request, response, confirmType, "Unable to reopen the PO at this time due to the incorrect PO status or a pending PO change document.", PODocumentsStrings.SINGLE_CONFIRMATION_QUESTION, questionType, "");
}
} catch (ValidationException ve) {
throw ve;
}
}
public PurchaseOrderDocument initiateReopenPurchaseOrder(PurchaseOrderDocument po, String annotation) {
try {
LogicContainer logicToRun = new LogicContainer() {
public Object runLogic(Object[] objects) throws Exception {
PurchaseOrderDocument po = (PurchaseOrderDocument) objects[0];
final NoteService noteService = SpringContext.getBean(NoteService.class);
Note cancelNote = new Note();
cancelNote.setNoteTypeCode(po.getNoteType().getCode());
cancelNote.setNoteText(SpringContext.getBean(ConfigurationService.class).getPropertyValueAsString(PurapKeyConstants.AP_REOPENS_PURCHASE_ORDER_NOTE));
cancelNote.setNotePostedTimestampToCurrent();
final Note poCancelNote = noteService.createNote(cancelNote, po.getNoteTarget(), GlobalVariables.getUserSession().getPerson().getPrincipalId());
po.addNote(poCancelNote);
noteService.save(poCancelNote);
return SpringContext.getBean(PurchaseOrderService.class).createAndRoutePotentialChangeDocument(po.getDocumentNumber(), PurchaseOrderDocTypes.PURCHASE_ORDER_REOPEN_DOCUMENT, (String) objects[1], null, PurchaseOrderStatuses.APPDOC_PENDING_REOPEN);
}
};
return (PurchaseOrderDocument) SpringContext.getBean(PurapService.class).performLogicWithFakedUserSession(KFSConstants.SYSTEM_USER, logicToRun, new Object[] { po, annotation });
}
catch (WorkflowException e) {
String errorMsg = "Workflow Exception caught: " + e.getLocalizedMessage();
LOG.error(errorMsg, e);
throw new RuntimeException(errorMsg, e);
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* gets the item from preq and restores the values from the original PO and then
* redistributes the amounts based on extended cost and quantity
*
* @param mapping
* @param form
* @param request
* @param response
* @return actionForward
* @throws Exception
*/
public ActionForward recalculateItemAccountsAmounts(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
AccountsPayableFormBase payableForm = (AccountsPayableFormBase) form;
AccountsPayableDocument apDoc = (AccountsPayableDocument) payableForm.getDocument();
PurapAccountingService purapAccountingService = SpringContext.getBean(PurapAccountingService.class);
String[] indexes = getSelectedItemNumber(request);
int itemIndex = Integer.parseInt(indexes[0]);
PurApItem item = apDoc.getItem((itemIndex));
//first reset the the corresponding po items accounts amounts to this item
restoreItemAccountsAmounts(apDoc, item);
item.refreshReferenceObject(PurapPropertyConstants.ITEM_TYPE);
final KualiDecimal itemExtendedPrice = (item.getExtendedPrice()==null)?KualiDecimal.ZERO:item.getExtendedPrice();
if (item.getItemType().isQuantityBasedGeneralLedgerIndicator() && item.getExtendedPrice()==null) {
KualiDecimal newExtendedPrice = item.calculateExtendedPrice();
item.setExtendedPrice(newExtendedPrice);
}
PaymentRequestDocument preqDoc = (PaymentRequestDocument) apDoc;
// set amounts on any empty
preqDoc.updateExtendedPriceOnItems();
// calculation just for the tax area, only at tax review stage
// by now, the general calculation shall have been done.
if (preqDoc.getApplicationDocumentStatus().equals(PaymentRequestStatuses.APPDOC_AWAITING_TAX_REVIEW)) {
SpringContext.getBean(PaymentRequestService.class).calculateTaxArea(preqDoc);
}
// notice we're ignoring whether the boolean, because these are just warnings they shouldn't halt anything
//Calculate Payment request before rules since the rule check totalAmount.
SpringContext.getBean(PaymentRequestService.class).calculatePaymentRequest(preqDoc, true);
SpringContext.getBean(KualiRuleService.class).applyRules(new AttributedCalculateAccountsPayableEvent(preqDoc));
PurchasingAccountsPayableDocumentBase document = (PurchasingAccountsPayableDocumentBase) apDoc;
String accountDistributionMethod = document.getAccountDistributionMethod();
if (PurapConstants.AccountDistributionMethodCodes.SEQUENTIAL_CODE.equalsIgnoreCase(accountDistributionMethod)) {
// update the accounts amounts for PREQ and distribution method = sequential
purapAccountingService.updatePreqItemAccountAmounts(item);
}
else {
List<PurApAccountingLine> sourceAccountingLines = item.getSourceAccountingLines();
for (PurApAccountingLine acctLine : sourceAccountingLines) {
acctLine.setAmount(KualiDecimal.ZERO);
}
purapAccountingService.updatePreqProportionalItemAccountAmounts(item);
}
return mapping.findForward(KFSConstants.MAPPING_BASIC);
}
/**
* gets the item from preq and restores the values from the original PO
*
* @param mapping
* @param form
* @param request
* @param response
* @return actionForward
* @throws Exception
*/
public ActionForward restoreItemAccountsAmounts(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
AccountsPayableFormBase payableForm = (AccountsPayableFormBase) form;
AccountsPayableDocument apDoc = (AccountsPayableDocument) payableForm.getDocument();
String[] indexes = getSelectedItemNumber(request);
int itemIndex = Integer.parseInt(indexes[0]);
PurApItem item = apDoc.getItem((itemIndex));
//first reset the the corresponding po items accounts amounts to this item
restoreItemAccountsAmounts(apDoc, item);
item.setItemQuantity(null);
item.setItemTaxAmount(null);
item.refreshReferenceObject(PurapPropertyConstants.ITEM_TYPE);
final KualiDecimal itemExtendedPrice = (item.getExtendedPrice()==null)?KualiDecimal.ZERO:item.getExtendedPrice();;
if (item.getItemType().isQuantityBasedGeneralLedgerIndicator()) {
KualiDecimal newExtendedPrice = item.calculateExtendedPrice();
item.setExtendedPrice(newExtendedPrice);
}
return mapping.findForward(KFSConstants.MAPPING_BASIC);
}
/**
* Will return an array of Strings containing 2 indexes, the first String is the item index and the second String is the account
* index. These are obtained by parsing the method to call parameter from the request, between the word ".line" and "." The
* indexes are separated by a semicolon (:)
*
* @param request The HttpServletRequest
* @return An array of Strings containing pairs of two indices, an item index
*/
protected String[] getSelectedItemNumber(HttpServletRequest request) {
String itemString = new String();
String parameterName = (String) request.getAttribute(KFSConstants.METHOD_TO_CALL_ATTRIBUTE);
if (StringUtils.isNotBlank(parameterName)) {
itemString = StringUtils.substringBetween(parameterName, ".line", ".");
}
String[] result = StringUtils.split(itemString, ":");
return result;
}
/**
* restores the preq preqItem' accounts amounts with po's item's account lines
* amounts.
*
* @param apDoc
* @param preqItem
*/
protected void restoreItemAccountsAmounts(AccountsPayableDocument apDoc, PurApItem preqItem) {
List<PurApItem> pOItems = apDoc.getPurchaseOrderDocument().getItems();
PurApItem pOItem = getPOItem(pOItems, preqItem.getItemLineNumber());
if (ObjectUtils.isNotNull(pOItem)) {
// preqItem.setItemUnitPrice(pOItem.getItemUnitPrice());
List <PurApAccountingLine> preqAccountingLines = preqItem.getSourceAccountingLines();
for (PurApAccountingLine lineAcct : preqAccountingLines) {
updateItemAccountLine(pOItem, lineAcct);
}
}
}
/**
* returns the matching po item based on matching item identifier and item line numbner
* from preq items.
*
* @param purchaseItems
* @param itemLineNumber
* @return pOItem
*/
protected PurApItem getPOItem(List<PurApItem> pOItems, Integer itemLineNumber) {
PurApItem pOItem = null;
for (PurApItem poItem : pOItems) {
if (poItem.getItemLineNumber().compareTo(itemLineNumber) == 0) {
//found the matching preqItem so return it...
return poItem;
}
}
return pOItem;
}
/**
* finds the line with matching sequence number, chart code, account number, financial
* object code and updates amount/percent on the account line.
*
* @param pOItem
* @param lineAcct
*/
protected void updateItemAccountLine(PurApItem pOItem, PurApAccountingLine lineAcct) {
List <PurApAccountingLine> pOAccountingLines = pOItem.getSourceAccountingLines();
for (PurApAccountingLine pOLineAcct : pOAccountingLines) {
if (lineAcct.getChartOfAccountsCode().equalsIgnoreCase(pOLineAcct.getChartOfAccountsCode()) &&
lineAcct.getAccountNumber().equalsIgnoreCase(pOLineAcct.getAccountNumber()) &&
lineAcct.getFinancialObjectCode().equalsIgnoreCase(pOLineAcct.getFinancialObjectCode())) {
lineAcct.setAmount(pOLineAcct.getAmount());
lineAcct.setAccountLinePercent(pOLineAcct.getAccountLinePercent());
}
}
}
}