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.apache.commons.lang.builder.ToStringBuilder; 020import org.apache.commons.lang.enums.Enum; 021import org.apache.log4j.Logger; 022import org.kuali.ole.module.purap.PurapConstants; 023import org.kuali.ole.module.purap.businessobject.PurApAccountingLine; 024import org.kuali.ole.module.purap.businessobject.PurchaseOrderItem; 025import org.kuali.ole.module.purap.businessobject.ReceivingThreshold; 026import org.kuali.ole.module.purap.document.PurchaseOrderDocument; 027import org.kuali.ole.module.purap.document.service.ThresholdService; 028import org.kuali.ole.module.purap.service.PurapAccountingService; 029import org.kuali.ole.sys.OLEPropertyConstants; 030import org.kuali.ole.sys.context.SpringContext; 031import org.kuali.rice.core.api.util.type.KualiDecimal; 032 033import java.util.*; 034 035/** 036 * A helper class to decide whether to set the receiving document required flag for a purchasing document or not. 037 */ 038public class ThresholdHelper { 039 040 //////////////////////////////////////////////////////////////////////// 041 //CLASS VARIABLES 042 //////////////////////////////////////////////////////////////////////// 043 private static Logger LOG = Logger.getLogger(ThresholdHelper.class); 044 045 public static final ThresholdCriteria CHART = new ThresholdCriteria("CHART"); 046 public static final ThresholdCriteria CHART_AND_ACCOUNTTYPE = new ThresholdCriteria("CHART_AND_ACCOUNT-TYPE"); 047 public static final ThresholdCriteria CHART_AND_SUBFUND = new ThresholdCriteria("CHART_AND_SUB-FUND"); 048 public static final ThresholdCriteria CHART_AND_COMMODITYCODE = new ThresholdCriteria("CHART_AND_COMMODITY-CODE"); 049 public static final ThresholdCriteria CHART_AND_OBJECTCODE = new ThresholdCriteria("CHART_AND_OBJECT-CODE"); 050 public static final ThresholdCriteria CHART_AND_ORGANIZATIONCODE = new ThresholdCriteria("CHART_AND_ORGANIZATION-CODE"); 051 public static final ThresholdCriteria CHART_AND_VENDOR = new ThresholdCriteria("CHART_AND_VENDOR"); 052 053 //////////////////////////////////////////////////////////////////////// 054 //INSTANCE VARIABLES 055 //////////////////////////////////////////////////////////////////////// 056 private PurapAccountingService purapAccountingService; 057 private ThresholdService thresholdService; 058 059 private List<ThresholdSummary> chartCodeSummary = new ArrayList(); 060 private List<ThresholdSummary> chartCodeAndFundSummary = new ArrayList(); 061 private List<ThresholdSummary> chartCodeAndSubFundSummary = new ArrayList(); 062 private List<ThresholdSummary> chartCodeAndCommodityCodeSummary = new ArrayList(); 063 private List<ThresholdSummary> chartCodeAndObjectCodeSummary = new ArrayList(); 064 private List<ThresholdSummary> chartCodeAndOrgCodeSummary = new ArrayList(); 065 private List<ThresholdSummary> chartCodeAndVendorSummary = new ArrayList(); 066 067 private ThresholdSummary thresholdSummary; 068 private ReceivingThreshold receivingThreshold; 069 070 private boolean allItemsNonQty; 071 072 public ThresholdHelper(PurchaseOrderDocument document) { 073 purapAccountingService = SpringContext.getBean(PurapAccountingService.class); 074 thresholdService = SpringContext.getBean(ThresholdService.class); 075 setupForThresholdCheck(document); 076 } 077 078 private void setupForThresholdCheck(PurchaseOrderDocument document) { 079 080 allItemsNonQty = checkForNonQtyItems(document); 081 082 if (allItemsNonQty) { 083 return; 084 } 085 086 List<SummaryAccount> accounts = purapAccountingService.generateSummaryAccounts(document); 087 088 if (accounts != null) { 089 090 for (SummaryAccount account : accounts) { 091 092 updateThresholdSummary(CHART, account); 093 updateThresholdSummary(CHART_AND_ACCOUNTTYPE, account); 094 updateThresholdSummary(CHART_AND_SUBFUND, account); 095 updateThresholdSummary(CHART_AND_OBJECTCODE, account); 096 updateThresholdSummary(CHART_AND_ORGANIZATIONCODE, account); 097 098 processVendorForThresholdSummary(account, 099 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 485final 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