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
017 package org.kuali.kfs.module.purap.businessobject;
018
019 import java.math.BigDecimal;
020 import java.util.ArrayList;
021 import java.util.HashMap;
022 import java.util.LinkedHashMap;
023 import java.util.List;
024
025 import org.apache.commons.lang.StringUtils;
026 import org.kuali.kfs.module.purap.PurapConstants;
027 import org.kuali.kfs.module.purap.PurapPropertyConstants;
028 import org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument;
029 import org.kuali.kfs.module.purap.util.PurApObjectUtils;
030 import org.kuali.rice.kns.bo.PersistableBusinessObjectBase;
031 import org.kuali.rice.kns.util.KualiDecimal;
032 import org.kuali.rice.kns.util.ObjectUtils;
033 import org.kuali.rice.kns.util.TypedArrayList;
034
035 /**
036 * Purap Item Base Business Object.
037 */
038 public abstract class PurApItemBase extends PersistableBusinessObjectBase implements PurApItem {
039
040 private Integer itemIdentifier;
041 private Integer itemLineNumber;
042 private String itemUnitOfMeasureCode;
043 private String itemCatalogNumber;
044 private String itemDescription;
045 private BigDecimal itemUnitPrice;
046 private String itemTypeCode;
047 private String itemAuxiliaryPartIdentifier;
048 private String externalOrganizationB2bProductReferenceNumber;
049 private String externalOrganizationB2bProductTypeName;
050 private boolean itemAssignedToTradeInIndicator;
051 private KualiDecimal extendedPrice; // not currently in DB
052 private KualiDecimal itemSalesTaxAmount;
053
054 private List<PurApItemUseTax> useTaxItems;
055 private List<PurApAccountingLine> sourceAccountingLines;
056 private List<PurApAccountingLine> baselineSourceAccountingLines;
057 private PurApAccountingLine newSourceLine;
058
059 private ItemType itemType;
060 private Integer purapDocumentIdentifier;
061 private KualiDecimal itemQuantity;
062
063 private PurchasingAccountsPayableDocument purapDocument;
064
065 /**
066 * Default constructor.
067 */
068 public PurApItemBase() {
069 itemTypeCode = PurapConstants.ItemTypeCodes.ITEM_TYPE_ITEM_CODE;
070 sourceAccountingLines = new TypedArrayList(getAccountingLineClass());
071 baselineSourceAccountingLines = new TypedArrayList(getAccountingLineClass());
072 useTaxItems = new TypedArrayList(getUseTaxClass());
073 resetAccount();
074 }
075
076 /**
077 * @see org.kuali.kfs.module.purap.businessobject.PurApItem#getItemIdentifierString()
078 */
079 public String getItemIdentifierString() {
080 String itemLineNumberString = (getItemLineNumber() != null ? getItemLineNumber().toString() : "");
081 String identifierString = (getItemType().isLineItemIndicator() ? "Item " + itemLineNumberString : getItemType().getItemTypeDescription());
082 return identifierString;
083 }
084
085 public Integer getItemIdentifier() {
086 return itemIdentifier;
087 }
088
089 public void setItemIdentifier(Integer ItemIdentifier) {
090 this.itemIdentifier = ItemIdentifier;
091 }
092
093 public Integer getItemLineNumber() {
094 return itemLineNumber;
095 }
096
097 public void setItemLineNumber(Integer itemLineNumber) {
098 this.itemLineNumber = itemLineNumber;
099 }
100
101 public String getItemUnitOfMeasureCode() {
102 return itemUnitOfMeasureCode;
103 }
104
105 public void setItemUnitOfMeasureCode(String itemUnitOfMeasureCode) {
106 this.itemUnitOfMeasureCode = (StringUtils.isNotBlank(itemUnitOfMeasureCode) ? itemUnitOfMeasureCode.toUpperCase() : itemUnitOfMeasureCode);
107 }
108
109 public String getItemCatalogNumber() {
110 return itemCatalogNumber;
111 }
112
113 public void setItemCatalogNumber(String itemCatalogNumber) {
114 this.itemCatalogNumber = itemCatalogNumber;
115 }
116
117 public String getItemDescription() {
118 return itemDescription;
119 }
120
121 public void setItemDescription(String itemDescription) {
122 this.itemDescription = itemDescription;
123 }
124
125 public BigDecimal getItemUnitPrice() {
126 // Setting scale on retrieval of unit price
127 if (itemUnitPrice != null) {
128 if (itemUnitPrice.scale() < PurapConstants.DOLLAR_AMOUNT_MIN_SCALE) {
129 itemUnitPrice = itemUnitPrice.setScale(PurapConstants.DOLLAR_AMOUNT_MIN_SCALE, KualiDecimal.ROUND_BEHAVIOR);
130 }
131 else if (itemUnitPrice.scale() > PurapConstants.UNIT_PRICE_MAX_SCALE) {
132 itemUnitPrice = itemUnitPrice.setScale(PurapConstants.UNIT_PRICE_MAX_SCALE, KualiDecimal.ROUND_BEHAVIOR);
133 }
134 }
135
136 return itemUnitPrice;
137 }
138
139 public void setItemUnitPrice(BigDecimal itemUnitPrice) {
140 if (itemUnitPrice != null) {
141 if (itemUnitPrice.scale() < PurapConstants.DOLLAR_AMOUNT_MIN_SCALE) {
142 itemUnitPrice = itemUnitPrice.setScale(PurapConstants.DOLLAR_AMOUNT_MIN_SCALE, KualiDecimal.ROUND_BEHAVIOR);
143 }
144 else if (itemUnitPrice.scale() > PurapConstants.UNIT_PRICE_MAX_SCALE) {
145 itemUnitPrice = itemUnitPrice.setScale(PurapConstants.UNIT_PRICE_MAX_SCALE, KualiDecimal.ROUND_BEHAVIOR);
146 }
147 }
148 this.itemUnitPrice = itemUnitPrice;
149 }
150
151 public String getItemTypeCode() {
152 return itemTypeCode;
153 }
154
155 public void setItemTypeCode(String itemTypeCode) {
156 this.itemTypeCode = itemTypeCode;
157 }
158
159 public String getItemAuxiliaryPartIdentifier() {
160 return itemAuxiliaryPartIdentifier;
161 }
162
163 public void setItemAuxiliaryPartIdentifier(String itemAuxiliaryPartIdentifier) {
164 this.itemAuxiliaryPartIdentifier = itemAuxiliaryPartIdentifier;
165 }
166
167 public String getExternalOrganizationB2bProductReferenceNumber() {
168 return externalOrganizationB2bProductReferenceNumber;
169 }
170
171 public void setExternalOrganizationB2bProductReferenceNumber(String externalOrganizationB2bProductReferenceNumber) {
172 this.externalOrganizationB2bProductReferenceNumber = externalOrganizationB2bProductReferenceNumber;
173 }
174
175 public String getExternalOrganizationB2bProductTypeName() {
176 return externalOrganizationB2bProductTypeName;
177 }
178
179 public void setExternalOrganizationB2bProductTypeName(String externalOrganizationB2bProductTypeName) {
180 this.externalOrganizationB2bProductTypeName = externalOrganizationB2bProductTypeName;
181 }
182
183 public boolean getItemAssignedToTradeInIndicator() {
184 return itemAssignedToTradeInIndicator;
185 }
186
187 public void setItemAssignedToTradeInIndicator(boolean itemAssignedToTradeInIndicator) {
188 this.itemAssignedToTradeInIndicator = itemAssignedToTradeInIndicator;
189 }
190
191 public ItemType getItemType() {
192 if (ObjectUtils.isNull(itemType) || !itemType.getItemTypeCode().equals(itemTypeCode)) {
193 refreshReferenceObject(PurapPropertyConstants.ITEM_TYPE);
194 }
195 return itemType;
196 }
197
198 /**
199 * Sets the itemType attribute.
200 *
201 * @param itemType The itemType to set.
202 * @deprecated
203 */
204 public void setItemType(ItemType itemType) {
205 this.itemType = itemType;
206 }
207
208 public KualiDecimal getItemTaxAmount() {
209 KualiDecimal taxAmount = KualiDecimal.ZERO;
210
211 if (ObjectUtils.isNull(purapDocument)) {
212 this.refreshReferenceObject("purapDocument");
213 }
214
215 if (purapDocument.isUseTaxIndicator() == false) {
216 taxAmount = this.itemSalesTaxAmount;
217 }
218 else {
219 // sum use tax item tax amounts
220 for (PurApItemUseTax useTaxItem : this.getUseTaxItems()) {
221 taxAmount = taxAmount.add(useTaxItem.getTaxAmount());
222 }
223 }
224
225 return taxAmount;
226 }
227
228 public void setItemTaxAmount(KualiDecimal itemTaxAmount) {
229
230 if (purapDocument == null) {
231 this.refreshReferenceObject("purapDocument");
232 }
233
234 if (purapDocument.isUseTaxIndicator() == false) {
235 this.itemSalesTaxAmount = itemTaxAmount;
236 }
237
238 }
239
240 public final KualiDecimal getItemSalesTaxAmount() {
241 return itemSalesTaxAmount;
242 }
243
244 public final void setItemSalesTaxAmount(KualiDecimal itemSalesTaxAmount) {
245 this.itemSalesTaxAmount = itemSalesTaxAmount;
246 }
247
248 public KualiDecimal getExtendedPrice() {
249 return calculateExtendedPrice();
250 }
251
252 public KualiDecimal getTotalAmount() {
253 KualiDecimal totalAmount = getExtendedPrice();
254 if (ObjectUtils.isNull(totalAmount)) {
255 totalAmount = KualiDecimal.ZERO;
256 }
257
258 KualiDecimal taxAmount = getItemTaxAmount();
259 if (ObjectUtils.isNull(taxAmount)) {
260 taxAmount = KualiDecimal.ZERO;
261 }
262
263 totalAmount = totalAmount.add(taxAmount);
264
265 return totalAmount;
266 }
267
268 public void setTotalAmount(KualiDecimal totalAmount) {
269 // do nothing, setter required by interface
270 }
271
272 public KualiDecimal calculateExtendedPrice() {
273 KualiDecimal extendedPrice = KualiDecimal.ZERO;
274 if (ObjectUtils.isNotNull(itemUnitPrice)) {
275 if (this.itemType.isAmountBasedGeneralLedgerIndicator()) {
276 // SERVICE ITEM: return unit price as extended price
277 extendedPrice = new KualiDecimal(this.itemUnitPrice.toString());
278 }
279 else if (ObjectUtils.isNotNull(this.getItemQuantity())) {
280 BigDecimal calcExtendedPrice = this.itemUnitPrice.multiply(this.itemQuantity.bigDecimalValue());
281 // ITEM TYPE (qty driven): return (unitPrice x qty)
282 extendedPrice = new KualiDecimal(calcExtendedPrice.setScale(KualiDecimal.SCALE, KualiDecimal.ROUND_BEHAVIOR));
283 }
284 }
285 return extendedPrice;
286 }
287
288 public void setExtendedPrice(KualiDecimal extendedPrice) {
289 this.extendedPrice = extendedPrice;
290 }
291
292 public List<PurApAccountingLine> getSourceAccountingLines() {
293 return sourceAccountingLines;
294 }
295
296 public void setSourceAccountingLines(List<PurApAccountingLine> accountingLines) {
297 this.sourceAccountingLines = accountingLines;
298 }
299
300 public List<PurApAccountingLine> getBaselineSourceAccountingLines() {
301 return baselineSourceAccountingLines;
302 }
303
304 public void setBaselineSourceAccountingLines(List<PurApAccountingLine> baselineSourceLines) {
305 this.baselineSourceAccountingLines = baselineSourceLines;
306 }
307
308 /**
309 * This implementation is coupled tightly with some underlying issues that the Struts PojoProcessor plugin has with how objects
310 * get instantiated within lists. The first three lines are required otherwise when the PojoProcessor tries to automatically
311 * 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
312 * instances at indices before that one are not being instantiated. So changing the code below will cause adding lines to break
313 * if you add more than one item to the list.
314 *
315 * @see org.kuali.rice.kns.document.FinancialDocument#getTargetAccountingLine(int)
316 */
317 public PurApAccountingLine getSourceAccountingLine(int index) {
318 return (PurApAccountingLine) getSourceAccountingLines().get(index);
319 }
320
321 public PurApAccountingLine getBaselineSourceAccountingLine(int index) {
322 return (PurApAccountingLine) getBaselineSourceAccountingLines().get(index);
323 }
324
325 private PurApAccountingLine getNewAccount() throws RuntimeException {
326
327 PurApAccountingLine newAccount = null;
328 try {
329 newAccount = (PurApAccountingLine) getAccountingLineClass().newInstance();
330 }
331 catch (InstantiationException e) {
332 throw new RuntimeException("Unable to get class");
333 }
334 catch (IllegalAccessException e) {
335 throw new RuntimeException("Unable to get class");
336 }
337 catch (NullPointerException e) {
338 throw new RuntimeException("Can't instantiate Purchasing Account from base");
339 }
340 return newAccount;
341 }
342
343 public abstract Class getAccountingLineClass();
344
345 public abstract Class getUseTaxClass();
346
347 public void resetAccount() {
348 // add a blank accounting line
349 PurApAccountingLine purApAccountingLine = getNewAccount();
350
351 purApAccountingLine.setItemIdentifier(this.itemIdentifier);
352 purApAccountingLine.setPurapItem(this);
353
354 setNewSourceLine(purApAccountingLine);
355 }
356
357 /**
358 * @see org.kuali.rice.kns.document.DocumentBase#buildListOfDeletionAwareLists()
359 */
360 @Override
361 public List buildListOfDeletionAwareLists() {
362 List managedLists = new ArrayList();
363
364 managedLists.add(getSourceAccountingLines());
365
366 return managedLists;
367 }
368
369 /**
370 * @see org.kuali.rice.kns.bo.BusinessObjectBase#toStringMapper()
371 */
372 protected LinkedHashMap toStringMapper() {
373 LinkedHashMap m = new LinkedHashMap();
374 if (this.itemIdentifier != null) {
375 m.put("requisitionItemIdentifier", this.itemIdentifier.toString());
376 }
377 return m;
378 }
379
380 public PurApAccountingLine getNewSourceLine() {
381 return newSourceLine;
382 }
383
384 public void setNewSourceLine(PurApAccountingLine newAccountingLine) {
385 this.newSourceLine = newAccountingLine;
386 }
387
388 public Integer getPurapDocumentIdentifier() {
389 return purapDocumentIdentifier;
390 }
391
392 public void setPurapDocumentIdentifier(Integer purapDocumentIdentifier) {
393 this.purapDocumentIdentifier = purapDocumentIdentifier;
394 }
395
396 public List<PurApItemUseTax> getUseTaxItems() {
397 return useTaxItems;
398 }
399
400 public void setUseTaxItems(List<PurApItemUseTax> useTaxItems) {
401 this.useTaxItems = useTaxItems;
402 }
403
404 public KualiDecimal getItemQuantity() {
405 return itemQuantity;
406 }
407
408 public void setItemQuantity(KualiDecimal itemQuantity) {
409 this.itemQuantity = itemQuantity;
410 }
411
412 public boolean isAccountListEmpty() {
413 List<PurApAccountingLine> accounts = getSourceAccountingLines();
414 if (ObjectUtils.isNotNull(accounts)) {
415 for (PurApAccountingLine element : accounts) {
416 if (!element.isEmpty()) {
417 return false;
418 }
419 }
420 }
421 return true;
422 }
423
424 public PurApSummaryItem getSummaryItem() {
425 PurApSummaryItem summaryItem = new PurApSummaryItem();
426 PurApObjectUtils.populateFromBaseClass(PurApItemBase.class, this, summaryItem, new HashMap());
427 summaryItem.getItemType().setItemTypeDescription(this.itemType.getItemTypeDescription());
428 return summaryItem;
429 }
430
431 public final <T extends PurchasingAccountsPayableDocument> T getPurapDocument() {
432 return (T) purapDocument;
433 }
434
435 public final void setPurapDocument(PurchasingAccountsPayableDocument purapDoc) {
436 this.purapDocument = purapDoc;
437 }
438
439 /**
440 * fixes item references on accounts
441 *
442 * @see org.kuali.kfs.module.purap.businessobject.PurApItem#fixAccountReferences()
443 */
444 public void fixAccountReferences() {
445 if (ObjectUtils.isNull(this.getItemIdentifier())) {
446 for (PurApAccountingLine account : this.getSourceAccountingLines()) {
447 account.setPurapItem(this);
448 }
449 }
450 }
451
452 @Override
453 public void refreshNonUpdateableReferences() {
454 PurchasingAccountsPayableDocument document = null;
455 PurchasingAccountsPayableDocument tempDocument = getPurapDocument();
456 if (tempDocument != null) {
457 Integer tempDocumentIdentifier = tempDocument.getPurapDocumentIdentifier();
458 if (tempDocumentIdentifier != null) {
459 document = this.getPurapDocument();
460 }
461 }
462 super.refreshNonUpdateableReferences();
463 if (ObjectUtils.isNotNull(document)) {
464 this.setPurapDocument(document);
465 }
466 }
467
468 public KualiDecimal getTotalRemitAmount() {
469 if (!purapDocument.isUseTaxIndicator()) {
470 return this.getTotalAmount();
471 }
472 return this.getExtendedPrice();
473 }
474
475 @Override
476 public String toString() {
477 return "Line "+(itemLineNumber==null?"(null)":itemLineNumber.toString())+": ["+itemTypeCode+"] " +
478 "Unit:"+(itemUnitPrice==null?"(null)":itemUnitPrice.toString())+" " +
479 "Tax:"+(itemSalesTaxAmount==null?"(null)":itemSalesTaxAmount.toString())+" " +
480 "*"+itemDescription+"*";
481 }
482
483 }