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         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         if(paymentHistoryPaymentRequestViews==null){
320             paymentHistoryPaymentRequestViews = updateRelatedView(PaymentRequestView.class, paymentHistoryPaymentRequestViews, false);
321         }
322         return paymentHistoryPaymentRequestViews;
323     }
324 
325     /**
326      * Gets the Payment History Credit Memo Views for this document.
327      *
328      * @return the list of Payment History Credit Memo Views.
329      */
330     public List<CreditMemoView> getPaymentHistoryCreditMemoViews() {
331         if(paymentHistoryCreditMemoViews==null){
332             paymentHistoryCreditMemoViews = updateRelatedView(CreditMemoView.class, paymentHistoryCreditMemoViews, false);
333         }
334         return paymentHistoryCreditMemoViews;
335     }
336 
337     /**
338      * Gets the Payment History Invoice Views for this document.
339      *
340      * @return the list of Payment History Invoice Views.
341      */
342     public List<InvoiceView> getPaymentHistoryInvoiceViews() {
343         paymentHistoryInvoiceViews = updateRelatedView(InvoiceView.class, paymentHistoryInvoiceViews, false);
344         return paymentHistoryInvoiceViews;
345     }
346 
347     /**
348      * @see org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocument#getRelatedRequisitionViews()
349      */
350     public List<LineItemReceivingView> getRelatedLineItemReceivingViews() {
351         if(relatedLineItemReceivingViews==null){
352             relatedLineItemReceivingViews = updateRelatedView(LineItemReceivingView.class, relatedLineItemReceivingViews, true);
353         }
354         return relatedLineItemReceivingViews;
355     }
356 
357     /**
358      * @see org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocument#getRelatedRequisitionViews()
359      */
360     public List<CorrectionReceivingView> getRelatedCorrectionReceivingViews() {
361         if(relatedCorrectionReceivingViews == null){
362             relatedCorrectionReceivingViews = updateRelatedView(CorrectionReceivingView.class, relatedCorrectionReceivingViews, true);
363         }
364         return relatedCorrectionReceivingViews;
365     }
366 
367     public List<BulkReceivingView> getRelatedBulkReceivingViews() {
368         relatedBulkReceivingViews = updateRelatedView(BulkReceivingView.class, relatedBulkReceivingViews, true);
369         return relatedBulkReceivingViews;
370     }
371 
372     /**
373      * Groups related LineItemReceivingView and its CorrectionReceivingViews, with more recent receiving groups in the front;
374      * and within each group, with more recent corrections in the front.
375      *
376      * @return A list of ReceivingCorrectionViewGroups.
377      */
378     public List<ReceivingViewGroup> getGroupedRelatedReceivingViews() {
379         if (groupedRelatedReceivingViews != null) {
380             return groupedRelatedReceivingViews;
381         }
382 
383         groupedRelatedReceivingViews = new ArrayList<ReceivingViewGroup>();
384         PurapService purapService = SpringContext.getBean(PurapService.class);
385         List<LineItemReceivingView> liviews = purapService.getRelatedViews(LineItemReceivingView.class, accountsPayablePurchasingDocumentLinkIdentifier);
386         List<CorrectionReceivingView> crviews = purapService.getRelatedViews(CorrectionReceivingView.class, accountsPayablePurchasingDocumentLinkIdentifier);
387 
388         // both LineItemReceivingViews and CorrectionReceivingViews are already in order with most recent first, so no need to sort
389         for (LineItemReceivingView liview : liviews) {
390             ReceivingViewGroup group = new ReceivingViewGroup();
391             group.lineItemView = liview; // could be current document
392             for (CorrectionReceivingView crview : crviews) {
393                 if (StringUtils.equals(crview.getLineItemReceivingDocumentNumber(), liview.getDocumentNumber()) &&
394                         !documentNumber.equals(crview.getDocumentNumber())) {// exclude current document
395                     group.addCorrectionView(crview);
396                 }
397             }
398             groupedRelatedReceivingViews.add(group);
399         }
400 
401         return groupedRelatedReceivingViews;
402     }
403 
404     /**
405      * A container for a List<PurchaseOrderView>, to be used by a nested c:forEach tag
406      * in relatedPurchaseOrderDocumentsDetail.tag.
407      */
408     public class PurchaseOrderViewGroup {
409         protected List<PurchaseOrderView> views = new ArrayList<PurchaseOrderView>();
410 
411         protected PurchaseOrderViewGroup() {
412         }
413 
414         public List<PurchaseOrderView> getViews() {
415             return views;
416         }
417     }
418 
419     /**
420      * A container for a LineItemReceivingView and a list of its associated CorrectionReceivingViews.
421      */
422     public class ReceivingViewGroup {
423         protected LineItemReceivingView lineItemView;
424         protected List<CorrectionReceivingView> correctionViews = new ArrayList<CorrectionReceivingView>();
425 
426         protected ReceivingViewGroup() {
427         }
428 
429         public LineItemReceivingView getLineItemView() {
430             return lineItemView;
431         }
432 
433         public List<CorrectionReceivingView> getCorrectionViews() {
434             return correctionViews;
435         }
436 
437         public void addCorrectionView(CorrectionReceivingView correctionView) {
438             correctionViews.add(correctionView);
439         }
440 
441         public boolean getIsLineItemViewCurrentDocument() {
442             return (lineItemView != null && documentNumber.equals(lineItemView.getDocumentNumber()));
443         }
444     }
445 
446 }