View Javadoc
1   /*
2    * Copyright 2008 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.ole.module.purap.util;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.kuali.ole.module.purap.PurapConstants;
20  import org.kuali.ole.module.purap.PurapPropertyConstants;
21  import org.kuali.ole.module.purap.businessobject.*;
22  import org.kuali.ole.module.purap.document.PurchaseOrderDocument;
23  import org.kuali.ole.module.purap.document.service.PurapService;
24  import org.kuali.ole.sys.OLEConstants;
25  import org.kuali.ole.sys.context.SpringContext;
26  import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader;
27  import org.kuali.rice.kew.api.document.DocumentStatus;
28  import org.kuali.rice.kew.api.exception.WorkflowException;
29  import org.kuali.rice.kim.api.KimConstants;
30  import org.kuali.rice.kim.api.services.IdentityManagementService;
31  import org.kuali.rice.kns.service.DataDictionaryService;
32  import org.kuali.rice.krad.datadictionary.exception.UnknownDocumentTypeException;
33  import org.kuali.rice.krad.document.Document;
34  import org.kuali.rice.krad.service.DocumentService;
35  import org.kuali.rice.krad.service.KRADServiceLocatorInternal;
36  import org.kuali.rice.krad.util.GlobalVariables;
37  import org.kuali.rice.krad.util.ObjectUtils;
38  import org.springframework.transaction.PlatformTransactionManager;
39  import org.springframework.transaction.TransactionStatus;
40  import org.springframework.transaction.support.TransactionCallback;
41  import org.springframework.transaction.support.TransactionTemplate;
42  
43  import java.util.*;
44  
45  public class PurApRelatedViews {
46      private String documentNumber;
47      private Integer accountsPayablePurchasingDocumentLinkIdentifier;
48  
49      private transient List<RequisitionView> relatedRequisitionViews;
50      private transient List<PurchaseOrderView> relatedPurchaseOrderViews;
51      private transient List<PaymentRequestView> relatedPaymentRequestViews;
52      private transient List<PaymentRequestView> paymentHistoryPaymentRequestViews;
53      private transient List<CreditMemoView> relatedCreditMemoViews;
54      private transient List<CreditMemoView> paymentHistoryCreditMemoViews;
55      private transient List<InvoiceView> relatedInvoiceViews;
56      private transient List<InvoiceView> paymentHistoryInvoiceViews;
57      private transient List<LineItemReceivingView> relatedLineItemReceivingViews;
58      private transient List<CorrectionReceivingView> relatedCorrectionReceivingViews;
59      private transient List<BulkReceivingView> relatedBulkReceivingViews;
60      private transient List<PurchaseOrderViewGroup> groupedRelatedPurchaseOrderViews;
61      private transient List<ReceivingViewGroup> groupedRelatedReceivingViews;
62      private transient List<ElectronicInvoiceRejectView> relatedRejectViews;
63  
64      public PurApRelatedViews(String documentNumber, Integer accountsPayablePurchasingDocumentLinkIdentifier) {
65          super();
66          this.documentNumber = documentNumber;
67          this.accountsPayablePurchasingDocumentLinkIdentifier = accountsPayablePurchasingDocumentLinkIdentifier;
68      }
69  
70      /**
71       * Reset all related view lists to null.
72       */
73      public void resetRelatedViews() {
74          relatedRequisitionViews = null;
75          relatedPurchaseOrderViews = null;
76          relatedPaymentRequestViews = null;
77          paymentHistoryPaymentRequestViews = null;
78          relatedInvoiceViews = null;
79          paymentHistoryInvoiceViews = null;
80          relatedCreditMemoViews = null;
81          paymentHistoryCreditMemoViews = null;
82          relatedLineItemReceivingViews = null;
83          relatedCorrectionReceivingViews = null;
84          relatedBulkReceivingViews = null;
85          groupedRelatedPurchaseOrderViews = null;
86          groupedRelatedReceivingViews = null;
87          relatedRejectViews = null;
88      }
89  
90      private static PlatformTransactionManager transactionManager;
91  
92      public static PlatformTransactionManager getTransactionManager() {
93          if (transactionManager == null) {
94              transactionManager = GlobalResourceLoader.getService("transactionManager");
95          }
96          return transactionManager;
97      }
98  
99      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 }