/*
* 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.businessobject;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.kuali.kfs.module.purap.PurapConstants;
import org.kuali.kfs.module.purap.PurapPropertyConstants;
import org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument;
import org.kuali.kfs.sys.util.ObjectPopulationUtils;
import org.kuali.rice.core.api.util.type.KualiDecimal;
import org.kuali.rice.krad.bo.PersistableBusinessObjectBase;
import org.kuali.rice.krad.util.ObjectUtils;
/**
* Purap Item Base Business Object.
*/
public abstract class PurApItemBase extends PersistableBusinessObjectBase implements PurApItem {
private Integer itemIdentifier;
private Integer itemLineNumber;
private String itemUnitOfMeasureCode;
private String itemCatalogNumber;
private String itemDescription;
private BigDecimal itemUnitPrice;
private String itemTypeCode;
private String itemAuxiliaryPartIdentifier;
private String externalOrganizationB2bProductReferenceNumber;
private String externalOrganizationB2bProductTypeName;
private boolean itemAssignedToTradeInIndicator;
private KualiDecimal extendedPrice; // not currently in DB
private KualiDecimal itemSalesTaxAmount;
private List<PurApItemUseTax> useTaxItems;
private List<PurApAccountingLine> sourceAccountingLines;
private List<PurApAccountingLine> baselineSourceAccountingLines;
private PurApAccountingLine newSourceLine;
private ItemType itemType;
private Integer purapDocumentIdentifier;
private KualiDecimal itemQuantity;
private PurchasingAccountsPayableDocument purapDocument;
/**
* Default constructor.
*/
public PurApItemBase() {
itemTypeCode = PurapConstants.ItemTypeCodes.ITEM_TYPE_ITEM_CODE;
sourceAccountingLines = new ArrayList();
baselineSourceAccountingLines = new ArrayList();
useTaxItems = new ArrayList();
resetAccount();
}
/**
* @see org.kuali.kfs.module.purap.businessobject.PurApItem#getItemIdentifierString()
*/
@Override
public String getItemIdentifierString() {
String itemLineNumberString = (getItemLineNumber() != null ? getItemLineNumber().toString() : "");
String identifierString = (getItemType().isLineItemIndicator() ? "Item " + itemLineNumberString : getItemType().getItemTypeDescription());
return identifierString;
}
@Override
public Integer getItemIdentifier() {
return itemIdentifier;
}
@Override
public void setItemIdentifier(Integer ItemIdentifier) {
this.itemIdentifier = ItemIdentifier;
}
@Override
public Integer getItemLineNumber() {
return itemLineNumber;
}
@Override
public void setItemLineNumber(Integer itemLineNumber) {
this.itemLineNumber = itemLineNumber;
}
@Override
public String getItemUnitOfMeasureCode() {
return itemUnitOfMeasureCode;
}
@Override
public void setItemUnitOfMeasureCode(String itemUnitOfMeasureCode) {
this.itemUnitOfMeasureCode = (StringUtils.isNotBlank(itemUnitOfMeasureCode) ? itemUnitOfMeasureCode.toUpperCase() : itemUnitOfMeasureCode);
}
@Override
public String getItemCatalogNumber() {
return itemCatalogNumber;
}
@Override
public void setItemCatalogNumber(String itemCatalogNumber) {
this.itemCatalogNumber = itemCatalogNumber;
}
@Override
public String getItemDescription() {
return itemDescription;
}
@Override
public void setItemDescription(String itemDescription) {
this.itemDescription = itemDescription;
}
@Override
public BigDecimal getItemUnitPrice() {
// Setting scale on retrieval of unit price
if (itemUnitPrice != null) {
if (itemUnitPrice.scale() < PurapConstants.DOLLAR_AMOUNT_MIN_SCALE) {
itemUnitPrice = itemUnitPrice.setScale(PurapConstants.DOLLAR_AMOUNT_MIN_SCALE, KualiDecimal.ROUND_BEHAVIOR);
}
else if (itemUnitPrice.scale() > PurapConstants.UNIT_PRICE_MAX_SCALE) {
itemUnitPrice = itemUnitPrice.setScale(PurapConstants.UNIT_PRICE_MAX_SCALE, KualiDecimal.ROUND_BEHAVIOR);
}
}
return itemUnitPrice;
}
@Override
public void setItemUnitPrice(BigDecimal itemUnitPrice) {
if (itemUnitPrice != null) {
if (itemUnitPrice.scale() < PurapConstants.DOLLAR_AMOUNT_MIN_SCALE) {
itemUnitPrice = itemUnitPrice.setScale(PurapConstants.DOLLAR_AMOUNT_MIN_SCALE, KualiDecimal.ROUND_BEHAVIOR);
}
else if (itemUnitPrice.scale() > PurapConstants.UNIT_PRICE_MAX_SCALE) {
itemUnitPrice = itemUnitPrice.setScale(PurapConstants.UNIT_PRICE_MAX_SCALE, KualiDecimal.ROUND_BEHAVIOR);
}
}
this.itemUnitPrice = itemUnitPrice;
}
@Override
public String getItemTypeCode() {
return itemTypeCode;
}
@Override
public void setItemTypeCode(String itemTypeCode) {
this.itemTypeCode = itemTypeCode;
}
@Override
public String getItemAuxiliaryPartIdentifier() {
return itemAuxiliaryPartIdentifier;
}
@Override
public void setItemAuxiliaryPartIdentifier(String itemAuxiliaryPartIdentifier) {
this.itemAuxiliaryPartIdentifier = itemAuxiliaryPartIdentifier;
}
@Override
public String getExternalOrganizationB2bProductReferenceNumber() {
return externalOrganizationB2bProductReferenceNumber;
}
@Override
public void setExternalOrganizationB2bProductReferenceNumber(String externalOrganizationB2bProductReferenceNumber) {
this.externalOrganizationB2bProductReferenceNumber = externalOrganizationB2bProductReferenceNumber;
}
@Override
public String getExternalOrganizationB2bProductTypeName() {
return externalOrganizationB2bProductTypeName;
}
@Override
public void setExternalOrganizationB2bProductTypeName(String externalOrganizationB2bProductTypeName) {
this.externalOrganizationB2bProductTypeName = externalOrganizationB2bProductTypeName;
}
@Override
public boolean getItemAssignedToTradeInIndicator() {
return itemAssignedToTradeInIndicator;
}
@Override
public void setItemAssignedToTradeInIndicator(boolean itemAssignedToTradeInIndicator) {
this.itemAssignedToTradeInIndicator = itemAssignedToTradeInIndicator;
}
@Override
public ItemType getItemType() {
if (ObjectUtils.isNull(itemType) || !itemType.getItemTypeCode().equals(itemTypeCode)) {
refreshReferenceObject(PurapPropertyConstants.ITEM_TYPE);
}
return itemType;
}
/**
* Sets the itemType attribute.
*
* @param itemType The itemType to set.
* @deprecated
*/
@Override
@Deprecated
public void setItemType(ItemType itemType) {
this.itemType = itemType;
}
@Override
public KualiDecimal getItemTaxAmount() {
KualiDecimal taxAmount = KualiDecimal.ZERO;
if (ObjectUtils.isNull(purapDocument)) {
this.refreshReferenceObject("purapDocument");
}
if (purapDocument.isUseTaxIndicator() == false) {
taxAmount = this.itemSalesTaxAmount;
}
else {
// sum use tax item tax amounts
for (PurApItemUseTax useTaxItem : this.getUseTaxItems()) {
taxAmount = taxAmount.add(useTaxItem.getTaxAmount());
}
}
return taxAmount;
}
@Override
public void setItemTaxAmount(KualiDecimal itemTaxAmount) {
if (purapDocument == null) {
this.refreshReferenceObject("purapDocument");
}
if (purapDocument.isUseTaxIndicator() == false) {
this.itemSalesTaxAmount = itemTaxAmount;
}
}
public final KualiDecimal getItemSalesTaxAmount() {
return itemSalesTaxAmount;
}
public final void setItemSalesTaxAmount(KualiDecimal itemSalesTaxAmount) {
this.itemSalesTaxAmount = itemSalesTaxAmount;
}
@Override
public KualiDecimal getExtendedPrice() {
return calculateExtendedPrice();
}
@Override
public KualiDecimal getTotalAmount() {
KualiDecimal totalAmount = getExtendedPrice();
if (ObjectUtils.isNull(totalAmount)) {
totalAmount = KualiDecimal.ZERO;
}
KualiDecimal taxAmount = getItemTaxAmount();
if (ObjectUtils.isNull(taxAmount)) {
taxAmount = KualiDecimal.ZERO;
}
totalAmount = totalAmount.add(taxAmount);
return totalAmount;
}
@Override
public void setTotalAmount(KualiDecimal totalAmount) {
// do nothing, setter required by interface
}
@Override
public KualiDecimal calculateExtendedPrice() {
KualiDecimal extendedPrice = KualiDecimal.ZERO;
if (ObjectUtils.isNotNull(itemUnitPrice)) {
if (this.itemType.isAmountBasedGeneralLedgerIndicator()) {
// SERVICE ITEM: return unit price as extended price
extendedPrice = new KualiDecimal(this.itemUnitPrice.toString());
}
else if (ObjectUtils.isNotNull(this.getItemQuantity())) {
BigDecimal calcExtendedPrice = this.itemUnitPrice.multiply(this.itemQuantity.bigDecimalValue());
// ITEM TYPE (qty driven): return (unitPrice x qty)
extendedPrice = new KualiDecimal(calcExtendedPrice.setScale(KualiDecimal.SCALE, KualiDecimal.ROUND_BEHAVIOR));
}
}
return extendedPrice;
}
@Override
public void setExtendedPrice(KualiDecimal extendedPrice) {
this.extendedPrice = extendedPrice;
}
@Override
public List<PurApAccountingLine> getSourceAccountingLines() {
return sourceAccountingLines;
}
@Override
public void setSourceAccountingLines(List<PurApAccountingLine> accountingLines) {
this.sourceAccountingLines = accountingLines;
}
@Override
public List<PurApAccountingLine> getBaselineSourceAccountingLines() {
return baselineSourceAccountingLines;
}
public void setBaselineSourceAccountingLines(List<PurApAccountingLine> baselineSourceLines) {
this.baselineSourceAccountingLines = baselineSourceLines;
}
/**
* This implementation is coupled tightly with some underlying issues that the Struts PojoProcessor plugin has with how objects
* get instantiated within lists. The first three lines are required otherwise when the PojoProcessor tries to automatically
* inject values into the list, it will get an index out of bounds error if the instance at an index is being called and prior
* instances at indices before that one are not being instantiated. So changing the code below will cause adding lines to break
* if you add more than one item to the list.
*
* @see org.kuali.rice.krad.document.FinancialDocument#getTargetAccountingLine(int)
*/
public PurApAccountingLine getSourceAccountingLine(int index) {
while (getSourceAccountingLines().size() <= index) {
PurApAccountingLine newAccount = getNewAccount();
getSourceAccountingLines().add(newAccount);
}
return getSourceAccountingLines().get(index);
}
/**
* This implementation is coupled tightly with some underlying issues that the Struts PojoProcessor plugin has with how objects
* get instantiated within lists. The first three lines are required otherwise when the PojoProcessor tries to automatically
* inject values into the list, it will get an index out of bounds error if the instance at an index is being called and prior
* instances at indices before that one are not being instantiated. So changing the code below will cause adding lines to break
* if you add more than one item to the list.
*
* @see org.kuali.rice.krad.document.FinancialDocument#getTargetAccountingLine(int)
*/
public PurApAccountingLine getBaselineSourceAccountingLine(int index) {
while (getBaselineSourceAccountingLines().size() <= index) {
PurApAccountingLine newAccount = getNewAccount();
getBaselineSourceAccountingLines().add(newAccount);
}
return getBaselineSourceAccountingLines().get(index);
}
private PurApAccountingLine getNewAccount() throws RuntimeException {
PurApAccountingLine newAccount = null;
try {
newAccount = (PurApAccountingLine) getAccountingLineClass().newInstance();
}
catch (InstantiationException e) {
throw new RuntimeException("Unable to get class");
}
catch (IllegalAccessException e) {
throw new RuntimeException("Unable to get class");
}
catch (NullPointerException e) {
throw new RuntimeException("Can't instantiate Purchasing Account from base");
}
return newAccount;
}
@Override
public abstract Class getAccountingLineClass();
@Override
public abstract Class getUseTaxClass();
@Override
public void resetAccount() {
// add a blank accounting line
PurApAccountingLine purApAccountingLine = getNewAccount();
purApAccountingLine.setItemIdentifier(this.itemIdentifier);
purApAccountingLine.setPurapItem(this);
purApAccountingLine.setSequenceNumber(0);
setNewSourceLine(purApAccountingLine);
}
/**
* @see org.kuali.rice.krad.document.DocumentBase#buildListOfDeletionAwareLists()
*/
@Override
public List buildListOfDeletionAwareLists() {
List managedLists = new ArrayList();
managedLists.add(getSourceAccountingLines());
return managedLists;
}
/**
* @see org.kuali.rice.krad.bo.BusinessObjectBase#toStringMapper()
*/
protected LinkedHashMap toStringMapper_RICE20_REFACTORME() {
LinkedHashMap m = new LinkedHashMap();
if (this.itemIdentifier != null) {
m.put("requisitionItemIdentifier", this.itemIdentifier.toString());
}
return m;
}
@Override
public PurApAccountingLine getNewSourceLine() {
return newSourceLine;
}
@Override
public void setNewSourceLine(PurApAccountingLine newAccountingLine) {
this.newSourceLine = newAccountingLine;
}
@Override
public Integer getPurapDocumentIdentifier() {
return purapDocumentIdentifier;
}
@Override
public void setPurapDocumentIdentifier(Integer purapDocumentIdentifier) {
this.purapDocumentIdentifier = purapDocumentIdentifier;
}
@Override
public List<PurApItemUseTax> getUseTaxItems() {
return useTaxItems;
}
@Override
public void setUseTaxItems(List<PurApItemUseTax> useTaxItems) {
this.useTaxItems = useTaxItems;
}
@Override
public KualiDecimal getItemQuantity() {
return itemQuantity;
}
@Override
public void setItemQuantity(KualiDecimal itemQuantity) {
this.itemQuantity = itemQuantity;
}
public boolean isAccountListEmpty() {
List<PurApAccountingLine> accounts = getSourceAccountingLines();
if (ObjectUtils.isNotNull(accounts)) {
for (PurApAccountingLine element : accounts) {
if (!element.isEmpty()) {
return false;
}
}
}
return true;
}
@Override
public PurApSummaryItem getSummaryItem() {
PurApSummaryItem summaryItem = new PurApSummaryItem();
ObjectPopulationUtils.populateFromBaseClass(PurApItemBase.class, this, summaryItem, new HashMap());
summaryItem.getItemType().setItemTypeDescription(this.itemType.getItemTypeDescription());
return summaryItem;
}
@Override
public final <T extends PurchasingAccountsPayableDocument> T getPurapDocument() {
return (T) purapDocument;
}
@Override
public final void setPurapDocument(PurchasingAccountsPayableDocument purapDoc) {
this.purapDocument = purapDoc;
}
/**
* fixes item references on accounts
*
* @see org.kuali.kfs.module.purap.businessobject.PurApItem#fixAccountReferences()
*/
@Override
public void fixAccountReferences() {
if (ObjectUtils.isNull(this.getItemIdentifier())) {
for (PurApAccountingLine account : this.getSourceAccountingLines()) {
account.setSequenceNumber(0);
account.setPurapItem(this);
}
}
}
@Override
public void refreshNonUpdateableReferences() {
PurchasingAccountsPayableDocument document = null;
PurchasingAccountsPayableDocument tempDocument = getPurapDocument();
if (tempDocument != null) {
Integer tempDocumentIdentifier = tempDocument.getPurapDocumentIdentifier();
if (tempDocumentIdentifier != null) {
document = this.getPurapDocument();
}
}
super.refreshNonUpdateableReferences();
if (ObjectUtils.isNotNull(document)) {
this.setPurapDocument(document);
}
}
@Override
public KualiDecimal getTotalRemitAmount() {
if (!purapDocument.isUseTaxIndicator()) {
return this.getTotalAmount();
}
return this.getExtendedPrice();
}
@Override
public String toString() {
return "Line "+(itemLineNumber==null?"(null)":itemLineNumber.toString())+": ["+itemTypeCode+"] " +
"Unit:"+(itemUnitPrice==null?"(null)":itemUnitPrice.toString())+" " +
"Tax:"+(itemSalesTaxAmount==null?"(null)":itemSalesTaxAmount.toString())+" " +
"*"+itemDescription+"*";
}
}