/*
* 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.util;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.kuali.kfs.module.purap.PurapConstants;
import org.kuali.kfs.module.purap.PurapPropertyConstants;
import org.kuali.kfs.module.purap.businessobject.AbstractRelatedView;
import org.kuali.kfs.module.purap.businessobject.BulkReceivingView;
import org.kuali.kfs.module.purap.businessobject.CorrectionReceivingView;
import org.kuali.kfs.module.purap.businessobject.CreditMemoView;
import org.kuali.kfs.module.purap.businessobject.ElectronicInvoiceRejectView;
import org.kuali.kfs.module.purap.businessobject.LineItemReceivingView;
import org.kuali.kfs.module.purap.businessobject.PaymentRequestView;
import org.kuali.kfs.module.purap.businessobject.PurApGenericAttributes;
import org.kuali.kfs.module.purap.businessobject.PurchaseOrderView;
import org.kuali.kfs.module.purap.businessobject.RequisitionView;
import org.kuali.kfs.module.purap.document.PurchaseOrderDocument;
import org.kuali.kfs.module.purap.document.service.PurapService;
import org.kuali.kfs.sys.KFSConstants;
import org.kuali.kfs.sys.context.SpringContext;
import org.kuali.rice.kew.api.KewApiServiceLocator;
import org.kuali.rice.kew.api.document.DocumentStatus;
import org.kuali.rice.kew.api.exception.WorkflowException;
import org.kuali.rice.kim.api.KimConstants;
import org.kuali.rice.kim.api.services.IdentityManagementService;
import org.kuali.rice.kns.service.DataDictionaryService;
import org.kuali.rice.krad.datadictionary.exception.UnknownDocumentTypeException;
import org.kuali.rice.krad.document.Document;
import org.kuali.rice.krad.service.DocumentService;
import org.kuali.rice.krad.util.GlobalVariables;
import org.kuali.rice.krad.util.ObjectUtils;
public class PurApRelatedViews {
private String documentNumber;
private Integer accountsPayablePurchasingDocumentLinkIdentifier;
private transient List<RequisitionView> relatedRequisitionViews;
private transient List<PurchaseOrderView> relatedPurchaseOrderViews;
private transient List<PaymentRequestView> relatedPaymentRequestViews;
private transient List<PaymentRequestView> paymentHistoryPaymentRequestViews;
private transient List<CreditMemoView> relatedCreditMemoViews;
private transient List<CreditMemoView> paymentHistoryCreditMemoViews;
private transient List<LineItemReceivingView> relatedLineItemReceivingViews;
private transient List<CorrectionReceivingView> relatedCorrectionReceivingViews;
private transient List<BulkReceivingView> relatedBulkReceivingViews;
private transient List<PurchaseOrderViewGroup> groupedRelatedPurchaseOrderViews;
private transient List<ReceivingViewGroup> groupedRelatedReceivingViews;
private transient List<ElectronicInvoiceRejectView> relatedRejectViews;
public PurApRelatedViews(String documentNumber, Integer accountsPayablePurchasingDocumentLinkIdentifier) {
super();
this.documentNumber = documentNumber;
this.accountsPayablePurchasingDocumentLinkIdentifier = accountsPayablePurchasingDocumentLinkIdentifier;
}
/**
* Reset all related view lists to null.
*/
public void resetRelatedViews() {
relatedRequisitionViews = null;
relatedPurchaseOrderViews = null;
relatedPaymentRequestViews = null;
paymentHistoryPaymentRequestViews = null;
relatedCreditMemoViews = null;
paymentHistoryCreditMemoViews = null;
relatedLineItemReceivingViews = null;
relatedCorrectionReceivingViews = null;
relatedBulkReceivingViews = null;
groupedRelatedPurchaseOrderViews = null;
groupedRelatedReceivingViews = null;
relatedRejectViews = null;
}
public List updateRelatedView(Class<?> clazz, List<? extends AbstractRelatedView> relatedList, boolean removeCurrentDocument) {
if (relatedList == null) {
relatedList = SpringContext.getBean(PurapService.class).getRelatedViews(clazz, accountsPayablePurchasingDocumentLinkIdentifier);
if (removeCurrentDocument) {
for (AbstractRelatedView view : relatedList) {
//KFSMI-4576 Mask/Unmask purapDocumentIdentifier field value
maskPONumberIfUnapproved(view);
if (documentNumber.equals(view.getDocumentNumber())) {
relatedList.remove(view);
break;
}
}
}
}
return relatedList;
}
/**
* masks the po number if the po is unappoved yet. If the document status is not FINAL then
* check for permission for purapDocumentIdentifier field. If NOT permitted to view the value
* then mask the value with * and setting this value in poNumberMasked property.
*
* @param view
*/
protected void maskPONumberIfUnapproved(AbstractRelatedView view) {
String poIDstr = "";
if (ObjectUtils.isNotNull(view.getPurapDocumentIdentifier())) {
poIDstr = view.getPurapDocumentIdentifier().toString();
}
if (PurapConstants.PurapDocTypeCodes.PO_DOCUMENT.equals(view.getDocumentTypeName())) {
DocumentStatus documentStatus = KewApiServiceLocator.getWorkflowDocumentService().getDocumentStatus(view.getDocumentNumber());
if (!(StringUtils.equals(documentStatus.getCode(), DocumentStatus.FINAL.getCode()))) {
String principalId = GlobalVariables.getUserSession().getPrincipalId();
String namespaceCode = KFSConstants.ParameterNamespaces.KNS;
String permissionTemplateName = KimConstants.PermissionTemplateNames.FULL_UNMASK_FIELD;
Map<String,String> roleQualifiers = new HashMap<String,String>();
Map<String,String> permissionDetails = new HashMap<String,String>();
permissionDetails.put(KimConstants.AttributeConstants.COMPONENT_NAME, PurchaseOrderDocument.class.getSimpleName());
permissionDetails.put(KimConstants.AttributeConstants.PROPERTY_NAME, PurapPropertyConstants.PURAP_DOC_ID);
IdentityManagementService identityManagementService = SpringContext.getBean(IdentityManagementService.class);
Boolean isAuthorized = identityManagementService.isAuthorizedByTemplateName(principalId, namespaceCode, permissionTemplateName, permissionDetails, roleQualifiers);
if (!isAuthorized) {
//not authorized to see... so mask the po number string
poIDstr = "";
int strLength = SpringContext.getBean(DataDictionaryService.class).getAttributeMaxLength(PurApGenericAttributes.class.getName(), PurapPropertyConstants.PURAP_DOC_ID);
for (int i = 0; i < strLength; i++) {
poIDstr = poIDstr.concat("*");
}
}
}
}
view.setPoNumberMasked(poIDstr);
}
public org.kuali.rice.kew.api.document.Document getWorkflowDocument(String documentId){
return KewApiServiceLocator.getWorkflowDocumentService().getDocument(documentId);
}
/**
* This method finds the document for the given document header id
* @param documentHeaderId
* @return document The document in the workflow that matches the document header id.
*/
protected Document findDocument(String documentHeaderId) {
Document document = null;
try {
document = SpringContext.getBean(DocumentService.class).getByDocumentHeaderId(documentHeaderId);
}
catch (WorkflowException ex) {
} catch (UnknownDocumentTypeException ex) {
// don't blow up just because a document type is not installed (but don't return it either)
}
return document;
}
/**
* @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument#getRelatedRequisitionViews()
*/
public List<RequisitionView> getRelatedRequisitionViews() {
relatedRequisitionViews = updateRelatedView(RequisitionView.class, relatedRequisitionViews, true);
return relatedRequisitionViews;
}
public List<ElectronicInvoiceRejectView> getRelatedRejectViews() {
relatedRejectViews = updateRelatedView(ElectronicInvoiceRejectView.class, relatedRejectViews, true);
return relatedRejectViews;
}
/**
* Obtains a list of related PurchaseOrderViews, first ordered by POIDs descending, then by document numbers descending;
* thus POs with newer POIDs will be in the front, and within the same POID, the current PO will be in the front.
*
* @return A list of <PurchaseOrderView> with newer POs in the front.
*/
public List<PurchaseOrderView> getRelatedPurchaseOrderViews() {
if (relatedPurchaseOrderViews != null) {
return relatedPurchaseOrderViews;
}
// Obtain a list which is sorted by workflow document ID descending.
relatedPurchaseOrderViews = updateRelatedView(PurchaseOrderView.class, relatedPurchaseOrderViews, true);
// Sort the list.
Collections.sort(relatedPurchaseOrderViews,
new Comparator<PurchaseOrderView>() {
@Override
public int compare(PurchaseOrderView v1, PurchaseOrderView v2) {
if ((v1 != null) && (v2 != null) &&
(v1.getPurapDocumentIdentifier() != null) &&
(v2.getPurapDocumentIdentifier() != null)) {
// sort by POID descending
int compare = -v1.getPurapDocumentIdentifier().compareTo(v2.getPurapDocumentIdentifier());
// if POIDs are the same, sort by document number descending; usually current PO has biggest documentNumber
if (compare == 0) {
compare = v1.getPurchaseOrderCurrentIndicator() ? -1 :
v2.getPurchaseOrderCurrentIndicator() ? 1 :
-v1.getCreateDate().compareTo(v2.getCreateDate());
}
return compare;
}
return 0;
}
}
);
return relatedPurchaseOrderViews;
}
/**
* Groups related PurchaseOrderViews by POIDs descending, and within each group order POs by document numbers descending;
* thus groups of newer POIDs will be in the front, and within each group, more current POs will be in the front.
*
* @return A list of <PurchaseOrderViewGroup> with newer POs in the front.
* @see org.kuali.kfs.module.purap.util.PurApRelatedViews.getRelatedPurchaseOrderViews
* @see org.kuali.kfs.module.purap.businessobject.PurchaseOrderView
*/
public List<PurchaseOrderViewGroup> getGroupedRelatedPurchaseOrderViews() {
if (groupedRelatedPurchaseOrderViews != null) {
return groupedRelatedPurchaseOrderViews;
}
/*
* This extra layer of grouping is necessary in order to display the notes for a group of
* related POChange documents (which should have identical POID) after that group,
* and before any other related groups which may result from PO splitting (with different POIDs).
* With direct use of relatedPurchaseOrderViews, location of the end of the group is problematic.
*/
groupedRelatedPurchaseOrderViews = new ArrayList<PurchaseOrderViewGroup>();
PurchaseOrderViewGroup group = new PurchaseOrderViewGroup();
int previousPOID = 0;
relatedPurchaseOrderViews = getRelatedPurchaseOrderViews();
for(PurchaseOrderView view : relatedPurchaseOrderViews) {
if (previousPOID == 0) {
previousPOID = view.getPurapDocumentIdentifier();
}
if( view.getPurapDocumentIdentifier() == previousPOID ) {
group.getViews().add(view);
}
else {
groupedRelatedPurchaseOrderViews.add(group);
group = new PurchaseOrderViewGroup();
group.getViews().add(view);
previousPOID = view.getPurapDocumentIdentifier();
}
if (relatedPurchaseOrderViews.size() == relatedPurchaseOrderViews.indexOf(view) + 1) {
groupedRelatedPurchaseOrderViews.add(group);
}
}
return groupedRelatedPurchaseOrderViews;
}
/**
* @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument#getRelatedPaymentRequestViews()
*/
public List<PaymentRequestView> getRelatedPaymentRequestViews() {
relatedPaymentRequestViews = updateRelatedView(PaymentRequestView.class, relatedPaymentRequestViews, true);
return relatedPaymentRequestViews;
}
/**
* @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument#getRelatedCreditMemoViews()
*/
public List<CreditMemoView> getRelatedCreditMemoViews() {
relatedCreditMemoViews = updateRelatedView(CreditMemoView.class, relatedCreditMemoViews, true);
return relatedCreditMemoViews;
}
/**
* Gets the Payment History Payment Request Views for this document.
*
* @return the list of Payment History Payment Request Views.
*/
public List<PaymentRequestView> getPaymentHistoryPaymentRequestViews() {
paymentHistoryPaymentRequestViews = updateRelatedView(PaymentRequestView.class, paymentHistoryPaymentRequestViews, false);
return paymentHistoryPaymentRequestViews;
}
/**
* Gets the Payment History Credit Memo Views for this document.
*
* @return the list of Payment History Credit Memo Views.
*/
public List<CreditMemoView> getPaymentHistoryCreditMemoViews() {
paymentHistoryCreditMemoViews = updateRelatedView(CreditMemoView.class, paymentHistoryCreditMemoViews, false);
return paymentHistoryCreditMemoViews;
}
/**
* @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument#getRelatedRequisitionViews()
*/
public List<LineItemReceivingView> getRelatedLineItemReceivingViews() {
relatedLineItemReceivingViews = updateRelatedView(LineItemReceivingView.class, relatedLineItemReceivingViews, true);
return relatedLineItemReceivingViews;
}
/**
* @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument#getRelatedRequisitionViews()
*/
public List<CorrectionReceivingView> getRelatedCorrectionReceivingViews() {
relatedCorrectionReceivingViews = updateRelatedView(CorrectionReceivingView.class, relatedCorrectionReceivingViews, true);
return relatedCorrectionReceivingViews;
}
public List<BulkReceivingView> getRelatedBulkReceivingViews() {
relatedBulkReceivingViews = updateRelatedView(BulkReceivingView.class, relatedBulkReceivingViews, true);
return relatedBulkReceivingViews;
}
/**
* Groups related LineItemReceivingView and its CorrectionReceivingViews, with more recent receiving groups in the front;
* and within each group, with more recent corrections in the front.
*
* @return A list of ReceivingCorrectionViewGroups.
*/
public List<ReceivingViewGroup> getGroupedRelatedReceivingViews() {
if (groupedRelatedReceivingViews != null) {
return groupedRelatedReceivingViews;
}
groupedRelatedReceivingViews = new ArrayList<ReceivingViewGroup>();
PurapService purapService = SpringContext.getBean(PurapService.class);
List<LineItemReceivingView> liviews = purapService.getRelatedViews(LineItemReceivingView.class, accountsPayablePurchasingDocumentLinkIdentifier);
List<CorrectionReceivingView> crviews = purapService.getRelatedViews(CorrectionReceivingView.class, accountsPayablePurchasingDocumentLinkIdentifier);
// both LineItemReceivingViews and CorrectionReceivingViews are already in order with most recent first, so no need to sort
for (LineItemReceivingView liview : liviews) {
ReceivingViewGroup group = new ReceivingViewGroup();
group.lineItemView = liview; // could be current document
for (CorrectionReceivingView crview : crviews) {
if (StringUtils.equals(crview.getLineItemReceivingDocumentNumber(), liview.getDocumentNumber()) &&
!documentNumber.equals(crview.getDocumentNumber())) {// exclude current document
group.addCorrectionView(crview);
}
}
groupedRelatedReceivingViews.add(group);
}
return groupedRelatedReceivingViews;
}
/**
* A container for a List<PurchaseOrderView>, to be used by a nested c:forEach tag
* in relatedPurchaseOrderDocumentsDetail.tag.
*/
public class PurchaseOrderViewGroup {
protected List<PurchaseOrderView> views = new ArrayList<PurchaseOrderView>();
protected PurchaseOrderViewGroup() {
}
public List<PurchaseOrderView> getViews() {
return views;
}
}
/**
* A container for a LineItemReceivingView and a list of its associated CorrectionReceivingViews.
*/
public class ReceivingViewGroup {
protected LineItemReceivingView lineItemView;
protected List<CorrectionReceivingView> correctionViews = new ArrayList<CorrectionReceivingView>();
protected ReceivingViewGroup() {
}
public LineItemReceivingView getLineItemView() {
return lineItemView;
}
public List<CorrectionReceivingView> getCorrectionViews() {
return correctionViews;
}
public void addCorrectionView(CorrectionReceivingView correctionView) {
correctionViews.add(correctionView);
}
public boolean getIsLineItemViewCurrentDocument() {
return (lineItemView != null && documentNumber.equals(lineItemView.getDocumentNumber()));
}
}
}