/*
* The Kuali Financial System, a comprehensive financial management system for higher education.
*
* Copyright 2005-2014 The Kuali Foundation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.kuali.kfs.module.purap.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.enums.Enum;
import org.apache.log4j.Logger;
import org.kuali.kfs.module.purap.PurapConstants;
import org.kuali.kfs.module.purap.businessobject.PurApAccountingLine;
import org.kuali.kfs.module.purap.businessobject.PurchaseOrderItem;
import org.kuali.kfs.module.purap.businessobject.ReceivingThreshold;
import org.kuali.kfs.module.purap.document.PurchaseOrderDocument;
import org.kuali.kfs.module.purap.document.service.ThresholdService;
import org.kuali.kfs.module.purap.service.PurapAccountingService;
import org.kuali.kfs.sys.KFSPropertyConstants;
import org.kuali.kfs.sys.context.SpringContext;
import org.kuali.rice.core.api.util.type.KualiDecimal;
/**
* A helper class to decide whether to set the receiving document required flag for a purchasing document or not.
*/
public class ThresholdHelper {
////////////////////////////////////////////////////////////////////////
//CLASS VARIABLES
////////////////////////////////////////////////////////////////////////
private static Logger LOG = Logger.getLogger(ThresholdHelper.class);
public static final ThresholdCriteria CHART = new ThresholdCriteria("CHART");
public static final ThresholdCriteria CHART_AND_ACCOUNTTYPE = new ThresholdCriteria("CHART_AND_ACCOUNT-TYPE");
public static final ThresholdCriteria CHART_AND_SUBFUND = new ThresholdCriteria("CHART_AND_SUB-FUND");
public static final ThresholdCriteria CHART_AND_COMMODITYCODE = new ThresholdCriteria("CHART_AND_COMMODITY-CODE");
public static final ThresholdCriteria CHART_AND_OBJECTCODE = new ThresholdCriteria("CHART_AND_OBJECT-CODE");
public static final ThresholdCriteria CHART_AND_ORGANIZATIONCODE = new ThresholdCriteria("CHART_AND_ORGANIZATION-CODE");
public static final ThresholdCriteria CHART_AND_VENDOR = new ThresholdCriteria("CHART_AND_VENDOR");
////////////////////////////////////////////////////////////////////////
//INSTANCE VARIABLES
////////////////////////////////////////////////////////////////////////
private PurapAccountingService purapAccountingService;
private ThresholdService thresholdService;
private List<ThresholdSummary> chartCodeSummary = new ArrayList();
private List<ThresholdSummary> chartCodeAndFundSummary = new ArrayList();
private List<ThresholdSummary> chartCodeAndSubFundSummary = new ArrayList();
private List<ThresholdSummary> chartCodeAndCommodityCodeSummary = new ArrayList();
private List<ThresholdSummary> chartCodeAndObjectCodeSummary = new ArrayList();
private List<ThresholdSummary> chartCodeAndOrgCodeSummary = new ArrayList();
private List<ThresholdSummary> chartCodeAndVendorSummary = new ArrayList();
private ThresholdSummary thresholdSummary;
private ReceivingThreshold receivingThreshold;
private boolean allItemsNonQty;
public ThresholdHelper(PurchaseOrderDocument document){
purapAccountingService = SpringContext.getBean(PurapAccountingService.class);
thresholdService = SpringContext.getBean(ThresholdService.class);
setupForThresholdCheck(document);
}
private void setupForThresholdCheck(PurchaseOrderDocument document){
allItemsNonQty = checkForNonQtyItems(document);
if (allItemsNonQty){
return;
}
List<SummaryAccount> accounts = purapAccountingService.generateSummaryAccounts(document);
if (accounts != null){
for (SummaryAccount account : accounts) {
updateThresholdSummary(CHART,account);
updateThresholdSummary(CHART_AND_ACCOUNTTYPE,account);
updateThresholdSummary(CHART_AND_SUBFUND,account);
updateThresholdSummary(CHART_AND_OBJECTCODE,account);
updateThresholdSummary(CHART_AND_ORGANIZATIONCODE,account);
processVendorForThresholdSummary(account,
document.getVendorHeaderGeneratedIdentifier().toString(),
document.getVendorDetailAssignedIdentifier().toString());
}
}
processCommodityCodeForThreshold(document.getItems());
}
private boolean checkForNonQtyItems(PurchaseOrderDocument document){
List<PurchaseOrderItem> items = document.getItems();
for (int i = 0; i < items.size(); i++) {
if (!items.get(i).getItemType().isAdditionalChargeIndicator() &&
!StringUtils.equals(items.get(i).getItemTypeCode(),PurapConstants.ItemTypeCodes.ITEM_TYPE_SERVICE_CODE)){
return false;
}
}
return true;
}
private void updateThresholdSummary(ThresholdCriteria thresholdCriteria,
SummaryAccount account){
if (thresholdCriteria != CHART_AND_COMMODITYCODE &&
thresholdCriteria != CHART_AND_VENDOR){
ThresholdSummary thresholdSummary = new ThresholdSummary(thresholdCriteria);
thresholdSummary.setProperty(ThresholdField.CHART_OF_ACCOUNTS_CODE,
account.getAccount().getChartOfAccountsCode());
if (thresholdCriteria == CHART_AND_ACCOUNTTYPE){
account.getAccount().refreshReferenceObject(KFSPropertyConstants.ACCOUNT);
if (StringUtils.isEmpty(account.getAccount().getAccount().getAccountTypeCode())){
return;
}
thresholdSummary.setProperty(ThresholdField.ACCOUNT_TYPE_CODE,
account.getAccount().getAccount().getAccountTypeCode());
}else if (thresholdCriteria == CHART_AND_SUBFUND){
account.getAccount().refreshReferenceObject(KFSPropertyConstants.ACCOUNT);
if (StringUtils.isEmpty(account.getAccount().getAccount().getSubFundGroupCode())){
return;
}
thresholdSummary.setProperty(ThresholdField.SUBFUND_GROUP_CODE,
account.getAccount().getAccount().getSubFundGroupCode());
}else if (thresholdCriteria == CHART_AND_OBJECTCODE){
if (StringUtils.isEmpty(account.getAccount().getFinancialObjectCode())){
return;
}
thresholdSummary.setProperty(ThresholdField.FINANCIAL_OBJECT_CODE,
account.getAccount().getFinancialObjectCode());
}else if (thresholdCriteria == CHART_AND_ORGANIZATIONCODE){
account.getAccount().refreshReferenceObject(KFSPropertyConstants.ACCOUNT);
if (StringUtils.isEmpty(account.getAccount().getAccount().getOrganizationCode())){
return;
}
thresholdSummary.setProperty(ThresholdField.ORGANIZATION_CODE,
account.getAccount().getAccount().getOrganizationCode());
}
thresholdSummary.addTotalAmount(account.getAccount().getAmount());
addToSummaryList(thresholdSummary);
}
}
private void addToSummaryList(ThresholdSummary thresholdSummary){
List<ThresholdSummary> summaryList = getThresholdSummaryCollection(thresholdSummary.getThresholdCriteria());
boolean matchFound = false;
for (int i = 0; i < summaryList.size(); i++) {
if (thresholdSummary.equals(summaryList.get(i))){
summaryList.get(i).addTotalAmount(thresholdSummary.getTotalAmount());
matchFound = true;
break;
}
}
if (!matchFound){
summaryList.add(thresholdSummary);
}
}
private List<ThresholdSummary> getThresholdSummaryCollection(ThresholdCriteria thresholdCriteria){
if (thresholdCriteria == CHART){
return chartCodeSummary;
}else if (thresholdCriteria == CHART_AND_ACCOUNTTYPE){
return chartCodeAndFundSummary;
}else if (thresholdCriteria == CHART_AND_SUBFUND){
return chartCodeAndSubFundSummary;
}else if (thresholdCriteria == CHART_AND_COMMODITYCODE){
return chartCodeAndCommodityCodeSummary;
}else if (thresholdCriteria == CHART_AND_OBJECTCODE){
return chartCodeAndObjectCodeSummary;
}else if (thresholdCriteria == CHART_AND_ORGANIZATIONCODE){
return chartCodeAndOrgCodeSummary;
}else if (thresholdCriteria == CHART_AND_VENDOR){
return chartCodeAndVendorSummary;
}
throw new RuntimeException("Invalid ThresholdCriteria Enum - " + thresholdCriteria);
}
private void processVendorForThresholdSummary(SummaryAccount account,
String vendorHeaderGeneratedIdentifier,
String vendorDetailAssignedIdentifier){
ThresholdSummary thresholdSummary = new ThresholdSummary(CHART_AND_VENDOR);
thresholdSummary.setProperty(ThresholdField.CHART_OF_ACCOUNTS_CODE,account.getAccount().getChartOfAccountsCode());
thresholdSummary.setProperty(ThresholdField.VENDOR_HEADER_GENERATED_ID,vendorHeaderGeneratedIdentifier);
thresholdSummary.setProperty(ThresholdField.VENDOR_DETAIL_ASSIGNED_ID,vendorDetailAssignedIdentifier);
thresholdSummary.addTotalAmount(account.getAccount().getAmount());
addToSummaryList(thresholdSummary);
}
private void processCommodityCodeForThreshold(List<PurchaseOrderItem> items){
if (items != null){
for (PurchaseOrderItem item : items) {
if (item.isItemActiveIndicator()){
List<PurApAccountingLine> accountingLines = item.getSourceAccountingLines();
for (int i = 0; i < accountingLines.size(); i++) {
if (StringUtils.isNotBlank(item.getPurchasingCommodityCode())){
ThresholdSummary thresholdSummary = new ThresholdSummary(CHART_AND_COMMODITYCODE);
thresholdSummary.setProperty(ThresholdField.CHART_OF_ACCOUNTS_CODE,accountingLines.get(i).getChartOfAccountsCode());
thresholdSummary.setProperty(ThresholdField.COMMODITY_CODE,item.getPurchasingCommodityCode());
thresholdSummary.addTotalAmount(item.getExtendedPrice());
addToSummaryList(thresholdSummary);
}
}
}
}
}
}
public boolean isReceivingDocumentRequired() {
// From spec - 7. If all the line items are non-quantity do not do the threshold check.
if (allItemsNonQty){
return false;
}
for (ThresholdCriteria thresholdEnum : ThresholdCriteria.getEnumList()) {
boolean result = isReceivingDocumentRequired(thresholdEnum);
if (result){
return true;
}
}
return false;
}
/**
* This method is public since it's required in the ThresholdTest class. To know the receiving required doc status for a PO,
* it's always better to call isReceivingDocumentRequired() instead of this method.
*/
public boolean isReceivingDocumentRequired(ThresholdCriteria thresholdEnum) {
List<ThresholdSummary> summaryList = getThresholdSummaryCollection(thresholdEnum);
if (summaryList != null){
for (ThresholdSummary summary : summaryList) {
Collection collection = null;
if (thresholdEnum == CHART){
collection = thresholdService.findByChart(summary.getProperty(ThresholdField.CHART_OF_ACCOUNTS_CODE));
}else if (thresholdEnum == CHART_AND_ACCOUNTTYPE){
collection = thresholdService.findByChartAndFund(summary.getProperty(ThresholdField.CHART_OF_ACCOUNTS_CODE),
summary.getProperty(ThresholdField.ACCOUNT_TYPE_CODE));
}else if (thresholdEnum == CHART_AND_SUBFUND){
collection = thresholdService.findByChartAndSubFund(summary.getProperty(ThresholdField.CHART_OF_ACCOUNTS_CODE),
summary.getProperty(ThresholdField.SUBFUND_GROUP_CODE));
}else if (thresholdEnum == CHART_AND_COMMODITYCODE){
collection = thresholdService.findByChartAndCommodity(summary.getProperty(ThresholdField.CHART_OF_ACCOUNTS_CODE),
summary.getProperty(ThresholdField.COMMODITY_CODE));
}else if (thresholdEnum == CHART_AND_OBJECTCODE){
collection = thresholdService.findByChartAndObjectCode(summary.getProperty(ThresholdField.CHART_OF_ACCOUNTS_CODE),
summary.getProperty(ThresholdField.FINANCIAL_OBJECT_CODE));
}else if (thresholdEnum == CHART_AND_ORGANIZATIONCODE){
collection = thresholdService.findByChartAndOrg(summary.getProperty(ThresholdField.CHART_OF_ACCOUNTS_CODE),
summary.getProperty(ThresholdField.ORGANIZATION_CODE));
}else if (thresholdEnum == CHART_AND_VENDOR){
collection = thresholdService.findByChartAndVendor(summary.getProperty(ThresholdField.CHART_OF_ACCOUNTS_CODE),
summary.getProperty(ThresholdField.VENDOR_HEADER_GENERATED_ID),
summary.getProperty(ThresholdField.VENDOR_DETAIL_ASSIGNED_ID));
}
if (collection != null){
for (ReceivingThreshold threshold :(List<ReceivingThreshold>) collection){
if (threshold.getThresholdAmount() == null || threshold.getThresholdAmount().isLessThan(summary.getTotalAmount())){
thresholdSummary = summary;
receivingThreshold = threshold;
return true;
}
}
}
}
}
return false;
}
public ThresholdSummary getThresholdSummary() {
return thresholdSummary;
}
public ReceivingThreshold getReceivingThreshold() {
return receivingThreshold;
}
public class ThresholdSummary {
private ThresholdCriteria thresholdCriteria;
private Map<ThresholdField,String> property2Value = new HashMap<ThresholdField,String>();
private KualiDecimal totalAmount = KualiDecimal.ZERO;
ThresholdSummary(ThresholdCriteria thresholdCriteria){
this.thresholdCriteria = thresholdCriteria;
}
void setProperty(ThresholdField thresholdField,
String fieldValue){
if (!isValidProperty(thresholdField)){
throw new RuntimeException("Property[" + thresholdField + "] not allowed for the threshold criteria[" + thresholdCriteria + "]");
}
property2Value.put(thresholdField,fieldValue);
}
String getProperty(ThresholdField thresholdEnum){
return property2Value.get(thresholdEnum);
}
public ThresholdCriteria getThresholdCriteria() {
return thresholdCriteria;
}
public String getThresholdCriteriaName() {
return thresholdCriteria.getName();
}
public KualiDecimal getTotalAmount() {
return totalAmount;
}
void addTotalAmount(KualiDecimal totalAmount) {
if (totalAmount != null){
this.totalAmount = this.totalAmount.add(totalAmount);
}
}
boolean isValidProperty(ThresholdField thresholdField){
if (getThresholdCriteria() == CHART &&
ThresholdField.CHART_OF_ACCOUNTS_CODE == thresholdField){
return true;
}else if ((getThresholdCriteria() == CHART_AND_ACCOUNTTYPE) &&
(ThresholdField.CHART_OF_ACCOUNTS_CODE == thresholdField ||
ThresholdField.ACCOUNT_TYPE_CODE == thresholdField)){
return true;
}else if ((getThresholdCriteria() == CHART_AND_SUBFUND) &&
(ThresholdField.CHART_OF_ACCOUNTS_CODE == thresholdField ||
ThresholdField.SUBFUND_GROUP_CODE == thresholdField)){
return true;
}else if ((getThresholdCriteria() == CHART_AND_COMMODITYCODE) &&
(ThresholdField.CHART_OF_ACCOUNTS_CODE == thresholdField ||
ThresholdField.COMMODITY_CODE == thresholdField)){
return true;
}else if ((getThresholdCriteria() == CHART_AND_OBJECTCODE) &&
(ThresholdField.CHART_OF_ACCOUNTS_CODE == thresholdField ||
ThresholdField.FINANCIAL_OBJECT_CODE == thresholdField)){
return true;
}else if ((getThresholdCriteria() == CHART_AND_ORGANIZATIONCODE) &&
(ThresholdField.CHART_OF_ACCOUNTS_CODE == thresholdField ||
ThresholdField.ORGANIZATION_CODE == thresholdField)){
return true;
}else if ((getThresholdCriteria() == CHART_AND_VENDOR) &&
(ThresholdField.CHART_OF_ACCOUNTS_CODE == thresholdField ||
ThresholdField.VENDOR_HEADER_GENERATED_ID == thresholdField ||
ThresholdField.VENDOR_DETAIL_ASSIGNED_ID == thresholdField)){
return true;
}
return false;
}
@Override
public boolean equals(Object obj){
if (obj != null){
if (!(obj instanceof ThresholdSummary)){
return false;
}
ThresholdSummary thresholdItem = (ThresholdSummary)obj;
if (getThresholdCriteria() == thresholdItem.getThresholdCriteria()){
if (getThresholdCriteria() == CHART){
if (StringUtils.equals(property2Value.get(ThresholdField.CHART_OF_ACCOUNTS_CODE),
thresholdItem.getProperty(ThresholdField.CHART_OF_ACCOUNTS_CODE))){
return true;
}
}else if (getThresholdCriteria() == CHART_AND_ACCOUNTTYPE){
if (StringUtils.equals(property2Value.get(ThresholdField.CHART_OF_ACCOUNTS_CODE),
thresholdItem.getProperty(ThresholdField.CHART_OF_ACCOUNTS_CODE)) &&
StringUtils.equals(property2Value.get(ThresholdField.ACCOUNT_TYPE_CODE),
thresholdItem.getProperty(ThresholdField.ACCOUNT_TYPE_CODE))){
return true;
}
}else if (getThresholdCriteria() == CHART_AND_SUBFUND){
if (StringUtils.equals(property2Value.get(ThresholdField.CHART_OF_ACCOUNTS_CODE),
thresholdItem.getProperty(ThresholdField.CHART_OF_ACCOUNTS_CODE)) &&
StringUtils.equals(property2Value.get(ThresholdField.SUBFUND_GROUP_CODE),
thresholdItem.getProperty(ThresholdField.SUBFUND_GROUP_CODE))){
return true;
}
}else if (getThresholdCriteria() == CHART_AND_COMMODITYCODE){
if (StringUtils.equals(property2Value.get(ThresholdField.CHART_OF_ACCOUNTS_CODE),
thresholdItem.getProperty(ThresholdField.CHART_OF_ACCOUNTS_CODE)) &&
StringUtils.equals(property2Value.get(ThresholdField.COMMODITY_CODE),
thresholdItem.getProperty(ThresholdField.COMMODITY_CODE))){
return true;
}
}else if (getThresholdCriteria() == CHART_AND_OBJECTCODE){
if (StringUtils.equals(property2Value.get(ThresholdField.CHART_OF_ACCOUNTS_CODE),
thresholdItem.getProperty(ThresholdField.CHART_OF_ACCOUNTS_CODE)) &&
StringUtils.equals(property2Value.get(ThresholdField.FINANCIAL_OBJECT_CODE),
thresholdItem.getProperty(ThresholdField.FINANCIAL_OBJECT_CODE))){
return true;
}
}else if (getThresholdCriteria() == CHART_AND_ORGANIZATIONCODE){
if (StringUtils.equals(property2Value.get(ThresholdField.CHART_OF_ACCOUNTS_CODE),
thresholdItem.getProperty(ThresholdField.CHART_OF_ACCOUNTS_CODE)) &&
StringUtils.equals(property2Value.get(ThresholdField.ORGANIZATION_CODE),
thresholdItem.getProperty(ThresholdField.ORGANIZATION_CODE))){
return true;
}
}else if (getThresholdCriteria() == CHART_AND_VENDOR){
if (StringUtils.equals(property2Value.get(ThresholdField.CHART_OF_ACCOUNTS_CODE),
thresholdItem.getProperty(ThresholdField.CHART_OF_ACCOUNTS_CODE)) &&
StringUtils.equals(property2Value.get(ThresholdField.VENDOR_HEADER_GENERATED_ID),
thresholdItem.getProperty(ThresholdField.VENDOR_HEADER_GENERATED_ID)) &&
StringUtils.equals(property2Value.get(ThresholdField.VENDOR_DETAIL_ASSIGNED_ID),
thresholdItem.getProperty(ThresholdField.VENDOR_DETAIL_ASSIGNED_ID))){
return true;
}
}
}
}
return false;
}
@Override
public String toString(){
ToStringBuilder stringBuilder = new ToStringBuilder(this);
stringBuilder.append("ThresholdCriteria",getThresholdCriteria().getName());
stringBuilder.append("Amount",getTotalAmount());
stringBuilder.append("Field2Values",property2Value);
return stringBuilder.toString();
}
}
}
final class ThresholdCriteria extends Enum {
ThresholdCriteria(String name) {
super(name);
}
public static List<ThresholdCriteria> getEnumList() {
return getEnumList(ThresholdCriteria.class);
}
}