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.apache.commons.lang.builder.ToStringBuilder;
20  import org.apache.commons.lang.enums.Enum;
21  import org.apache.log4j.Logger;
22  import org.kuali.ole.module.purap.PurapConstants;
23  import org.kuali.ole.module.purap.businessobject.PurApAccountingLine;
24  import org.kuali.ole.module.purap.businessobject.PurchaseOrderItem;
25  import org.kuali.ole.module.purap.businessobject.ReceivingThreshold;
26  import org.kuali.ole.module.purap.document.PurchaseOrderDocument;
27  import org.kuali.ole.module.purap.document.service.ThresholdService;
28  import org.kuali.ole.module.purap.service.PurapAccountingService;
29  import org.kuali.ole.sys.OLEPropertyConstants;
30  import org.kuali.ole.sys.context.SpringContext;
31  import org.kuali.rice.core.api.util.type.KualiDecimal;
32  
33  import java.util.*;
34  
35  /**
36   * A helper class to decide whether to set the receiving document required flag for a purchasing document or not.
37   */
38  public class ThresholdHelper {
39  
40      ////////////////////////////////////////////////////////////////////////
41      //CLASS VARIABLES
42      ////////////////////////////////////////////////////////////////////////
43      private static Logger LOG = Logger.getLogger(ThresholdHelper.class);
44  
45      public static final ThresholdCriteria CHART = new ThresholdCriteria("CHART");
46      public static final ThresholdCriteria CHART_AND_ACCOUNTTYPE = new ThresholdCriteria("CHART_AND_ACCOUNT-TYPE");
47      public static final ThresholdCriteria CHART_AND_SUBFUND = new ThresholdCriteria("CHART_AND_SUB-FUND");
48      public static final ThresholdCriteria CHART_AND_COMMODITYCODE = new ThresholdCriteria("CHART_AND_COMMODITY-CODE");
49      public static final ThresholdCriteria CHART_AND_OBJECTCODE = new ThresholdCriteria("CHART_AND_OBJECT-CODE");
50      public static final ThresholdCriteria CHART_AND_ORGANIZATIONCODE = new ThresholdCriteria("CHART_AND_ORGANIZATION-CODE");
51      public static final ThresholdCriteria CHART_AND_VENDOR = new ThresholdCriteria("CHART_AND_VENDOR");
52  
53      ////////////////////////////////////////////////////////////////////////
54      //INSTANCE VARIABLES
55      ////////////////////////////////////////////////////////////////////////
56      private PurapAccountingService purapAccountingService;
57      private ThresholdService thresholdService;
58  
59      private List<ThresholdSummary> chartCodeSummary = new ArrayList();
60      private List<ThresholdSummary> chartCodeAndFundSummary = new ArrayList();
61      private List<ThresholdSummary> chartCodeAndSubFundSummary = new ArrayList();
62      private List<ThresholdSummary> chartCodeAndCommodityCodeSummary = new ArrayList();
63      private List<ThresholdSummary> chartCodeAndObjectCodeSummary = new ArrayList();
64      private List<ThresholdSummary> chartCodeAndOrgCodeSummary = new ArrayList();
65      private List<ThresholdSummary> chartCodeAndVendorSummary = new ArrayList();
66  
67      private ThresholdSummary thresholdSummary;
68      private ReceivingThreshold receivingThreshold;
69  
70      private boolean allItemsNonQty;
71  
72      public ThresholdHelper(PurchaseOrderDocument document) {
73          purapAccountingService = SpringContext.getBean(PurapAccountingService.class);
74          thresholdService = SpringContext.getBean(ThresholdService.class);
75          setupForThresholdCheck(document);
76      }
77  
78      private void setupForThresholdCheck(PurchaseOrderDocument document) {
79  
80          allItemsNonQty = checkForNonQtyItems(document);
81  
82          if (allItemsNonQty) {
83              return;
84          }
85  
86          List<SummaryAccount> accounts = purapAccountingService.generateSummaryAccounts(document);
87  
88          if (accounts != null) {
89  
90              for (SummaryAccount account : accounts) {
91  
92                  updateThresholdSummary(CHART, account);
93                  updateThresholdSummary(CHART_AND_ACCOUNTTYPE, account);
94                  updateThresholdSummary(CHART_AND_SUBFUND, account);
95                  updateThresholdSummary(CHART_AND_OBJECTCODE, account);
96                  updateThresholdSummary(CHART_AND_ORGANIZATIONCODE, account);
97  
98                  processVendorForThresholdSummary(account,
99                          document.getVendorHeaderGeneratedIdentifier().toString(),
100                         document.getVendorDetailAssignedIdentifier().toString());
101 
102             }
103         }
104 
105         processCommodityCodeForThreshold(document.getItems());
106 
107     }
108 
109     private boolean checkForNonQtyItems(PurchaseOrderDocument document) {
110         List<PurchaseOrderItem> items = document.getItems();
111 
112         for (int i = 0; i < items.size(); i++) {
113             if (!items.get(i).getItemType().isAdditionalChargeIndicator() &&
114                     !StringUtils.equals(items.get(i).getItemTypeCode(), PurapConstants.ItemTypeCodes.ITEM_TYPE_SERVICE_CODE)) {
115                 return false;
116             }
117         }
118         return true;
119     }
120 
121     private void updateThresholdSummary(ThresholdCriteria thresholdCriteria,
122                                         SummaryAccount account) {
123 
124         if (thresholdCriteria != CHART_AND_COMMODITYCODE &&
125                 thresholdCriteria != CHART_AND_VENDOR) {
126 
127             ThresholdSummary thresholdSummary = new ThresholdSummary(thresholdCriteria);
128             thresholdSummary.setProperty(ThresholdField.CHART_OF_ACCOUNTS_CODE,
129                     account.getAccount().getChartOfAccountsCode());
130 
131             if (thresholdCriteria == CHART_AND_ACCOUNTTYPE) {
132                 account.getAccount().refreshReferenceObject(OLEPropertyConstants.ACCOUNT);
133                 if (StringUtils.isEmpty(account.getAccount().getAccount().getAccountTypeCode())) {
134                     return;
135                 }
136                 thresholdSummary.setProperty(ThresholdField.ACCOUNT_TYPE_CODE,
137                         account.getAccount().getAccount().getAccountTypeCode());
138 
139             } else if (thresholdCriteria == CHART_AND_SUBFUND) {
140                 account.getAccount().refreshReferenceObject(OLEPropertyConstants.ACCOUNT);
141                 if (StringUtils.isEmpty(account.getAccount().getAccount().getSubFundGroupCode())) {
142                     return;
143                 }
144                 thresholdSummary.setProperty(ThresholdField.SUBFUND_GROUP_CODE,
145                         account.getAccount().getAccount().getSubFundGroupCode());
146             } else if (thresholdCriteria == CHART_AND_OBJECTCODE) {
147                 if (StringUtils.isEmpty(account.getAccount().getFinancialObjectCode())) {
148                     return;
149                 }
150                 thresholdSummary.setProperty(ThresholdField.FINANCIAL_OBJECT_CODE,
151                         account.getAccount().getFinancialObjectCode());
152             } else if (thresholdCriteria == CHART_AND_ORGANIZATIONCODE) {
153                 account.getAccount().refreshReferenceObject(OLEPropertyConstants.ACCOUNT);
154                 if (StringUtils.isEmpty(account.getAccount().getAccount().getOrganizationCode())) {
155                     return;
156                 }
157                 thresholdSummary.setProperty(ThresholdField.ORGANIZATION_CODE,
158                         account.getAccount().getAccount().getOrganizationCode());
159             }
160 
161             thresholdSummary.addTotalAmount(account.getAccount().getAmount());
162             addToSummaryList(thresholdSummary);
163         }
164     }
165 
166     private void addToSummaryList(ThresholdSummary thresholdSummary) {
167 
168         List<ThresholdSummary> summaryList = getThresholdSummaryCollection(thresholdSummary.getThresholdCriteria());
169 
170         boolean matchFound = false;
171         for (int i = 0; i < summaryList.size(); i++) {
172             if (thresholdSummary.equals(summaryList.get(i))) {
173                 summaryList.get(i).addTotalAmount(thresholdSummary.getTotalAmount());
174                 matchFound = true;
175                 break;
176             }
177         }
178 
179         if (!matchFound) {
180             summaryList.add(thresholdSummary);
181         }
182     }
183 
184     private List<ThresholdSummary> getThresholdSummaryCollection(ThresholdCriteria thresholdCriteria) {
185 
186         if (thresholdCriteria == CHART) {
187             return chartCodeSummary;
188         } else if (thresholdCriteria == CHART_AND_ACCOUNTTYPE) {
189             return chartCodeAndFundSummary;
190         } else if (thresholdCriteria == CHART_AND_SUBFUND) {
191             return chartCodeAndSubFundSummary;
192         } else if (thresholdCriteria == CHART_AND_COMMODITYCODE) {
193             return chartCodeAndCommodityCodeSummary;
194         } else if (thresholdCriteria == CHART_AND_OBJECTCODE) {
195             return chartCodeAndObjectCodeSummary;
196         } else if (thresholdCriteria == CHART_AND_ORGANIZATIONCODE) {
197             return chartCodeAndOrgCodeSummary;
198         } else if (thresholdCriteria == CHART_AND_VENDOR) {
199             return chartCodeAndVendorSummary;
200         }
201 
202         throw new RuntimeException("Invalid ThresholdCriteria Enum - " + thresholdCriteria);
203     }
204 
205     private void processVendorForThresholdSummary(SummaryAccount account,
206                                                   String vendorHeaderGeneratedIdentifier,
207                                                   String vendorDetailAssignedIdentifier) {
208 
209         ThresholdSummary thresholdSummary = new ThresholdSummary(CHART_AND_VENDOR);
210         thresholdSummary.setProperty(ThresholdField.CHART_OF_ACCOUNTS_CODE, account.getAccount().getChartOfAccountsCode());
211         thresholdSummary.setProperty(ThresholdField.VENDOR_HEADER_GENERATED_ID, vendorHeaderGeneratedIdentifier);
212         thresholdSummary.setProperty(ThresholdField.VENDOR_DETAIL_ASSIGNED_ID, vendorDetailAssignedIdentifier);
213         thresholdSummary.addTotalAmount(account.getAccount().getAmount());
214 
215         addToSummaryList(thresholdSummary);
216 
217     }
218 
219     private void processCommodityCodeForThreshold(List<PurchaseOrderItem> items) {
220         if (items != null) {
221             for (PurchaseOrderItem item : items) {
222                 if (item.isItemActiveIndicator()) {
223                     List<PurApAccountingLine> accountingLines = item.getSourceAccountingLines();
224                     for (int i = 0; i < accountingLines.size(); i++) {
225                         if (StringUtils.isNotBlank(item.getPurchasingCommodityCode())) {
226                             ThresholdSummary thresholdSummary = new ThresholdSummary(CHART_AND_COMMODITYCODE);
227                             thresholdSummary.setProperty(ThresholdField.CHART_OF_ACCOUNTS_CODE, accountingLines.get(i).getChartOfAccountsCode());
228                             thresholdSummary.setProperty(ThresholdField.COMMODITY_CODE, item.getPurchasingCommodityCode());
229                             thresholdSummary.addTotalAmount(item.getExtendedPrice());
230                             addToSummaryList(thresholdSummary);
231                         }
232                     }
233                 }
234             }
235         }
236     }
237 
238     public boolean isReceivingDocumentRequired() {
239 
240         // From spec - 7. If all the line items are non-quantity do not do the threshold check. 
241         if (allItemsNonQty) {
242             return false;
243         }
244 
245         for (ThresholdCriteria thresholdEnum : ThresholdCriteria.getEnumList()) {
246             boolean result = isReceivingDocumentRequired(thresholdEnum);
247             if (result) {
248                 return true;
249             }
250         }
251 
252         return false;
253     }
254 
255     /**
256      * This method is public since it's required in the ThresholdTest class. To know the receiving required doc status for a PO,
257      * it's always better to call isReceivingDocumentRequired() instead of this method.
258      */
259     public boolean isReceivingDocumentRequired(ThresholdCriteria thresholdEnum) {
260 
261         List<ThresholdSummary> summaryList = getThresholdSummaryCollection(thresholdEnum);
262 
263         if (summaryList != null) {
264             for (ThresholdSummary summary : summaryList) {
265                 Collection collection = null;
266 
267                 if (thresholdEnum == CHART) {
268                     collection = thresholdService.findByChart(summary.getProperty(ThresholdField.CHART_OF_ACCOUNTS_CODE));
269                 } else if (thresholdEnum == CHART_AND_ACCOUNTTYPE) {
270                     collection = thresholdService.findByChartAndFund(summary.getProperty(ThresholdField.CHART_OF_ACCOUNTS_CODE),
271                             summary.getProperty(ThresholdField.ACCOUNT_TYPE_CODE));
272                 } else if (thresholdEnum == CHART_AND_SUBFUND) {
273                     collection = thresholdService.findByChartAndSubFund(summary.getProperty(ThresholdField.CHART_OF_ACCOUNTS_CODE),
274                             summary.getProperty(ThresholdField.SUBFUND_GROUP_CODE));
275                 } else if (thresholdEnum == CHART_AND_COMMODITYCODE) {
276                     collection = thresholdService.findByChartAndCommodity(summary.getProperty(ThresholdField.CHART_OF_ACCOUNTS_CODE),
277                             summary.getProperty(ThresholdField.COMMODITY_CODE));
278                 } else if (thresholdEnum == CHART_AND_OBJECTCODE) {
279                     collection = thresholdService.findByChartAndObjectCode(summary.getProperty(ThresholdField.CHART_OF_ACCOUNTS_CODE),
280                             summary.getProperty(ThresholdField.FINANCIAL_OBJECT_CODE));
281                 } else if (thresholdEnum == CHART_AND_ORGANIZATIONCODE) {
282                     collection = thresholdService.findByChartAndOrg(summary.getProperty(ThresholdField.CHART_OF_ACCOUNTS_CODE),
283                             summary.getProperty(ThresholdField.ORGANIZATION_CODE));
284                 } else if (thresholdEnum == CHART_AND_VENDOR) {
285                     collection = thresholdService.findByChartAndVendor(summary.getProperty(ThresholdField.CHART_OF_ACCOUNTS_CODE),
286                             summary.getProperty(ThresholdField.VENDOR_HEADER_GENERATED_ID),
287                             summary.getProperty(ThresholdField.VENDOR_DETAIL_ASSIGNED_ID));
288                 }
289 
290                 if (collection != null) {
291                     for (ReceivingThreshold threshold : (List<ReceivingThreshold>) collection) {
292                         if (threshold.getThresholdAmount() == null || threshold.getThresholdAmount().isLessThan(summary.getTotalAmount())) {
293                             thresholdSummary = summary;
294                             receivingThreshold = threshold;
295                             return true;
296                         }
297                     }
298                 }
299             }
300         }
301 
302         return false;
303 
304     }
305 
306     public ThresholdSummary getThresholdSummary() {
307         return thresholdSummary;
308     }
309 
310     public ReceivingThreshold getReceivingThreshold() {
311         return receivingThreshold;
312     }
313 
314     public class ThresholdSummary {
315 
316         private ThresholdCriteria thresholdCriteria;
317         private Map<ThresholdField, String> property2Value = new HashMap<ThresholdField, String>();
318         private KualiDecimal totalAmount = KualiDecimal.ZERO;
319 
320         ThresholdSummary(ThresholdCriteria thresholdCriteria) {
321             this.thresholdCriteria = thresholdCriteria;
322         }
323 
324         void setProperty(ThresholdField thresholdField,
325                          String fieldValue) {
326             if (!isValidProperty(thresholdField)) {
327                 throw new RuntimeException("Property[" + thresholdField + "] not allowed for the threshold criteria[" + thresholdCriteria + "]");
328             }
329 
330             property2Value.put(thresholdField, fieldValue);
331         }
332 
333         String getProperty(ThresholdField thresholdEnum) {
334             return property2Value.get(thresholdEnum);
335         }
336 
337         public ThresholdCriteria getThresholdCriteria() {
338             return thresholdCriteria;
339         }
340 
341         public String getThresholdCriteriaName() {
342             return thresholdCriteria.getName();
343         }
344 
345         public KualiDecimal getTotalAmount() {
346             return totalAmount;
347         }
348 
349         void addTotalAmount(KualiDecimal totalAmount) {
350             if (totalAmount != null) {
351                 this.totalAmount = this.totalAmount.add(totalAmount);
352             }
353         }
354 
355         boolean isValidProperty(ThresholdField thresholdField) {
356 
357             if (getThresholdCriteria() == CHART &&
358                     ThresholdField.CHART_OF_ACCOUNTS_CODE == thresholdField) {
359                 return true;
360             } else if ((getThresholdCriteria() == CHART_AND_ACCOUNTTYPE) &&
361                     (ThresholdField.CHART_OF_ACCOUNTS_CODE == thresholdField ||
362                             ThresholdField.ACCOUNT_TYPE_CODE == thresholdField)) {
363                 return true;
364             } else if ((getThresholdCriteria() == CHART_AND_SUBFUND) &&
365                     (ThresholdField.CHART_OF_ACCOUNTS_CODE == thresholdField ||
366                             ThresholdField.SUBFUND_GROUP_CODE == thresholdField)) {
367                 return true;
368             } else if ((getThresholdCriteria() == CHART_AND_COMMODITYCODE) &&
369                     (ThresholdField.CHART_OF_ACCOUNTS_CODE == thresholdField ||
370                             ThresholdField.COMMODITY_CODE == thresholdField)) {
371                 return true;
372             } else if ((getThresholdCriteria() == CHART_AND_OBJECTCODE) &&
373                     (ThresholdField.CHART_OF_ACCOUNTS_CODE == thresholdField ||
374                             ThresholdField.FINANCIAL_OBJECT_CODE == thresholdField)) {
375                 return true;
376             } else if ((getThresholdCriteria() == CHART_AND_ORGANIZATIONCODE) &&
377                     (ThresholdField.CHART_OF_ACCOUNTS_CODE == thresholdField ||
378                             ThresholdField.ORGANIZATION_CODE == thresholdField)) {
379                 return true;
380             } else if ((getThresholdCriteria() == CHART_AND_VENDOR) &&
381                     (ThresholdField.CHART_OF_ACCOUNTS_CODE == thresholdField ||
382                             ThresholdField.VENDOR_HEADER_GENERATED_ID == thresholdField ||
383                             ThresholdField.VENDOR_DETAIL_ASSIGNED_ID == thresholdField)) {
384                 return true;
385             }
386 
387             return false;
388         }
389 
390         @Override
391         public boolean equals(Object obj) {
392 
393             if (obj != null) {
394 
395                 if (!(obj instanceof ThresholdSummary)) {
396                     return false;
397                 }
398 
399                 ThresholdSummary thresholdItem = (ThresholdSummary) obj;
400 
401                 if (getThresholdCriteria() == thresholdItem.getThresholdCriteria()) {
402 
403                     if (getThresholdCriteria() == CHART) {
404 
405                         if (StringUtils.equals(property2Value.get(ThresholdField.CHART_OF_ACCOUNTS_CODE),
406                                 thresholdItem.getProperty(ThresholdField.CHART_OF_ACCOUNTS_CODE))) {
407                             return true;
408                         }
409 
410                     } else if (getThresholdCriteria() == CHART_AND_ACCOUNTTYPE) {
411 
412                         if (StringUtils.equals(property2Value.get(ThresholdField.CHART_OF_ACCOUNTS_CODE),
413                                 thresholdItem.getProperty(ThresholdField.CHART_OF_ACCOUNTS_CODE)) &&
414                                 StringUtils.equals(property2Value.get(ThresholdField.ACCOUNT_TYPE_CODE),
415                                         thresholdItem.getProperty(ThresholdField.ACCOUNT_TYPE_CODE))) {
416                             return true;
417                         }
418 
419                     } else if (getThresholdCriteria() == CHART_AND_SUBFUND) {
420 
421                         if (StringUtils.equals(property2Value.get(ThresholdField.CHART_OF_ACCOUNTS_CODE),
422                                 thresholdItem.getProperty(ThresholdField.CHART_OF_ACCOUNTS_CODE)) &&
423                                 StringUtils.equals(property2Value.get(ThresholdField.SUBFUND_GROUP_CODE),
424                                         thresholdItem.getProperty(ThresholdField.SUBFUND_GROUP_CODE))) {
425                             return true;
426                         }
427 
428                     } else if (getThresholdCriteria() == CHART_AND_COMMODITYCODE) {
429 
430                         if (StringUtils.equals(property2Value.get(ThresholdField.CHART_OF_ACCOUNTS_CODE),
431                                 thresholdItem.getProperty(ThresholdField.CHART_OF_ACCOUNTS_CODE)) &&
432                                 StringUtils.equals(property2Value.get(ThresholdField.COMMODITY_CODE),
433                                         thresholdItem.getProperty(ThresholdField.COMMODITY_CODE))) {
434                             return true;
435                         }
436 
437                     } else if (getThresholdCriteria() == CHART_AND_OBJECTCODE) {
438 
439                         if (StringUtils.equals(property2Value.get(ThresholdField.CHART_OF_ACCOUNTS_CODE),
440                                 thresholdItem.getProperty(ThresholdField.CHART_OF_ACCOUNTS_CODE)) &&
441                                 StringUtils.equals(property2Value.get(ThresholdField.FINANCIAL_OBJECT_CODE),
442                                         thresholdItem.getProperty(ThresholdField.FINANCIAL_OBJECT_CODE))) {
443                             return true;
444                         }
445 
446                     } else if (getThresholdCriteria() == CHART_AND_ORGANIZATIONCODE) {
447 
448                         if (StringUtils.equals(property2Value.get(ThresholdField.CHART_OF_ACCOUNTS_CODE),
449                                 thresholdItem.getProperty(ThresholdField.CHART_OF_ACCOUNTS_CODE)) &&
450                                 StringUtils.equals(property2Value.get(ThresholdField.ORGANIZATION_CODE),
451                                         thresholdItem.getProperty(ThresholdField.ORGANIZATION_CODE))) {
452                             return true;
453                         }
454 
455                     } else if (getThresholdCriteria() == CHART_AND_VENDOR) {
456 
457                         if (StringUtils.equals(property2Value.get(ThresholdField.CHART_OF_ACCOUNTS_CODE),
458                                 thresholdItem.getProperty(ThresholdField.CHART_OF_ACCOUNTS_CODE)) &&
459                                 StringUtils.equals(property2Value.get(ThresholdField.VENDOR_HEADER_GENERATED_ID),
460                                         thresholdItem.getProperty(ThresholdField.VENDOR_HEADER_GENERATED_ID)) &&
461                                 StringUtils.equals(property2Value.get(ThresholdField.VENDOR_DETAIL_ASSIGNED_ID),
462                                         thresholdItem.getProperty(ThresholdField.VENDOR_DETAIL_ASSIGNED_ID))) {
463                             return true;
464                         }
465 
466                     }
467                 }
468             }
469             return false;
470         }
471 
472         @Override
473         public String toString() {
474             ToStringBuilder stringBuilder = new ToStringBuilder(this);
475             stringBuilder.append("ThresholdCriteria", getThresholdCriteria().getName());
476             stringBuilder.append("Amount", getTotalAmount());
477             stringBuilder.append("Field2Values", property2Value);
478 
479             return stringBuilder.toString();
480         }
481     }
482 
483 }
484 
485 final class ThresholdCriteria extends Enum {
486 
487     ThresholdCriteria(String name) {
488         super(name);
489     }
490 
491     public static List<ThresholdCriteria> getEnumList() {
492         return getEnumList(ThresholdCriteria.class);
493     }
494 }
495