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    }