/*
* 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 java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
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.CMDocumentsStrings;
import org.kuali.kfs.module.purap.PurapKeyConstants;
import org.kuali.kfs.module.purap.PurapParameterConstants;
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.PaymentRequestDocument;
import org.kuali.kfs.module.purap.document.PurchaseOrderDocument;
import org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument;
import org.kuali.kfs.module.purap.document.VendorCreditMemoDocument;
import org.kuali.kfs.module.purap.document.service.CreditMemoService;
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.AttributedContinuePurapEvent;
import org.kuali.kfs.module.purap.util.PurQuestionCallback;
import org.kuali.kfs.sys.KFSConstants;
import org.kuali.kfs.sys.context.SpringContext;
import org.kuali.rice.core.api.util.type.KualiDecimal;
import org.kuali.rice.coreservice.framework.parameter.ParameterService;
import org.kuali.rice.kew.api.KewApiConstants;
import org.kuali.rice.kew.api.exception.WorkflowException;
import org.kuali.rice.kim.api.KimConstants;
import org.kuali.rice.kns.document.authorization.DocumentAuthorizer;
import org.kuali.rice.kns.question.ConfirmationQuestion;
import org.kuali.rice.kns.service.DocumentHelperService;
import org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase;
import org.kuali.rice.krad.document.Document;
import org.kuali.rice.krad.service.BusinessObjectService;
import org.kuali.rice.krad.service.KualiRuleService;
import org.kuali.rice.krad.util.GlobalVariables;
import org.kuali.rice.krad.util.KRADConstants;
import org.kuali.rice.krad.util.ObjectUtils;
/**
* Struts Action for Credit Memo document.
*/
public class VendorCreditMemoAction extends AccountsPayableActionBase {
protected static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(VendorCreditMemoAction.class);
/**
* Do initialization for a new credit memo.
*
* @see org.kuali.rice.kns.web.struts.action.KualiDocumentActionBase#createDocument(org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase)
*/
@Override
protected void createDocument(KualiDocumentFormBase kualiDocumentFormBase) throws WorkflowException {
super.createDocument(kualiDocumentFormBase);
((VendorCreditMemoDocument) kualiDocumentFormBase.getDocument()).initiateDocument();
}
/**
* Handles continue request. This request comes from the initial screen which gives indicates whether the type is payment
* request, purchase order, or vendor. Based on that, the credit memo is initially populated and the remaining tabs shown.
*
* @param mapping An ActionMapping
* @param form An ActionForm
* @param request The HttpServletRequest
* @param response The HttpServletResponse
* @throws Exception
* @return An ActionForward
*/
public ActionForward continueCreditMemo(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
VendorCreditMemoForm cmForm = (VendorCreditMemoForm) form;
VendorCreditMemoDocument creditMemoDocument = (VendorCreditMemoDocument) cmForm.getDocument();
String defaultDistributionMethod = SpringContext.getBean(ParameterService.class).getParameterValueAsString(PurapConstants.PURAP_NAMESPACE, "Document", PurapParameterConstants.DISTRIBUTION_METHOD_FOR_ACCOUNTING_LINES);
String preqId = request.getParameter("document.paymentRequestIdentifier");
if (! StringUtils.isEmpty(preqId)) {
//get the po document and get the account distribution method code....
String distributionCode = getDistributionMethodFromPReq(preqId);
if (ObjectUtils.isNotNull(distributionCode)) {
defaultDistributionMethod = distributionCode;
}
} else {
String poId = request.getParameter("document.purchaseOrderIdentifier");
if (! StringUtils.isEmpty(poId)) {
//get the po document and get the account distribution method code....
String distributionCode = getDistributionMethodFromPO(poId);
if (ObjectUtils.isNotNull(distributionCode)) {
defaultDistributionMethod = distributionCode;
}
}
}
//set the account distribution method code on the document.
creditMemoDocument.setAccountDistributionMethod(defaultDistributionMethod);
boolean rulePassed = SpringContext.getBean(KualiRuleService.class).applyRules(new AttributedContinuePurapEvent(creditMemoDocument));
if (!rulePassed){
return mapping.findForward(KFSConstants.MAPPING_BASIC);
}
if (creditMemoDocument.isSourceDocumentPaymentRequest()) {
PaymentRequestDocument preq = SpringContext.getBean(PaymentRequestService.class).getPaymentRequestById(creditMemoDocument.getPaymentRequestIdentifier());
if (ObjectUtils.isNotNull(preq)) {
// TODO figure out a more straightforward way to do this. ailish put this in so the link id would be set and the perm check would work
creditMemoDocument.setAccountsPayablePurchasingDocumentLinkIdentifier(preq.getAccountsPayablePurchasingDocumentLinkIdentifier());
if (!SpringContext.getBean(DocumentHelperService.class).getDocumentAuthorizer(creditMemoDocument).isAuthorizedByTemplate(creditMemoDocument, KRADConstants.KNS_NAMESPACE, KimConstants.PermissionTemplateNames.OPEN_DOCUMENT, GlobalVariables.getUserSession().getPrincipalId())) {
throw buildAuthorizationException("initiate document", creditMemoDocument);
}
}
}
else if (creditMemoDocument.isSourceDocumentPurchaseOrder()) {
PurchaseOrderDocument po = SpringContext.getBean(PurchaseOrderService.class).getCurrentPurchaseOrder(creditMemoDocument.getPurchaseOrderIdentifier());
if (ObjectUtils.isNotNull(po)) {
// TODO figure out a more straightforward way to do this. ailish put this in so the link id would be set and the perm check would work
creditMemoDocument.setAccountsPayablePurchasingDocumentLinkIdentifier(po.getAccountsPayablePurchasingDocumentLinkIdentifier());
if (!SpringContext.getBean(DocumentHelperService.class).getDocumentAuthorizer(creditMemoDocument).isAuthorizedByTemplate(creditMemoDocument, KRADConstants.KNS_NAMESPACE, KimConstants.PermissionTemplateNames.OPEN_DOCUMENT, GlobalVariables.getUserSession().getPrincipalId())) {
throw buildAuthorizationException("initiate document", creditMemoDocument);
}
}
}
else {
//do nothing for credit memos against a vendor; no link to PO means no need to hide doc based on sensitive data
}
// preform duplicate check which will forward to a question prompt if one is found
ActionForward forward = performDuplicateCreditMemoCheck(mapping, form, request, response, creditMemoDocument);
if (forward != null) {
return forward;
}
// perform validation of init tab
SpringContext.getBean(CreditMemoService.class).populateAndSaveCreditMemo(creditMemoDocument);
// sort below the line (doesn't really need to be done on CM, but will help if we ever bring btl from other docs)
SpringContext.getBean(PurapService.class).sortBelowTheLine(creditMemoDocument);
// update the counts on the form
cmForm.updateItemCounts();
//if source is (PREQ or PO) and the PO status is CLOSED, automatically reopen the PO
PurchaseOrderDocument po = creditMemoDocument.getPurchaseOrderDocument();
if ((creditMemoDocument.isSourceDocumentPaymentRequest() || creditMemoDocument.isSourceDocumentPurchaseOrder()) && PurapConstants.PurchaseOrderStatuses.APPDOC_CLOSED.equals(po.getApplicationDocumentStatus())) {
initiateReopenPurchaseOrder(po, cmForm.getAnnotation());
}
// update the accounts amounts to zero. The recalculate will calculate the totals...
List<PurApItem> items = creditMemoDocument.getItems();
for (PurApItem item : items) {
for (PurApAccountingLine accountLine : item.getSourceAccountingLines()) {
accountLine.setAmount(KualiDecimal.ZERO);
}
}
return mapping.findForward(KFSConstants.MAPPING_BASIC);
}
/**
* using preqId from vendor credit memo initiation screen, the corresponding
* preq documents are collected and then the distribution menthod is retrieved from it..
*
* @param preqId
* @return distributionMethod
*/
protected String getDistributionMethodFromPReq(String preqId) {
String distributionMethod = "";
Map criteria = new HashMap();
criteria.put("purapDocumentIdentifier", preqId);
List<PaymentRequestDocument> preqDocuments = (List<PaymentRequestDocument>)SpringContext.getBean(BusinessObjectService.class).findMatching(PaymentRequestDocument.class, criteria);
for (PaymentRequestDocument preqDoc : preqDocuments) {
if (ObjectUtils.isNotNull(preqDoc.getAccountDistributionMethod())) {
distributionMethod = preqDoc.getAccountDistributionMethod();
return distributionMethod;
}
}
return distributionMethod;
}
/**
* using poId from vendor credit memo initiation screen, the corresponding
* po documents are collected and then the distribution menthod is retrieved from it..
*
* @param preqId
* @return distributionMethod
*/
protected String getDistributionMethodFromPO(String poId) {
String distributionMethod = "";
Map criteria = new HashMap();
criteria.put("purapDocumentIdentifier", poId);
List<PurchaseOrderDocument> poDocuments = (List<PurchaseOrderDocument>)SpringContext.getBean(BusinessObjectService.class).findMatching(PurchaseOrderDocument.class, criteria);
for (PurchaseOrderDocument poDoc : poDocuments) {
if (ObjectUtils.isNotNull(poDoc.getAccountDistributionMethod())) {
distributionMethod = poDoc.getAccountDistributionMethod();
return distributionMethod;
}
}
return distributionMethod;
}
/**
* Clears out fields of the init tab.
*
* @param mapping An ActionMapping
* @param form An ActionForm
* @param request The HttpServletRequest
* @param response The HttpServletResponse
* @throws Exception
* @return An ActionForward
*/
public ActionForward clearInitFields(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
VendorCreditMemoForm cmForm = (VendorCreditMemoForm) form;
VendorCreditMemoDocument creditMemoDocument = (VendorCreditMemoDocument) cmForm.getDocument();
creditMemoDocument.clearInitFields();
return super.refresh(mapping, form, request, response);
}
/**
* Calls <code>CreditMemoService</code> to perform the duplicate credit memo check. If one is found, a question is setup and
* control is forwarded to the question action method. Coming back from the question prompt, the button that was clicked is
* checked, and if 'no' was selected, they are forward back to the page still in init mode.
*
* @param mapping An ActionMapping
* @param form An ActionForm
* @param request The HttpServletRequest
* @param response The HttpServletResponse
* @param creditMemoDocument The CreditMemoDocument
* @throws Exception
* @return An ActionForward
* @see org.kuali.kfs.module.purap.document.service.CreditMemoService
*/
protected ActionForward performDuplicateCreditMemoCheck(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response, VendorCreditMemoDocument creditMemoDocument) throws Exception {
ActionForward forward = null;
String duplicateMessage = SpringContext.getBean(CreditMemoService.class).creditMemoDuplicateMessages(creditMemoDocument);
if (StringUtils.isNotBlank(duplicateMessage)) {
Object question = request.getParameter(KFSConstants.QUESTION_INST_ATTRIBUTE_NAME);
if (question == null) {
return this.performQuestionWithoutInput(mapping, form, request, response, PurapConstants.PREQDocumentsStrings.DUPLICATE_INVOICE_QUESTION, duplicateMessage, KFSConstants.CONFIRMATION_QUESTION, "continueCreditMemo", "");
}
Object buttonClicked = request.getParameter(KFSConstants.QUESTION_CLICKED_BUTTON);
if ((PurapConstants.PREQDocumentsStrings.DUPLICATE_INVOICE_QUESTION.equals(question)) && ConfirmationQuestion.NO.equals(buttonClicked)) {
forward = mapping.findForward(KFSConstants.MAPPING_BASIC);
}
}
return forward;
}
/**
* Calls methods to perform credit allowed calculation and total credit memo amount.
*
* @param apDoc An AccountsPayableDocument
*/
@Override
protected void customCalculate(PurchasingAccountsPayableDocument apDoc) {
VendorCreditMemoDocument cmDocument = (VendorCreditMemoDocument) apDoc;
// call service method to finish up calculation
SpringContext.getBean(CreditMemoService.class).calculateCreditMemo(cmDocument);
// notice we're ignoring the boolean because these are just warnings they shouldn't halt anything
SpringContext.getBean(KualiRuleService.class).applyRules(new AttributedCalculateAccountsPayableEvent(cmDocument));
// }
}
/**
* Puts a credit memo on hold, prompting for a reason before hand. This stops further approvals or routing.
*
* @param mapping An ActionMapping
* @param form An ActionForm
* @param request The HttpServletRequest
* @param response The HttpServletResponse
* @throws Exception
* @return An ActionForward
*/
public ActionForward addHoldOnCreditMemo(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
String operation = "Hold ";
PurQuestionCallback callback = new PurQuestionCallback() {
@Override
public AccountsPayableDocument doPostQuestion(AccountsPayableDocument document, String noteText) throws Exception {
VendorCreditMemoDocument cmDocument = SpringContext.getBean(CreditMemoService.class).addHoldOnCreditMemo((VendorCreditMemoDocument) document, noteText);
return cmDocument;
}
};
return askQuestionWithInput(mapping, form, request, response, CMDocumentsStrings.HOLD_CM_QUESTION, operation, CMDocumentsStrings.HOLD_NOTE_PREFIX, PurapKeyConstants.CREDIT_MEMO_QUESTION_HOLD_DOCUMENT, callback);
}
/**
* Removes a hold on the credit memo.
*
* @param mapping An ActionMapping
* @param form An ActionForm
* @param request The HttpServletRequest
* @param response The HttpServletResponse
* @throws Exception
* @return An ActionForward
*/
public ActionForward removeHoldFromCreditMemo(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
String operation = "Remove Hold ";
PurQuestionCallback callback = new PurQuestionCallback() {
@Override
public AccountsPayableDocument doPostQuestion(AccountsPayableDocument document, String noteText) throws Exception {
VendorCreditMemoDocument cmDocument = SpringContext.getBean(CreditMemoService.class).removeHoldOnCreditMemo((VendorCreditMemoDocument) document, noteText);
return cmDocument;
}
};
return askQuestionWithInput(mapping, form, request, response, CMDocumentsStrings.REMOVE_HOLD_CM_QUESTION, operation, CMDocumentsStrings.REMOVE_HOLD_NOTE_PREFIX, PurapKeyConstants.CREDIT_MEMO_QUESTION_REMOVE_HOLD_DOCUMENT, callback);
}
/**
* @see org.kuali.kfs.module.purap.document.web.struts.AccountsPayableActionBase#cancelPOActionCallbackMethod()
*/
// @Override
// protected PurQuestionCallback cancelPOActionCallbackMethod() {
// return new PurQuestionCallback() {
// public AccountsPayableDocument doPostQuestion(AccountsPayableDocument document, String noteText) throws Exception {
// VendorCreditMemoDocument cmDocument = (VendorCreditMemoDocument) document;
// cmDocument.setClosePurchaseOrderIndicator(true);
// return cmDocument;
// }
// };
// }
/**
* @see org.kuali.kfs.module.purap.document.web.struts.AccountsPayableActionBase#getActionName()
*/
@Override
public String getActionName() {
return PurapConstants.CREDIT_MEMO_ACTION_NAME;
}
@Override
protected void populateAdHocActionRequestCodes(KualiDocumentFormBase formBase){
Document document = formBase.getDocument();
DocumentAuthorizer documentAuthorizer = getDocumentHelperService().getDocumentAuthorizer(document);
Map<String,String> adHocActionRequestCodes = new HashMap<String,String>();
if (documentAuthorizer.canSendAdHocRequests(document, KewApiConstants.ACTION_REQUEST_FYI_REQ, GlobalVariables.getUserSession().getPerson())) {
adHocActionRequestCodes.put(KewApiConstants.ACTION_REQUEST_FYI_REQ, KewApiConstants.ACTION_REQUEST_FYI_REQ_LABEL);
}
if ( (document.getDocumentHeader().getWorkflowDocument().isInitiated()
|| document.getDocumentHeader().getWorkflowDocument().isSaved()
|| document.getDocumentHeader().getWorkflowDocument().isEnroute()
)&& documentAuthorizer.canSendAdHocRequests(document, KewApiConstants.ACTION_REQUEST_ACKNOWLEDGE_REQ, GlobalVariables.getUserSession().getPerson())) {
adHocActionRequestCodes.put(KewApiConstants.ACTION_REQUEST_ACKNOWLEDGE_REQ, KewApiConstants.ACTION_REQUEST_ACKNOWLEDGE_REQ_LABEL);
}
formBase.setAdHocActionRequestCodes(adHocActionRequestCodes);
}
}