001/* 002 * Copyright 2008 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.ole.module.purap.util; 017 018import org.apache.commons.lang.StringUtils; 019import org.kuali.ole.module.purap.PurapConstants; 020import org.kuali.ole.module.purap.PurapPropertyConstants; 021import org.kuali.ole.module.purap.businessobject.*; 022import org.kuali.ole.module.purap.document.PurchaseOrderDocument; 023import org.kuali.ole.module.purap.document.service.PurapService; 024import org.kuali.ole.sys.OLEConstants; 025import org.kuali.ole.sys.context.SpringContext; 026import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader; 027import org.kuali.rice.kew.api.document.DocumentStatus; 028import org.kuali.rice.kew.api.exception.WorkflowException; 029import org.kuali.rice.kim.api.KimConstants; 030import org.kuali.rice.kim.api.services.IdentityManagementService; 031import org.kuali.rice.kns.service.DataDictionaryService; 032import org.kuali.rice.krad.datadictionary.exception.UnknownDocumentTypeException; 033import org.kuali.rice.krad.document.Document; 034import org.kuali.rice.krad.service.DocumentService; 035import org.kuali.rice.krad.service.KRADServiceLocatorInternal; 036import org.kuali.rice.krad.util.GlobalVariables; 037import org.kuali.rice.krad.util.ObjectUtils; 038import org.springframework.transaction.PlatformTransactionManager; 039import org.springframework.transaction.TransactionStatus; 040import org.springframework.transaction.support.TransactionCallback; 041import org.springframework.transaction.support.TransactionTemplate; 042 043import java.util.*; 044 045public class PurApRelatedViews { 046 private String documentNumber; 047 private Integer accountsPayablePurchasingDocumentLinkIdentifier; 048 049 private transient List<RequisitionView> relatedRequisitionViews; 050 private transient List<PurchaseOrderView> relatedPurchaseOrderViews; 051 private transient List<PaymentRequestView> relatedPaymentRequestViews; 052 private transient List<PaymentRequestView> paymentHistoryPaymentRequestViews; 053 private transient List<CreditMemoView> relatedCreditMemoViews; 054 private transient List<CreditMemoView> paymentHistoryCreditMemoViews; 055 private transient List<InvoiceView> relatedInvoiceViews; 056 private transient List<InvoiceView> paymentHistoryInvoiceViews; 057 private transient List<LineItemReceivingView> relatedLineItemReceivingViews; 058 private transient List<CorrectionReceivingView> relatedCorrectionReceivingViews; 059 private transient List<BulkReceivingView> relatedBulkReceivingViews; 060 private transient List<PurchaseOrderViewGroup> groupedRelatedPurchaseOrderViews; 061 private transient List<ReceivingViewGroup> groupedRelatedReceivingViews; 062 private transient List<ElectronicInvoiceRejectView> relatedRejectViews; 063 064 public PurApRelatedViews(String documentNumber, Integer accountsPayablePurchasingDocumentLinkIdentifier) { 065 super(); 066 this.documentNumber = documentNumber; 067 this.accountsPayablePurchasingDocumentLinkIdentifier = accountsPayablePurchasingDocumentLinkIdentifier; 068 } 069 070 /** 071 * Reset all related view lists to null. 072 */ 073 public void resetRelatedViews() { 074 relatedRequisitionViews = null; 075 relatedPurchaseOrderViews = null; 076 relatedPaymentRequestViews = null; 077 paymentHistoryPaymentRequestViews = null; 078 relatedInvoiceViews = null; 079 paymentHistoryInvoiceViews = null; 080 relatedCreditMemoViews = null; 081 paymentHistoryCreditMemoViews = null; 082 relatedLineItemReceivingViews = null; 083 relatedCorrectionReceivingViews = null; 084 relatedBulkReceivingViews = null; 085 groupedRelatedPurchaseOrderViews = null; 086 groupedRelatedReceivingViews = null; 087 relatedRejectViews = null; 088 } 089 090 private static PlatformTransactionManager transactionManager; 091 092 public static PlatformTransactionManager getTransactionManager() { 093 if (transactionManager == null) { 094 transactionManager = GlobalResourceLoader.getService("transactionManager"); 095 } 096 return transactionManager; 097 } 098 099 public List updateRelatedView(final Class<?> clazz, List<? extends AbstractRelatedView> relatedList, final boolean removeCurrentDocument) { 100 if (relatedList == null) { 101 TransactionTemplate template = new TransactionTemplate(getTransactionManager()); 102 relatedList = template.execute(new TransactionCallback<List<? extends AbstractRelatedView>>() { 103 @Override 104 public List<? extends AbstractRelatedView> doInTransaction(TransactionStatus status) { 105 List<? extends AbstractRelatedView> relatedList = SpringContext.getBean(PurapService.class).getRelatedViews(clazz, accountsPayablePurchasingDocumentLinkIdentifier); 106 if (removeCurrentDocument) { 107 for (AbstractRelatedView view : relatedList) { 108 //KFSMI-4576 Mask/Unmask purapDocumentIdentifier field value 109 maskPONumberIfUnapproved(view); 110 if (documentNumber.equals(view.getDocumentNumber())) { 111 relatedList.remove(view); 112 break; 113 } 114 } 115 } 116 return relatedList; 117 } 118 }); 119 } 120 121 return relatedList; 122 } 123 124 /** 125 * masks the po number if the po is unappoved yet. If the document status is not FINAL then 126 * check for permission for purapDocumentIdentifier field. If NOT permitted to view the value 127 * then mask the value with * and setting this value in poNumberMasked property. 128 * 129 * @param view 130 */ 131 protected void maskPONumberIfUnapproved(AbstractRelatedView view) { 132 Document document = findDocument(view.getDocumentNumber()); 133 134 String poIDstr = ""; 135 136 if (ObjectUtils.isNotNull(view.getPurapDocumentIdentifier())) { 137 poIDstr = view.getPurapDocumentIdentifier().toString(); 138 } 139 140 if (PurapConstants.PurapDocTypeCodes.PO_DOCUMENT.equals(document.getDocumentHeader().getWorkflowDocument().getDocumentTypeName())) { 141 if ((document != null) && (document.getDocumentHeader().getWorkflowDocument() != null)) { 142 if (!document.getDocumentHeader().getWorkflowDocument().getStatus().equals(DocumentStatus.FINAL)) { 143 144 String principalId = GlobalVariables.getUserSession().getPrincipalId(); 145 146 String namespaceCode = OLEConstants.ParameterNamespaces.KNS; 147 String permissionTemplateName = KimConstants.PermissionTemplateNames.FULL_UNMASK_FIELD; 148 149 Map<String, String> roleQualifiers = new HashMap<String, String>(); 150 151 Map<String, String> permissionDetails = new HashMap<String, String>(); 152 permissionDetails.put(KimConstants.AttributeConstants.COMPONENT_NAME, PurchaseOrderDocument.class.getSimpleName()); 153 permissionDetails.put(KimConstants.AttributeConstants.PROPERTY_NAME, PurapPropertyConstants.PURAP_DOC_ID); 154 155 IdentityManagementService identityManagementService = SpringContext.getBean(IdentityManagementService.class); 156 Boolean isAuthorized = identityManagementService.isAuthorizedByTemplateName(principalId, namespaceCode, permissionTemplateName, permissionDetails, roleQualifiers); 157 if (!isAuthorized) { 158 //not authorized to see... so mask the po number string 159 poIDstr = ""; 160 int strLength = SpringContext.getBean(DataDictionaryService.class).getAttributeMaxLength(PurApGenericAttributes.class.getName(), PurapPropertyConstants.PURAP_DOC_ID); 161 for (int i = 0; i < strLength; i++) { 162 poIDstr = poIDstr.concat("*"); 163 } 164 } 165 } 166 } 167 } 168 169 view.setPoNumberMasked(poIDstr); 170 } 171 172 /** 173 * This method finds the document for the given document header id 174 * 175 * @param documentHeaderId 176 * @return document The document in the workflow that matches the document header id. 177 */ 178 protected Document findDocument(String documentHeaderId) { 179 Document document = null; 180 181 try { 182 document = SpringContext.getBean(DocumentService.class).getByDocumentHeaderId(documentHeaderId); 183 } catch (WorkflowException ex) { 184 } catch (UnknownDocumentTypeException ex) { 185 // don't blow up just because a document type is not installed (but don't return it either) 186 } 187 188 return document; 189 } 190 191 /** 192 * @see org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocument#getRelatedRequisitionViews() 193 */ 194 public List<RequisitionView> getRelatedRequisitionViews() { 195 if(relatedRequisitionViews==null){ 196 relatedRequisitionViews = updateRelatedView(RequisitionView.class, relatedRequisitionViews, true); 197 } 198 return relatedRequisitionViews; 199 } 200 201 public List<ElectronicInvoiceRejectView> getRelatedRejectViews() { 202 relatedRejectViews = updateRelatedView(ElectronicInvoiceRejectView.class, relatedRejectViews, true); 203 return relatedRejectViews; 204 } 205 206 /** 207 * Obtains a list of related PurchaseOrderViews, first ordered by POIDs descending, then by document numbers descending; 208 * thus POs with newer POIDs will be in the front, and within the same POID, the current PO will be in the front. 209 * 210 * @return A list of <PurchaseOrderView> with newer POs in the front. 211 */ 212 public List<PurchaseOrderView> getRelatedPurchaseOrderViews() { 213 if (relatedPurchaseOrderViews != null) { 214 return relatedPurchaseOrderViews; 215 } 216 217 // Obtain a list which is sorted by workflow document ID descending. 218 relatedPurchaseOrderViews = updateRelatedView(PurchaseOrderView.class, relatedPurchaseOrderViews, true); 219 220 // Sort the list. 221 Collections.sort(relatedPurchaseOrderViews, 222 new Comparator<PurchaseOrderView>() { 223 @Override 224 public int compare(PurchaseOrderView v1, PurchaseOrderView v2) { 225 if ((v1 != null) && (v2 != null) && 226 (v1.getPurapDocumentIdentifier() != null) && 227 (v2.getPurapDocumentIdentifier() != null)) { 228 // sort by POID descending 229 int compare = -v1.getPurapDocumentIdentifier().compareTo(v2.getPurapDocumentIdentifier()); 230 // if POIDs are the same, sort by document number descending; usually current PO has biggest documentNumber 231 if (compare == 0) { 232 compare = v1.getPurchaseOrderCurrentIndicator() ? -1 : 233 v2.getPurchaseOrderCurrentIndicator() ? 1 : 234 -v1.getCreateDate().compareTo(v2.getCreateDate()); 235 } 236 return compare; 237 } 238 return 0; 239 } 240 } 241 ); 242 243 return relatedPurchaseOrderViews; 244 } 245 246 /** 247 * Groups related PurchaseOrderViews by POIDs descending, and within each group order POs by document numbers descending; 248 * thus groups of newer POIDs will be in the front, and within each group, more current POs will be in the front. 249 * 250 * @return A list of <PurchaseOrderViewGroup> with newer POs in the front. 251 * @see org.kuali.ole.module.purap.util.PurApRelatedViews.getRelatedPurchaseOrderViews 252 * @see org.kuali.ole.module.purap.businessobject.PurchaseOrderView 253 */ 254 public List<PurchaseOrderViewGroup> getGroupedRelatedPurchaseOrderViews() { 255 if (groupedRelatedPurchaseOrderViews != null) { 256 return groupedRelatedPurchaseOrderViews; 257 } 258 259 /* 260 * This extra layer of grouping is necessary in order to display the notes for a group of 261 * related POChange documents (which should have identical POID) after that group, 262 * and before any other related groups which may result from PO splitting (with different POIDs). 263 * With direct use of relatedPurchaseOrderViews, location of the end of the group is problematic. 264 */ 265 groupedRelatedPurchaseOrderViews = new ArrayList<PurchaseOrderViewGroup>(); 266 PurchaseOrderViewGroup group = new PurchaseOrderViewGroup(); 267 int previousPOID = 0; 268 relatedPurchaseOrderViews = getRelatedPurchaseOrderViews(); 269 for (PurchaseOrderView view : relatedPurchaseOrderViews) { 270 if (previousPOID == 0) { 271 previousPOID = view.getPurapDocumentIdentifier(); 272 273 } 274 if (view.getPurapDocumentIdentifier() == previousPOID) { 275 group.getViews().add(view); 276 } else { 277 groupedRelatedPurchaseOrderViews.add(group); 278 group = new PurchaseOrderViewGroup(); 279 group.getViews().add(view); 280 previousPOID = view.getPurapDocumentIdentifier(); 281 } 282 if (relatedPurchaseOrderViews.size() == relatedPurchaseOrderViews.indexOf(view) + 1) { 283 groupedRelatedPurchaseOrderViews.add(group); 284 } 285 } 286 287 return groupedRelatedPurchaseOrderViews; 288 } 289 290 /** 291 * @see org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocument#getRelatedPaymentRequestViews() 292 */ 293 public List<PaymentRequestView> getRelatedPaymentRequestViews() { 294 if(relatedPaymentRequestViews == null){ 295 relatedPaymentRequestViews = updateRelatedView(PaymentRequestView.class, relatedPaymentRequestViews, true); 296 } 297 return relatedPaymentRequestViews; 298 } 299 300 /** 301 * @see org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocument#getRelatedCreditMemoViews() 302 */ 303 public List<CreditMemoView> getRelatedCreditMemoViews() { 304 if(relatedCreditMemoViews==null){ 305 relatedCreditMemoViews = updateRelatedView(CreditMemoView.class, relatedCreditMemoViews, true); 306 } 307 return relatedCreditMemoViews; 308 } 309 310 311 /** 312 * @see org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocument#getRelatedPaymentRequestViews() 313 */ 314 public List<InvoiceView> getRelatedInvoiceViews() { 315 relatedInvoiceViews = updateRelatedView(InvoiceView.class, relatedInvoiceViews, true); 316 return relatedInvoiceViews; 317 } 318 319 /** 320 * Gets the Payment History Payment Request Views for this document. 321 * 322 * @return the list of Payment History Payment Request Views. 323 */ 324 public List<PaymentRequestView> getPaymentHistoryPaymentRequestViews() { 325 if(paymentHistoryPaymentRequestViews==null){ 326 paymentHistoryPaymentRequestViews = updateRelatedView(PaymentRequestView.class, paymentHistoryPaymentRequestViews, false); 327 } 328 return paymentHistoryPaymentRequestViews; 329 } 330 331 /** 332 * Gets the Payment History Credit Memo Views for this document. 333 * 334 * @return the list of Payment History Credit Memo Views. 335 */ 336 public List<CreditMemoView> getPaymentHistoryCreditMemoViews() { 337 if(paymentHistoryCreditMemoViews==null){ 338 paymentHistoryCreditMemoViews = updateRelatedView(CreditMemoView.class, paymentHistoryCreditMemoViews, false); 339 } 340 return paymentHistoryCreditMemoViews; 341 } 342 343 /** 344 * Gets the Payment History Invoice Views for this document. 345 * 346 * @return the list of Payment History Invoice Views. 347 */ 348 public List<InvoiceView> getPaymentHistoryInvoiceViews() { 349 paymentHistoryInvoiceViews = updateRelatedView(InvoiceView.class, paymentHistoryInvoiceViews, false); 350 return paymentHistoryInvoiceViews; 351 } 352 353 /** 354 * @see org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocument#getRelatedRequisitionViews() 355 */ 356 public List<LineItemReceivingView> getRelatedLineItemReceivingViews() { 357 if(relatedLineItemReceivingViews==null){ 358 relatedLineItemReceivingViews = updateRelatedView(LineItemReceivingView.class, relatedLineItemReceivingViews, true); 359 } 360 return relatedLineItemReceivingViews; 361 } 362 363 /** 364 * @see org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocument#getRelatedRequisitionViews() 365 */ 366 public List<CorrectionReceivingView> getRelatedCorrectionReceivingViews() { 367 if(relatedCorrectionReceivingViews == null){ 368 relatedCorrectionReceivingViews = updateRelatedView(CorrectionReceivingView.class, relatedCorrectionReceivingViews, true); 369 } 370 return relatedCorrectionReceivingViews; 371 } 372 373 public List<BulkReceivingView> getRelatedBulkReceivingViews() { 374 relatedBulkReceivingViews = updateRelatedView(BulkReceivingView.class, relatedBulkReceivingViews, true); 375 return relatedBulkReceivingViews; 376 } 377 378 /** 379 * Groups related LineItemReceivingView and its CorrectionReceivingViews, with more recent receiving groups in the front; 380 * and within each group, with more recent corrections in the front. 381 * 382 * @return A list of ReceivingCorrectionViewGroups. 383 */ 384 public List<ReceivingViewGroup> getGroupedRelatedReceivingViews() { 385 if (groupedRelatedReceivingViews != null) { 386 return groupedRelatedReceivingViews; 387 } 388 389 groupedRelatedReceivingViews = new ArrayList<ReceivingViewGroup>(); 390 PurapService purapService = SpringContext.getBean(PurapService.class); 391 List<LineItemReceivingView> liviews = purapService.getRelatedViews(LineItemReceivingView.class, accountsPayablePurchasingDocumentLinkIdentifier); 392 List<CorrectionReceivingView> crviews = purapService.getRelatedViews(CorrectionReceivingView.class, accountsPayablePurchasingDocumentLinkIdentifier); 393 394 // both LineItemReceivingViews and CorrectionReceivingViews are already in order with most recent first, so no need to sort 395 for (LineItemReceivingView liview : liviews) { 396 ReceivingViewGroup group = new ReceivingViewGroup(); 397 group.lineItemView = liview; // could be current document 398 for (CorrectionReceivingView crview : crviews) { 399 if (StringUtils.equals(crview.getLineItemReceivingDocumentNumber(), liview.getDocumentNumber()) && 400 !documentNumber.equals(crview.getDocumentNumber())) {// exclude current document 401 group.addCorrectionView(crview); 402 } 403 } 404 groupedRelatedReceivingViews.add(group); 405 } 406 407 return groupedRelatedReceivingViews; 408 } 409 410 /** 411 * A container for a List<PurchaseOrderView>, to be used by a nested c:forEach tag 412 * in relatedPurchaseOrderDocumentsDetail.tag. 413 */ 414 public class PurchaseOrderViewGroup { 415 protected List<PurchaseOrderView> views = new ArrayList<PurchaseOrderView>(); 416 417 protected PurchaseOrderViewGroup() { 418 } 419 420 public List<PurchaseOrderView> getViews() { 421 return views; 422 } 423 } 424 425 /** 426 * A container for a LineItemReceivingView and a list of its associated CorrectionReceivingViews. 427 */ 428 public class ReceivingViewGroup { 429 protected LineItemReceivingView lineItemView; 430 protected List<CorrectionReceivingView> correctionViews = new ArrayList<CorrectionReceivingView>(); 431 432 protected ReceivingViewGroup() { 433 } 434 435 public LineItemReceivingView getLineItemView() { 436 return lineItemView; 437 } 438 439 public List<CorrectionReceivingView> getCorrectionViews() { 440 return correctionViews; 441 } 442 443 public void addCorrectionView(CorrectionReceivingView correctionView) { 444 correctionViews.add(correctionView); 445 } 446 447 public boolean getIsLineItemViewCurrentDocument() { 448 return (lineItemView != null && documentNumber.equals(lineItemView.getDocumentNumber())); 449 } 450 } 451 452}