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 relatedRequisitionViews = updateRelatedView(RequisitionView.class, relatedRequisitionViews, true); 196 return relatedRequisitionViews; 197 } 198 199 public List<ElectronicInvoiceRejectView> getRelatedRejectViews() { 200 relatedRejectViews = updateRelatedView(ElectronicInvoiceRejectView.class, relatedRejectViews, true); 201 return relatedRejectViews; 202 } 203 204 /** 205 * Obtains a list of related PurchaseOrderViews, first ordered by POIDs descending, then by document numbers descending; 206 * thus POs with newer POIDs will be in the front, and within the same POID, the current PO will be in the front. 207 * 208 * @return A list of <PurchaseOrderView> with newer POs in the front. 209 */ 210 public List<PurchaseOrderView> getRelatedPurchaseOrderViews() { 211 if (relatedPurchaseOrderViews != null) { 212 return relatedPurchaseOrderViews; 213 } 214 215 // Obtain a list which is sorted by workflow document ID descending. 216 relatedPurchaseOrderViews = updateRelatedView(PurchaseOrderView.class, relatedPurchaseOrderViews, true); 217 218 // Sort the list. 219 Collections.sort(relatedPurchaseOrderViews, 220 new Comparator<PurchaseOrderView>() { 221 @Override 222 public int compare(PurchaseOrderView v1, PurchaseOrderView v2) { 223 if ((v1 != null) && (v2 != null) && 224 (v1.getPurapDocumentIdentifier() != null) && 225 (v2.getPurapDocumentIdentifier() != null)) { 226 // sort by POID descending 227 int compare = -v1.getPurapDocumentIdentifier().compareTo(v2.getPurapDocumentIdentifier()); 228 // if POIDs are the same, sort by document number descending; usually current PO has biggest documentNumber 229 if (compare == 0) { 230 compare = v1.getPurchaseOrderCurrentIndicator() ? -1 : 231 v2.getPurchaseOrderCurrentIndicator() ? 1 : 232 -v1.getCreateDate().compareTo(v2.getCreateDate()); 233 } 234 return compare; 235 } 236 return 0; 237 } 238 } 239 ); 240 241 return relatedPurchaseOrderViews; 242 } 243 244 /** 245 * Groups related PurchaseOrderViews by POIDs descending, and within each group order POs by document numbers descending; 246 * thus groups of newer POIDs will be in the front, and within each group, more current POs will be in the front. 247 * 248 * @return A list of <PurchaseOrderViewGroup> with newer POs in the front. 249 * @see org.kuali.ole.module.purap.util.PurApRelatedViews.getRelatedPurchaseOrderViews 250 * @see org.kuali.ole.module.purap.businessobject.PurchaseOrderView 251 */ 252 public List<PurchaseOrderViewGroup> getGroupedRelatedPurchaseOrderViews() { 253 if (groupedRelatedPurchaseOrderViews != null) { 254 return groupedRelatedPurchaseOrderViews; 255 } 256 257 /* 258 * This extra layer of grouping is necessary in order to display the notes for a group of 259 * related POChange documents (which should have identical POID) after that group, 260 * and before any other related groups which may result from PO splitting (with different POIDs). 261 * With direct use of relatedPurchaseOrderViews, location of the end of the group is problematic. 262 */ 263 groupedRelatedPurchaseOrderViews = new ArrayList<PurchaseOrderViewGroup>(); 264 PurchaseOrderViewGroup group = new PurchaseOrderViewGroup(); 265 int previousPOID = 0; 266 relatedPurchaseOrderViews = getRelatedPurchaseOrderViews(); 267 for (PurchaseOrderView view : relatedPurchaseOrderViews) { 268 if (previousPOID == 0) { 269 previousPOID = view.getPurapDocumentIdentifier(); 270 271 } 272 if (view.getPurapDocumentIdentifier() == previousPOID) { 273 group.getViews().add(view); 274 } else { 275 groupedRelatedPurchaseOrderViews.add(group); 276 group = new PurchaseOrderViewGroup(); 277 group.getViews().add(view); 278 previousPOID = view.getPurapDocumentIdentifier(); 279 } 280 if (relatedPurchaseOrderViews.size() == relatedPurchaseOrderViews.indexOf(view) + 1) { 281 groupedRelatedPurchaseOrderViews.add(group); 282 } 283 } 284 285 return groupedRelatedPurchaseOrderViews; 286 } 287 288 /** 289 * @see org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocument#getRelatedPaymentRequestViews() 290 */ 291 public List<PaymentRequestView> getRelatedPaymentRequestViews() { 292 relatedPaymentRequestViews = updateRelatedView(PaymentRequestView.class, relatedPaymentRequestViews, true); 293 return relatedPaymentRequestViews; 294 } 295 296 /** 297 * @see org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocument#getRelatedCreditMemoViews() 298 */ 299 public List<CreditMemoView> getRelatedCreditMemoViews() { 300 relatedCreditMemoViews = updateRelatedView(CreditMemoView.class, relatedCreditMemoViews, true); 301 return relatedCreditMemoViews; 302 } 303 304 305 /** 306 * @see org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocument#getRelatedPaymentRequestViews() 307 */ 308 public List<InvoiceView> getRelatedInvoiceViews() { 309 relatedInvoiceViews = updateRelatedView(InvoiceView.class, relatedInvoiceViews, true); 310 return relatedInvoiceViews; 311 } 312 313 /** 314 * Gets the Payment History Payment Request Views for this document. 315 * 316 * @return the list of Payment History Payment Request Views. 317 */ 318 public List<PaymentRequestView> getPaymentHistoryPaymentRequestViews() { 319 paymentHistoryPaymentRequestViews = updateRelatedView(PaymentRequestView.class, paymentHistoryPaymentRequestViews, false); 320 return paymentHistoryPaymentRequestViews; 321 } 322 323 /** 324 * Gets the Payment History Credit Memo Views for this document. 325 * 326 * @return the list of Payment History Credit Memo Views. 327 */ 328 public List<CreditMemoView> getPaymentHistoryCreditMemoViews() { 329 paymentHistoryCreditMemoViews = updateRelatedView(CreditMemoView.class, paymentHistoryCreditMemoViews, false); 330 return paymentHistoryCreditMemoViews; 331 } 332 333 /** 334 * Gets the Payment History Invoice Views for this document. 335 * 336 * @return the list of Payment History Invoice Views. 337 */ 338 public List<InvoiceView> getPaymentHistoryInvoiceViews() { 339 paymentHistoryInvoiceViews = updateRelatedView(InvoiceView.class, paymentHistoryInvoiceViews, false); 340 return paymentHistoryInvoiceViews; 341 } 342 343 /** 344 * @see org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocument#getRelatedRequisitionViews() 345 */ 346 public List<LineItemReceivingView> getRelatedLineItemReceivingViews() { 347 relatedLineItemReceivingViews = updateRelatedView(LineItemReceivingView.class, relatedLineItemReceivingViews, true); 348 return relatedLineItemReceivingViews; 349 } 350 351 /** 352 * @see org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocument#getRelatedRequisitionViews() 353 */ 354 public List<CorrectionReceivingView> getRelatedCorrectionReceivingViews() { 355 relatedCorrectionReceivingViews = updateRelatedView(CorrectionReceivingView.class, relatedCorrectionReceivingViews, true); 356 return relatedCorrectionReceivingViews; 357 } 358 359 public List<BulkReceivingView> getRelatedBulkReceivingViews() { 360 relatedBulkReceivingViews = updateRelatedView(BulkReceivingView.class, relatedBulkReceivingViews, true); 361 return relatedBulkReceivingViews; 362 } 363 364 /** 365 * Groups related LineItemReceivingView and its CorrectionReceivingViews, with more recent receiving groups in the front; 366 * and within each group, with more recent corrections in the front. 367 * 368 * @return A list of ReceivingCorrectionViewGroups. 369 */ 370 public List<ReceivingViewGroup> getGroupedRelatedReceivingViews() { 371 if (groupedRelatedReceivingViews != null) { 372 return groupedRelatedReceivingViews; 373 } 374 375 groupedRelatedReceivingViews = new ArrayList<ReceivingViewGroup>(); 376 PurapService purapService = SpringContext.getBean(PurapService.class); 377 List<LineItemReceivingView> liviews = purapService.getRelatedViews(LineItemReceivingView.class, accountsPayablePurchasingDocumentLinkIdentifier); 378 List<CorrectionReceivingView> crviews = purapService.getRelatedViews(CorrectionReceivingView.class, accountsPayablePurchasingDocumentLinkIdentifier); 379 380 // both LineItemReceivingViews and CorrectionReceivingViews are already in order with most recent first, so no need to sort 381 for (LineItemReceivingView liview : liviews) { 382 ReceivingViewGroup group = new ReceivingViewGroup(); 383 group.lineItemView = liview; // could be current document 384 for (CorrectionReceivingView crview : crviews) { 385 if (StringUtils.equals(crview.getLineItemReceivingDocumentNumber(), liview.getDocumentNumber()) && 386 !documentNumber.equals(crview.getDocumentNumber())) {// exclude current document 387 group.addCorrectionView(crview); 388 } 389 } 390 groupedRelatedReceivingViews.add(group); 391 } 392 393 return groupedRelatedReceivingViews; 394 } 395 396 /** 397 * A container for a List<PurchaseOrderView>, to be used by a nested c:forEach tag 398 * in relatedPurchaseOrderDocumentsDetail.tag. 399 */ 400 public class PurchaseOrderViewGroup { 401 protected List<PurchaseOrderView> views = new ArrayList<PurchaseOrderView>(); 402 403 protected PurchaseOrderViewGroup() { 404 } 405 406 public List<PurchaseOrderView> getViews() { 407 return views; 408 } 409 } 410 411 /** 412 * A container for a LineItemReceivingView and a list of its associated CorrectionReceivingViews. 413 */ 414 public class ReceivingViewGroup { 415 protected LineItemReceivingView lineItemView; 416 protected List<CorrectionReceivingView> correctionViews = new ArrayList<CorrectionReceivingView>(); 417 418 protected ReceivingViewGroup() { 419 } 420 421 public LineItemReceivingView getLineItemView() { 422 return lineItemView; 423 } 424 425 public List<CorrectionReceivingView> getCorrectionViews() { 426 return correctionViews; 427 } 428 429 public void addCorrectionView(CorrectionReceivingView correctionView) { 430 correctionViews.add(correctionView); 431 } 432 433 public boolean getIsLineItemViewCurrentDocument() { 434 return (lineItemView != null && documentNumber.equals(lineItemView.getDocumentNumber())); 435 } 436 } 437 438}