001/*
002 * Copyright 2006 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
017package org.kuali.ole.module.purap.businessobject;
018
019import org.kuali.ole.module.purap.PurapConstants;
020import org.kuali.ole.module.purap.PurapPropertyConstants;
021import org.kuali.ole.module.purap.document.PaymentRequestDocument;
022import org.kuali.ole.module.purap.document.PurchaseOrderDocument;
023import org.kuali.ole.module.purap.document.service.AccountsPayableService;
024import org.kuali.ole.module.purap.document.service.PurapService;
025import org.kuali.ole.module.purap.exception.PurError;
026import org.kuali.ole.module.purap.util.ExpiredOrClosedAccountEntry;
027import org.kuali.ole.module.purap.util.PurApItemUtils;
028import org.kuali.ole.module.purap.util.PurApObjectUtils;
029import org.kuali.ole.select.businessobject.OleInvoiceItem;
030import org.kuali.ole.select.document.OlePaymentRequestDocument;
031import org.kuali.ole.select.document.service.OlePaymentRequestService;
032import org.kuali.ole.sys.businessobject.SourceAccountingLine;
033import org.kuali.ole.sys.context.SpringContext;
034import org.kuali.rice.core.api.util.type.KualiDecimal;
035import org.kuali.rice.krad.util.ObjectUtils;
036
037import java.math.BigDecimal;
038import java.util.ArrayList;
039import java.util.HashMap;
040import java.util.List;
041
042/**
043 * Payment Request Item Business Object.
044 */
045public class PaymentRequestItem extends AccountsPayableItemBase {
046    private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(PaymentRequestItem.class);
047
048    protected BigDecimal purchaseOrderItemUnitPrice;
049    private KualiDecimal itemOutstandingInvoiceQuantity;
050    private KualiDecimal itemOutstandingInvoiceAmount;
051
052    /**
053     * Default constructor.
054     */
055    public PaymentRequestItem() {
056
057    }
058
059    /**
060     * preq item constructor - Delegate
061     *
062     * @param poi  - purchase order item
063     * @param preq - payment request document
064     */
065    public PaymentRequestItem(PurchaseOrderItem poi, PaymentRequestDocument preq) {
066        this(poi, preq, new HashMap<String, ExpiredOrClosedAccountEntry>());
067    }
068
069    /**
070     * Constructs a new payment request item, but also merges expired accounts.
071     *
072     * @param poi                        - purchase order item
073     * @param preq                       - payment request document
074     * @param expiredOrClosedAccountList - list of expired or closed accounts to merge
075     */
076    public PaymentRequestItem(PurchaseOrderItem poi, PaymentRequestDocument preq, HashMap<String, ExpiredOrClosedAccountEntry> expiredOrClosedAccountList) {
077
078        // copy base attributes w/ extra array of fields not to be copied
079        PurApObjectUtils.populateFromBaseClass(PurApItemBase.class, poi, this, PurapConstants.PREQ_ITEM_UNCOPYABLE_FIELDS);
080
081        setItemDescription(poi.getItemDescription());
082
083        //New Source Line should be set for PaymentRequestItem
084        resetAccount();
085
086        // set up accounts
087        List accounts = new ArrayList();
088        for (PurApAccountingLine account : poi.getSourceAccountingLines()) {
089            PurchaseOrderAccount poa = (PurchaseOrderAccount) account;
090
091            // check if this account is expired/closed and replace as needed
092            SpringContext.getBean(AccountsPayableService.class).processExpiredOrClosedAccount(poa, expiredOrClosedAccountList);
093
094            //KFSMI-4522 copy an accounting line with zero dollar amount if system parameter allows
095            if (poa.getAmount().isZero()) {
096                if (SpringContext.getBean(AccountsPayableService.class).canCopyAccountingLinesWithZeroAmount()) {
097                    accounts.add(new PaymentRequestAccount(this, poa));
098                }
099            } else {
100                accounts.add(new PaymentRequestAccount(this, poa));
101            }
102        }
103
104        this.setSourceAccountingLines(accounts);
105        this.getUseTaxItems().clear();
106        //List<PurApItemUseTax> newUseTaxItems = new ArrayList<PurApItemUseTax>();
107        /// this.setUseTaxItems(newUseTaxItems);
108        //copy use tax items over, and blank out keys (useTaxId and itemIdentifier)
109        /*
110        this.getUseTaxItems().clear();
111        for (PurApItemUseTax useTaxItem : poi.getUseTaxItems()) {
112            PaymentRequestItemUseTax newItemUseTax = new PaymentRequestItemUseTax(useTaxItem);
113            this.getUseTaxItems().add(newItemUseTax);
114
115        }
116        */
117
118        // clear amount and desc on below the line - we probably don't need that null
119        // itemType check but it's there just in case remove if it causes problems
120        // also do this if of type service
121        if ((ObjectUtils.isNotNull(this.getItemType()) && this.getItemType().isAmountBasedGeneralLedgerIndicator())) {
122            // setting unit price to be null to be more consistent with other below the line
123            this.setItemUnitPrice(null);
124        }
125
126        // copy custom
127        this.purchaseOrderItemUnitPrice = poi.getItemUnitPrice();
128//        this.purchaseOrderCommodityCode = poi.getPurchaseOrderCommodityCd();
129
130        // set doc fields
131        this.setPurapDocumentIdentifier(preq.getPurapDocumentIdentifier());
132        this.setPurapDocument(preq);
133    }
134
135    /**
136     * Retrieves a purchase order item by inspecting the item type to see if its above the line or below the line and returns the
137     * appropriate type.
138     *
139     * @return - purchase order item
140     */
141    @Override
142    public PurchaseOrderItem getPurchaseOrderItem() {
143        if (ObjectUtils.isNotNull(this.getPurapDocumentIdentifier())) {
144            if (ObjectUtils.isNull(this.getPaymentRequest())) {
145                this.refreshReferenceObject(PurapPropertyConstants.PURAP_DOC);
146            }
147        }
148        // ideally we should do this a different way - maybe move it all into the service or save this info somehow (make sure and
149        // update though)
150        if (getPaymentRequest() != null) {
151            PurchaseOrderDocument po = getPaymentRequest().getPurchaseOrderDocument();
152            PurchaseOrderItem poi = null;
153            if (this.getItemType().isLineItemIndicator()) {
154                List<PurchaseOrderItem> items = po.getItems();
155                poi = items.get(this.getItemLineNumber().intValue() - 1);
156                // throw error if line numbers don't match
157                // MSU Contribution DTT-3014 OLEMI-8483 OLECNTRB-974
158                /*
159                 * List items = po.getItems(); if (items != null) { for (Object object : items) { PurchaseOrderItem item =
160                 * (PurchaseOrderItem) object; if (item != null && item.getItemLineNumber().equals(this.getItemLineNumber())) { poi
161                 * = item; break; } } }
162                 */
163            } else {
164                poi = (PurchaseOrderItem) SpringContext.getBean(PurapService.class).getBelowTheLineByType(po, this.getItemType());
165            }
166            if (poi != null) {
167                return poi;
168            } else {
169                if (LOG.isDebugEnabled()) {
170                    LOG.debug("getPurchaseOrderItem() Returning null because PurchaseOrderItem object for line number" + getItemLineNumber() + "or itemType " + getItemTypeCode() + " is null");
171                }
172                return null;
173            }
174        } else {
175
176            LOG.error("getPurchaseOrderItem() Returning null because paymentRequest object is null");
177            throw new PurError("Payment Request Object in Purchase Order item line number " + getItemLineNumber() + "or itemType " + getItemTypeCode() + " is null");
178        }
179    }
180
181    public KualiDecimal getPoOutstandingAmount() {
182        PurchaseOrderItem poi = getPurchaseOrderItem();
183        if (ObjectUtils.isNull(this.getPurchaseOrderItemUnitPrice()) || KualiDecimal.ZERO.equals(this.getPurchaseOrderItemUnitPrice())) {
184            return null;
185        } else {
186            return this.getPoOutstandingAmount(poi);
187        }
188    }
189
190    private KualiDecimal getPoOutstandingAmount(PurchaseOrderItem poi) {
191        if (poi == null) {
192            return KualiDecimal.ZERO;
193        } else {
194            return poi.getItemOutstandingEncumberedAmount();
195        }
196    }
197
198    public KualiDecimal getPoOriginalAmount() {
199        PurchaseOrderItem poi = getPurchaseOrderItem();
200        if (poi == null) {
201            return null;
202        } else {
203            return poi.getExtendedPrice();
204        }
205    }
206
207    /**
208     * Exists due to a setter requirement by the htmlControlAttribute
209     *
210     * @param amount - po outstanding amount
211     * @deprecated
212     */
213    @Deprecated
214    public void setPoOutstandingAmount(KualiDecimal amount) {
215        // do nothing
216    }
217
218
219    public KualiDecimal getPoOutstandingQuantity() {
220        PurchaseOrderItem poi = getPurchaseOrderItem();
221        if (poi == null) {
222            return null;
223        } else {
224            if (PurapConstants.ItemTypeCodes.ITEM_TYPE_SERVICE_CODE.equals(this.getItemTypeCode())) {
225                return null;
226            } else {
227                return poi.getOutstandingQuantity();
228            }
229        }
230    }
231
232    /**
233     * Exists due to a setter requirement by the htmlControlAttribute
234     *
235     * @param qty - po outstanding quantity
236     * @deprecated
237     */
238    @Deprecated
239    public void setPoOutstandingQuantity(KualiDecimal qty) {
240        // do nothing
241    }
242
243    public BigDecimal getPurchaseOrderItemUnitPrice() {
244        return purchaseOrderItemUnitPrice;
245    }
246
247    public BigDecimal getOriginalAmountfromPO() {
248        return purchaseOrderItemUnitPrice;
249    }
250
251    public void setOriginalAmountfromPO(BigDecimal purchaseOrderItemUnitPrice) {
252        // Do nothing
253    }
254
255    public void setPurchaseOrderItemUnitPrice(BigDecimal purchaseOrderItemUnitPrice) {
256        this.purchaseOrderItemUnitPrice = purchaseOrderItemUnitPrice;
257    }
258
259    public KualiDecimal getItemOutstandingInvoiceAmount() {
260        return itemOutstandingInvoiceAmount;
261    }
262
263    public void setItemOutstandingInvoiceAmount(KualiDecimal itemOutstandingInvoiceAmount) {
264        this.itemOutstandingInvoiceAmount = itemOutstandingInvoiceAmount;
265    }
266
267    public KualiDecimal getItemOutstandingInvoiceQuantity() {
268        return itemOutstandingInvoiceQuantity;
269    }
270
271    public void setItemOutstandingInvoiceQuantity(KualiDecimal itemOutstandingInvoiceQuantity) {
272        this.itemOutstandingInvoiceQuantity = itemOutstandingInvoiceQuantity;
273    }
274
275    public PaymentRequestDocument getPaymentRequest() {
276        if (ObjectUtils.isNotNull(getPurapDocumentIdentifier())) {
277            if (ObjectUtils.isNull(getPurapDocument())) {
278                this.refreshReferenceObject(PurapPropertyConstants.PURAP_DOC);
279            }
280        }
281        return super.getPurapDocument();
282    }
283
284    public void setPaymentRequest(PaymentRequestDocument paymentRequest) {
285        this.setPurapDocument(paymentRequest);
286    }
287
288    public void generateAccountListFromPoItemAccounts(List<PurApAccountingLine> accounts) {
289        for (PurApAccountingLine line : accounts) {
290            PurchaseOrderAccount poa = (PurchaseOrderAccount) line;
291            if (!line.isEmpty()) {
292                getSourceAccountingLines().add(new PaymentRequestAccount(this, poa));
293            }
294        }
295    }
296
297    /**
298     * @see org.kuali.ole.module.purap.businessobject.PurApItem#getAccountingLineClass()
299     */
300    @Override
301    public Class getAccountingLineClass() {
302        return PaymentRequestAccount.class;
303    }
304
305    public boolean isDisplayOnPreq() {
306        PurchaseOrderItem poi = getPurchaseOrderItem();
307        if (ObjectUtils.isNull(poi)) {
308            LOG.debug("poi was null");
309            return false;
310        }
311
312        // if the po item is not active... skip it
313        if (!poi.isItemActiveIndicator()) {
314            if (LOG.isDebugEnabled()) {
315                LOG.debug("poi was not active: " + poi.toString());
316            }
317            return false;
318        }
319
320        ItemType poiType = poi.getItemType();
321
322        if (poiType.isQuantityBasedGeneralLedgerIndicator()) {
323            if (poi.getItemQuantity().isGreaterThan(poi.getItemInvoicedTotalQuantity())) {
324                return true;
325            } else {
326                if (ObjectUtils.isNotNull(this.getItemQuantity()) && this.getItemQuantity().isGreaterThan(KualiDecimal.ZERO)) {
327                    return true;
328                }
329            }
330
331            return false;
332        } else { // not quantity based
333            if (poi.getItemOutstandingEncumberedAmount().isGreaterThan(KualiDecimal.ZERO)) {
334                return true;
335            } else {
336                if (PurApItemUtils.isNonZeroExtended(this)) {
337                    return true;
338                }
339                return false;
340            }
341
342        }
343    }
344
345    /**
346     * sets account line percentage to zero.
347     *
348     * @see org.kuali.ole.module.purap.businessobject.PurApItem#resetAccount()
349     */
350    @Override
351    public void resetAccount() {
352        super.resetAccount();
353        this.getNewSourceLine().setAmount(null);
354        this.getNewSourceLine().setAccountLinePercent(new BigDecimal(0));
355    }
356
357    /**
358     * Added for electronic invoice
359     */
360    public void addToUnitPrice(BigDecimal addThisValue) {
361        if (getItemUnitPrice() == null) {
362            setItemUnitPrice(BigDecimal.ZERO);
363        }
364        BigDecimal addedPrice = getItemUnitPrice().add(addThisValue);
365        setItemUnitPrice(addedPrice);
366    }
367
368    public void addToExtendedPrice(KualiDecimal addThisValue) {
369        if (getExtendedPrice() == null) {
370            setExtendedPrice(KualiDecimal.ZERO);
371        }
372        KualiDecimal addedPrice = getExtendedPrice().add(addThisValue);
373        setExtendedPrice(addedPrice);
374    }
375
376    @Override
377    public Class getUseTaxClass() {
378        return PaymentRequestItemUseTax.class;
379    }
380
381    /**
382     * preq item constructor - Delegate
383     *
384     * @param poi  - purchase order item
385     * @param preq - payment request document
386     */
387    public PaymentRequestItem(OleInvoiceItem poi, OlePaymentRequestDocument preq) {
388        this(poi, preq, new HashMap<String, ExpiredOrClosedAccountEntry>());
389    }
390
391
392    /**
393     * Constructs a new payment request item, but also merges expired accounts.
394     *
395     * @param poi                        - purchase order item
396     * @param preq                       - payment request document
397     * @param expiredOrClosedAccountList - list of expired or closed accounts to merge
398     */
399    public PaymentRequestItem(OleInvoiceItem poi, OlePaymentRequestDocument preq, HashMap<String, ExpiredOrClosedAccountEntry> expiredOrClosedAccountList) {
400
401        // copy base attributes w/ extra array of fields not to be copied
402        PurApObjectUtils.populateFromBaseClass(PurApItemBase.class, poi, this, PurapConstants.PREQ_ITEM_UNCOPYABLE_FIELDS);
403
404        setItemDescription(poi.getItemDescription());
405
406        //New Source Line should be set for PaymentRequestItem
407        resetAccount();
408
409        // set up accounts
410        List accounts = new ArrayList();
411
412        for (PurApAccountingLine account : poi.getSourceAccountingLines()) {
413            InvoiceAccount poa = (InvoiceAccount) account;
414
415            // check if this account is expired/closed and replace as needed
416            SpringContext.getBean(AccountsPayableService.class).processExpiredOrClosedAccount(poa, expiredOrClosedAccountList);
417
418            //KFSMI-4522 copy an accounting line with zero dollar amount if system parameter allows
419            if (poa.getAmount().isZero()) {
420                if (SpringContext.getBean(AccountsPayableService.class).canCopyAccountingLinesWithZeroAmount()) {
421                    accounts.add(new PaymentRequestAccount(this, poa));
422                }
423            } else {
424                accounts.add(new PaymentRequestAccount(this, poa));
425            }
426        }
427
428        this.setSourceAccountingLines(accounts);
429        this.getUseTaxItems().clear();
430        //List<PurApItemUseTax> newUseTaxItems = new ArrayList<PurApItemUseTax>();
431        /// this.setUseTaxItems(newUseTaxItems);
432        //copy use tax items over, and blank out keys (useTaxId and itemIdentifier)
433        /*
434        this.getUseTaxItems().clear();
435        for (PurApItemUseTax useTaxItem : poi.getUseTaxItems()) {
436            PaymentRequestItemUseTax newItemUseTax = new PaymentRequestItemUseTax(useTaxItem);
437            this.getUseTaxItems().add(newItemUseTax);
438
439        }
440        */
441
442        // clear amount and desc on below the line - we probably don't need that null
443        // itemType check but it's there just in case remove if it causes problems
444        // also do this if of type service
445        if ((ObjectUtils.isNotNull(this.getItemType()) && this.getItemType().isAmountBasedGeneralLedgerIndicator())) {
446            // setting unit price to be null to be more consistent with other below the line
447            // this.setItemUnitPrice(null);
448        }
449
450        // copy custom
451        /*Modified for the jira -5458*/
452        this.purchaseOrderItemUnitPrice = poi.getPurchaseOrderItem()!=null ? poi.getPurchaseOrderItem().getItemUnitPrice() : null;
453//        this.purchaseOrderCommodityCode = poi.getPurchaseOrderCommodityCd();
454
455        // set doc fields
456        this.setPurapDocumentIdentifier(preq.getPurapDocumentIdentifier());
457        this.setPurapDocument(preq);
458    }
459
460}