001    /*
002     * Copyright 2011 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     */
016    package org.kuali.kfs.module.purap.util;
017    
018    import java.util.ArrayList;
019    import java.util.Collections;
020    import java.util.Comparator;
021    import java.util.List;
022    
023    import org.apache.commons.lang.StringUtils;
024    import org.kuali.kfs.module.purap.businessobject.AbstractRelatedView;
025    import org.kuali.kfs.module.purap.businessobject.BulkReceivingView;
026    import org.kuali.kfs.module.purap.businessobject.CorrectionReceivingView;
027    import org.kuali.kfs.module.purap.businessobject.CreditMemoView;
028    import org.kuali.kfs.module.purap.businessobject.ElectronicInvoiceRejectView;
029    import org.kuali.kfs.module.purap.businessobject.LineItemReceivingView;
030    import org.kuali.kfs.module.purap.businessobject.PaymentRequestView;
031    import org.kuali.kfs.module.purap.businessobject.PurchaseOrderView;
032    import org.kuali.kfs.module.purap.businessobject.RequisitionView;
033    import org.kuali.kfs.module.purap.document.service.PurapService;
034    import org.kuali.kfs.sys.context.SpringContext;
035    
036    public class PurApRelatedViews {
037        private String documentNumber;
038        private Integer accountsPayablePurchasingDocumentLinkIdentifier;
039        
040        private transient List<RequisitionView> relatedRequisitionViews;
041        private transient List<PurchaseOrderView> relatedPurchaseOrderViews;
042        private transient List<PaymentRequestView> relatedPaymentRequestViews;
043        private transient List<PaymentRequestView> paymentHistoryPaymentRequestViews;
044        private transient List<CreditMemoView> relatedCreditMemoViews;
045        private transient List<CreditMemoView> paymentHistoryCreditMemoViews;
046        private transient List<LineItemReceivingView> relatedLineItemReceivingViews;
047        private transient List<CorrectionReceivingView> relatedCorrectionReceivingViews;
048        private transient List<BulkReceivingView> relatedBulkReceivingViews;    
049        private transient List<PurchaseOrderViewGroup> groupedRelatedPurchaseOrderViews;
050        private transient List<ReceivingViewGroup> groupedRelatedReceivingViews;
051        private transient List<ElectronicInvoiceRejectView> relatedRejectViews;
052        
053        public PurApRelatedViews(String documentNumber, Integer accountsPayablePurchasingDocumentLinkIdentifier) {
054            super();
055            this.documentNumber = documentNumber;
056            this.accountsPayablePurchasingDocumentLinkIdentifier = accountsPayablePurchasingDocumentLinkIdentifier;
057        }
058    
059        /**
060         * Reset all related view lists to null. 
061         */
062        public void resetRelatedViews() {
063            relatedRequisitionViews = null;
064            relatedPurchaseOrderViews = null;
065            relatedPaymentRequestViews = null;
066            paymentHistoryPaymentRequestViews = null;
067            relatedCreditMemoViews = null;
068            paymentHistoryCreditMemoViews = null;
069            relatedLineItemReceivingViews = null;
070            relatedCorrectionReceivingViews = null;
071            relatedBulkReceivingViews = null;
072            groupedRelatedPurchaseOrderViews = null;
073            groupedRelatedReceivingViews = null;
074            relatedRejectViews = null;
075        }
076        
077        public List updateRelatedView(Class<?> clazz, List<? extends AbstractRelatedView> relatedList, boolean removeCurrentDocument) {
078            if (relatedList == null) {
079                relatedList = SpringContext.getBean(PurapService.class).getRelatedViews(clazz, accountsPayablePurchasingDocumentLinkIdentifier);
080                if (removeCurrentDocument) {
081                    for (AbstractRelatedView view : relatedList) {
082                        if (documentNumber.equals(view.getDocumentNumber())) {
083                            relatedList.remove(view);
084                            break;
085                        }
086                    }
087                }
088            }
089            return relatedList;
090        }
091    
092        /**
093         * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument#getRelatedRequisitionViews()
094         */
095        public List<RequisitionView> getRelatedRequisitionViews() {
096            relatedRequisitionViews = updateRelatedView(RequisitionView.class, relatedRequisitionViews, true);
097            return relatedRequisitionViews;
098        }
099    
100        public List<ElectronicInvoiceRejectView> getRelatedRejectViews() {
101            relatedRejectViews = updateRelatedView(ElectronicInvoiceRejectView.class, relatedRejectViews, true);
102            return relatedRejectViews;
103        }
104        
105        /**
106         * Obtains a list of related PurchaseOrderViews, first ordered by POIDs descending, then by document numbers descending;
107         * thus POs with newer POIDs will be in the front, and within the same POID, the current PO will be in the front. 
108         * 
109         * @return  A list of <PurchaseOrderView> with newer POs in the front.
110         */
111        public List<PurchaseOrderView> getRelatedPurchaseOrderViews() {
112            if (relatedPurchaseOrderViews != null)
113                return relatedPurchaseOrderViews;
114            
115            // Obtain a list which is sorted by workflow document ID descending.
116            relatedPurchaseOrderViews = updateRelatedView(PurchaseOrderView.class, relatedPurchaseOrderViews, true);
117            
118            // Sort the list.
119            Collections.sort(relatedPurchaseOrderViews, 
120                    new Comparator<PurchaseOrderView>() {
121                        public int compare(PurchaseOrderView v1, PurchaseOrderView v2) {
122                            if ((v1 != null) && (v2 != null) && 
123                                (v1.getPurapDocumentIdentifier() != null) &&
124                                (v2.getPurapDocumentIdentifier() != null)) {
125                                // sort by POID descending
126                                int compare = -v1.getPurapDocumentIdentifier().compareTo(v2.getPurapDocumentIdentifier());                            
127                                // if POIDs are the same, sort by document number descending; usually current PO has biggest documentNumber
128                                if (compare == 0) {
129                                    compare = v1.getPurchaseOrderCurrentIndicator() ? -1 :
130                                        v2.getPurchaseOrderCurrentIndicator() ? 1 : 
131                                        -v1.getDocumentNumber().compareTo(v2.getDocumentNumber());
132                                }
133                                return compare;
134                            }
135                            return 0;
136                        }
137                    }
138            );
139            
140            return relatedPurchaseOrderViews;        
141        }
142        
143        /**
144         * Groups related PurchaseOrderViews by POIDs descending, and within each group order POs by document numbers descending;
145         * thus groups of newer POIDs will be in the front, and within each group, more current POs will be in the front. 
146         * 
147         * @return  A list of <PurchaseOrderViewGroup> with newer POs in the front.
148         * @see org.kuali.kfs.module.purap.util.PurApRelatedViews.getRelatedPurchaseOrderViews
149         * @see org.kuali.kfs.module.purap.businessobject.PurchaseOrderView
150         */
151        public List<PurchaseOrderViewGroup> getGroupedRelatedPurchaseOrderViews() {
152            if (groupedRelatedPurchaseOrderViews != null)
153                return groupedRelatedPurchaseOrderViews;
154            
155            /*
156             * This extra layer of grouping is necessary in order to display the notes for a group of 
157             * related POChange documents (which should have identical POID) after that group, 
158             * and before any other related groups which may result from PO splitting (with different POIDs).  
159             * With direct use of relatedPurchaseOrderViews, location of the end of the group is problematic.
160             */        
161            groupedRelatedPurchaseOrderViews = new ArrayList<PurchaseOrderViewGroup>();
162            PurchaseOrderViewGroup group = new PurchaseOrderViewGroup();
163            int previousPOID = 0;
164            relatedPurchaseOrderViews = getRelatedPurchaseOrderViews();
165            for(PurchaseOrderView view : relatedPurchaseOrderViews) {
166                if (previousPOID == 0) {
167                    previousPOID = view.getPurapDocumentIdentifier();
168                    
169                }
170                if( view.getPurapDocumentIdentifier() == previousPOID ) {
171                    group.getViews().add(view);
172                }
173                else {
174                    groupedRelatedPurchaseOrderViews.add(group);
175                    group = new PurchaseOrderViewGroup();
176                    group.getViews().add(view);
177                    previousPOID = view.getPurapDocumentIdentifier();
178                }
179                if (relatedPurchaseOrderViews.size() == relatedPurchaseOrderViews.indexOf(view) + 1) {
180                    groupedRelatedPurchaseOrderViews.add(group);
181                }
182            }
183            
184            return groupedRelatedPurchaseOrderViews;    
185        }    
186        
187        /**
188         * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument#getRelatedPaymentRequestViews()
189         */
190        public List<PaymentRequestView> getRelatedPaymentRequestViews() {
191            relatedPaymentRequestViews = updateRelatedView(PaymentRequestView.class, relatedPaymentRequestViews, true);
192            return relatedPaymentRequestViews;
193        }
194    
195        /**
196         * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument#getRelatedCreditMemoViews()
197         */
198        public List<CreditMemoView> getRelatedCreditMemoViews() {
199            relatedCreditMemoViews = updateRelatedView(CreditMemoView.class, relatedCreditMemoViews, true);
200            return relatedCreditMemoViews;
201        }
202    
203        /**
204         * Gets the Payment History Payment Request Views for this document.
205         * 
206         * @return the list of Payment History Payment Request Views.
207         */
208        public List<PaymentRequestView> getPaymentHistoryPaymentRequestViews() {
209            paymentHistoryPaymentRequestViews = updateRelatedView(PaymentRequestView.class, paymentHistoryPaymentRequestViews, false);
210            return paymentHistoryPaymentRequestViews;
211        }
212    
213        /**
214         * Gets the Payment History Credit Memo Views for this document.
215         * 
216         * @return the list of Payment History Credit Memo Views.
217         */
218        public List<CreditMemoView> getPaymentHistoryCreditMemoViews() {
219            paymentHistoryCreditMemoViews = updateRelatedView(CreditMemoView.class, paymentHistoryCreditMemoViews, false);
220            return paymentHistoryCreditMemoViews;
221        }
222    
223        /**
224         * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument#getRelatedRequisitionViews()
225         */
226        public List<LineItemReceivingView> getRelatedLineItemReceivingViews() {
227            relatedLineItemReceivingViews = updateRelatedView(LineItemReceivingView.class, relatedLineItemReceivingViews, true);
228            return relatedLineItemReceivingViews;
229        }
230    
231        /**
232         * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument#getRelatedRequisitionViews()
233         */
234        public List<CorrectionReceivingView> getRelatedCorrectionReceivingViews() {
235            relatedCorrectionReceivingViews = updateRelatedView(CorrectionReceivingView.class, relatedCorrectionReceivingViews, true);
236            return relatedCorrectionReceivingViews;
237        }
238        
239        public List<BulkReceivingView> getRelatedBulkReceivingViews() {
240            relatedBulkReceivingViews = updateRelatedView(BulkReceivingView.class, relatedBulkReceivingViews, true);
241            return relatedBulkReceivingViews;
242        }
243        
244        /**
245         * Groups related LineItemReceivingView and its CorrectionReceivingViews, with more recent receiving groups in the front;
246         * and within each group, with more recent corrections in the front. 
247         * 
248         * @return  A list of ReceivingCorrectionViewGroups.
249         */
250        public List<ReceivingViewGroup> getGroupedRelatedReceivingViews() {
251            if (groupedRelatedReceivingViews != null)
252                return groupedRelatedReceivingViews;
253            
254            groupedRelatedReceivingViews = new ArrayList<ReceivingViewGroup>();
255            PurapService purapService = SpringContext.getBean(PurapService.class);
256            List<LineItemReceivingView> liviews = purapService.getRelatedViews(LineItemReceivingView.class, accountsPayablePurchasingDocumentLinkIdentifier);
257            List<CorrectionReceivingView> crviews = purapService.getRelatedViews(CorrectionReceivingView.class, accountsPayablePurchasingDocumentLinkIdentifier);
258            
259            // both LineItemReceivingViews and CorrectionReceivingViews are already in order with most recent first, so no need to sort
260            for (LineItemReceivingView liview : liviews) {
261                ReceivingViewGroup group = new ReceivingViewGroup(); 
262                group.lineItemView = liview; // could be current document
263                for (CorrectionReceivingView crview : crviews) {
264                    if (StringUtils.equals(crview.getLineItemReceivingDocumentNumber(), liview.getDocumentNumber()) && 
265                            !documentNumber.equals(crview.getDocumentNumber())) {// exclude current document                
266                        group.addCorrectionView(crview);
267                    }
268                }
269                groupedRelatedReceivingViews.add(group);
270            }
271                    
272            return groupedRelatedReceivingViews;    
273        }        
274    
275        /**
276         * A container for a List<PurchaseOrderView>, to be used by a nested c:forEach tag
277         * in relatedPurchaseOrderDocumentsDetail.tag.
278         */
279        public class PurchaseOrderViewGroup {
280            protected List<PurchaseOrderView> views = new ArrayList<PurchaseOrderView>();
281            
282            protected PurchaseOrderViewGroup() {
283            }
284    
285            public List<PurchaseOrderView> getViews() {
286                return views;
287            }
288        }
289        
290        /**
291         * A container for a LineItemReceivingView and a list of its associated CorrectionReceivingViews.
292         */
293        public class ReceivingViewGroup {
294            protected LineItemReceivingView lineItemView;
295            protected List<CorrectionReceivingView> correctionViews = new ArrayList<CorrectionReceivingView>();
296            
297            protected ReceivingViewGroup() {
298            }
299    
300            public LineItemReceivingView getLineItemView() {
301                return lineItemView;
302            }
303    
304            public List<CorrectionReceivingView> getCorrectionViews() {
305                return correctionViews;
306            }
307    
308            public void addCorrectionView(CorrectionReceivingView correctionView) {
309                correctionViews.add(correctionView);
310            }
311            
312            public boolean getIsLineItemViewCurrentDocument() {
313                return (lineItemView != null && documentNumber.equals(lineItemView.getDocumentNumber()));
314            }
315        }
316        
317    }