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    package org.kuali.kfs.fp.businessobject;
017    
018    import java.util.ArrayList;
019    import java.util.HashMap;
020    import java.util.Iterator;
021    import java.util.LinkedHashMap;
022    import java.util.List;
023    import java.util.Map;
024    
025    import org.kuali.kfs.sys.KFSConstants;
026    import org.kuali.kfs.sys.context.SpringContext;
027    import org.kuali.rice.kns.bo.TransientBusinessObjectBase;
028    import org.kuali.rice.kns.service.DateTimeService;
029    import org.kuali.rice.kns.util.KualiDecimal;
030    
031    /**
032     * This class represents a cashiering-related transaction used in the cash management document
033     */
034    public class CashieringTransaction extends TransientBusinessObjectBase {
035        public static final String DETAIL_DOCUMENT_TYPE = "CM";
036    
037        private String campusCode;
038        private String referenceFinancialDocumentNumber;
039    
040        // money in properties
041        private List<Check> moneyInChecks;
042        private CoinDetail moneyInCoin;
043        private CurrencyDetail moneyInCurrency;
044        private CashieringItemInProcess newItemInProcess;
045        private List<Check> baselineChecks;
046        private Check newCheck;
047        private KualiDecimal checkTotal;
048    
049        // money out properties
050        private CoinDetail moneyOutCoin;
051        private CurrencyDetail moneyOutCurrency;
052        private List<CashieringItemInProcess> openItemsInProcess;
053    
054        private java.util.Date transactionStarted;
055        private java.util.Date transactionEnded;
056    
057        // incrementers for detail lines
058        private Integer nextCheckSequenceId;
059    
060    
061        /**
062         * Constructs a CashieringTransaction
063         */
064        public CashieringTransaction(String campusCode, String referenceFinancialDocumentNumber) {
065            super();
066            this.campusCode = campusCode;
067            this.referenceFinancialDocumentNumber = referenceFinancialDocumentNumber;
068            this.transactionStarted = SpringContext.getBean(DateTimeService.class).getCurrentDate();
069    
070            moneyInCoin = new CoinDetail();
071            moneyInCurrency = new CurrencyDetail();
072    
073            moneyOutCoin = new CoinDetail();
074            moneyOutCurrency = new CurrencyDetail();
075    
076            newItemInProcess = new CashieringItemInProcess();
077            moneyInChecks = new ArrayList<Check>();
078            newCheck = new CheckBase();
079            baselineChecks = new ArrayList<Check>();
080            openItemsInProcess = new ArrayList<CashieringItemInProcess>();
081            nextCheckSequenceId = new Integer(1);
082        }
083    
084        /**
085         * Gets the moneyInChecks attribute.
086         * 
087         * @return Returns the moneyInChecks.
088         */
089        public List<Check> getMoneyInChecks() {
090            return moneyInChecks;
091        }
092    
093        /**
094         * Sets the moneyInChecks attribute value.
095         * 
096         * @param moneyInChecks The moneyInChecks to set.
097         */
098        public void setMoneyInChecks(List<Check> moneyInChecks) {
099            this.moneyInChecks = moneyInChecks;
100        }
101    
102        /**
103         * Retrieves a specific check from the list, by array index
104         * 
105         * @param index the index of the checks array to retrieve the check from
106         * @return a Check
107         */
108        public Check getMoneyInCheck(int index) {
109            if (index >= moneyInChecks.size()) {
110                for (int i = moneyInChecks.size(); i <= index; i++) {
111                    moneyInChecks.add(createNewCheck());
112                }
113            }
114            return moneyInChecks.get(index);
115        }
116    
117        /**
118         * Gets the moneyInCoin attribute.
119         * 
120         * @return Returns the moneyInCoin.
121         */
122        public CoinDetail getMoneyInCoin() {
123            return moneyInCoin;
124        }
125    
126        /**
127         * Sets the moneyInCoin attribute value.
128         * 
129         * @param moneyInCoin The moneyInCoin to set.
130         */
131        public void setMoneyInCoin(CoinDetail moneyInCoin) {
132            this.moneyInCoin = moneyInCoin;
133        }
134    
135        /**
136         * Gets the moneyInCurrency attribute.
137         * 
138         * @return Returns the moneyInCurrency.
139         */
140        public CurrencyDetail getMoneyInCurrency() {
141            return moneyInCurrency;
142        }
143    
144        /**
145         * Sets the moneyInCurrency attribute value.
146         * 
147         * @param moneyInCurrency The moneyInCurrency to set.
148         */
149        public void setMoneyInCurrency(CurrencyDetail moneyInCurrency) {
150            this.moneyInCurrency = moneyInCurrency;
151        }
152    
153        /**
154         * Gets the moneyOutCoin attribute.
155         * 
156         * @return Returns the moneyOutCoin.
157         */
158        public CoinDetail getMoneyOutCoin() {
159            return moneyOutCoin;
160        }
161    
162        /**
163         * Sets the moneyOutCoin attribute value.
164         * 
165         * @param moneyOutCoin The moneyOutCoin to set.
166         */
167        public void setMoneyOutCoin(CoinDetail moneyOutCoin) {
168            this.moneyOutCoin = moneyOutCoin;
169        }
170    
171        /**
172         * Gets the transactionEnded attribute.
173         * 
174         * @return Returns the transactionEnded.
175         */
176        public java.util.Date getTransactionEnded() {
177            return transactionEnded;
178        }
179    
180        /**
181         * Sets the transactionEnded attribute value.
182         * 
183         * @param transactionEnded The transactionEnded to set.
184         */
185        public void setTransactionEnded(java.util.Date transactionEnded) {
186            this.transactionEnded = transactionEnded;
187        }
188    
189        /**
190         * Gets the transactionStarted attribute.
191         * 
192         * @return Returns the transactionStarted.
193         */
194        public java.util.Date getTransactionStarted() {
195            return transactionStarted;
196        }
197    
198        /**
199         * Sets the transactionStarted attribute value.
200         * 
201         * @param transactionStarted The transactionStarted to set.
202         */
203        public void setTransactionStarted(java.util.Date transactionStarted) {
204            this.transactionStarted = transactionStarted;
205        }
206    
207        /**
208         * Gets the campusCode attribute.
209         * 
210         * @return Returns the campusCode.
211         */
212        public String getCampusCode() {
213            return campusCode;
214        }
215    
216        /**
217         * Sets the campusCode attribute value.
218         * 
219         * @param campusCode The campusCode to set.
220         */
221        public void setCampusCode(String campusCode) {
222            this.campusCode = campusCode;
223        }
224    
225        /**
226         * Sets the moneyOutCurrency attribute value.
227         * 
228         * @param moneyOutCurrency The moneyOutCurrency to set.
229         */
230        public void setMoneyOutCurrency(CurrencyDetail moneyOutCurrency) {
231            this.moneyOutCurrency = moneyOutCurrency;
232        }
233    
234        /**
235         * Gets the referenceFinancialDocumentNumber attribute.
236         * 
237         * @return Returns the referenceFinancialDocumentNumber.
238         */
239        public String getReferenceFinancialDocumentNumber() {
240            return referenceFinancialDocumentNumber;
241        }
242    
243        /**
244         * Sets the referenceFinancialDocumentNumber attribute value.
245         * 
246         * @param referenceFinancialDocumentNumber The referenceFinancialDocumentNumber to set.
247         */
248        public void setReferenceFinancialDocumentNumber(String referenceFinancialDocumentNumber) {
249            this.referenceFinancialDocumentNumber = referenceFinancialDocumentNumber;
250        }
251    
252        /**
253         * Gets the moneyOutCurrency attribute.
254         * 
255         * @return Returns the moneyOutCurrency.
256         */
257        public CurrencyDetail getMoneyOutCurrency() {
258            return moneyOutCurrency;
259        }
260    
261        /**
262         * Gets the newItemInProcess attribute.
263         * 
264         * @return Returns the newItemInProcess.
265         */
266        public CashieringItemInProcess getNewItemInProcess() {
267            return newItemInProcess;
268        }
269    
270        /**
271         * Sets the newItemInProcess attribute value.
272         * 
273         * @param newItemInProcess The newItemInProcess to set.
274         */
275        public void setNewItemInProcess(CashieringItemInProcess newItemInProcess) {
276            this.newItemInProcess = newItemInProcess;
277        }
278    
279        /**
280         * Gets the openItemsInProcess attribute.
281         * 
282         * @return Returns the openItemsInProcess.
283         */
284        public List<CashieringItemInProcess> getOpenItemsInProcess() {
285            return openItemsInProcess;
286        }
287    
288        /**
289         * Sets the openItemsInProcess attribute value.
290         * 
291         * @param openItemsInProcess The openItemsInProcess to set.
292         */
293        public void setOpenItemsInProcess(List<CashieringItemInProcess> openItemsInProcess) {
294            this.openItemsInProcess = openItemsInProcess;
295        }
296    
297        /**
298         * This method returns a single open item in process
299         * 
300         * @return a cashiering item in process
301         */
302        public CashieringItemInProcess getOpenItemInProcess(int index) {
303            extendOpenItemsList(index);
304            return this.openItemsInProcess.get(index);
305        }
306    
307        /**
308         * make the open items in process list bigger, so it doesn't return a null value
309         * 
310         * @param minSize the minsize to make the list
311         */
312        private void extendOpenItemsList(int minSize) {
313            while (this.openItemsInProcess.size() <= minSize) {
314                this.openItemsInProcess.add(new CashieringItemInProcess());
315            }
316        }
317    
318        /**
319         * Gets the newCheck attribute.
320         * 
321         * @return Returns the newCheck.
322         */
323        public Check getNewCheck() {
324            return newCheck;
325        }
326    
327        /**
328         * Sets the newCheck attribute value.
329         * 
330         * @param newCheck The newCheck to set.
331         */
332        public void setNewCheck(Check newCheck) {
333            this.newCheck = newCheck;
334        }
335    
336        /**
337         * This method will make sure that all of the various currency, coin, check, and item in process detail records are populated
338         * with the correct info.
339         */
340        public void prepareForSave() {
341            moneyInCoin.setDocumentNumber(this.referenceFinancialDocumentNumber);
342            moneyInCoin.setFinancialDocumentTypeCode(DETAIL_DOCUMENT_TYPE);
343            moneyInCoin.setCashieringRecordSource(KFSConstants.CurrencyCoinSources.CASH_MANAGEMENT_IN);
344    
345            moneyInCurrency.setDocumentNumber(this.referenceFinancialDocumentNumber);
346            moneyInCurrency.setFinancialDocumentTypeCode(DETAIL_DOCUMENT_TYPE);
347            moneyInCurrency.setCashieringRecordSource(KFSConstants.CurrencyCoinSources.CASH_MANAGEMENT_IN);
348    
349            moneyOutCoin.setDocumentNumber(this.referenceFinancialDocumentNumber);
350            moneyOutCoin.setFinancialDocumentTypeCode(DETAIL_DOCUMENT_TYPE);
351            moneyOutCoin.setCashieringRecordSource(KFSConstants.CurrencyCoinSources.CASH_MANAGEMENT_OUT);
352    
353            moneyOutCurrency.setDocumentNumber(this.referenceFinancialDocumentNumber);
354            moneyOutCurrency.setFinancialDocumentTypeCode(DETAIL_DOCUMENT_TYPE);
355            moneyOutCurrency.setCashieringRecordSource(KFSConstants.CurrencyCoinSources.CASH_MANAGEMENT_OUT);
356    
357            newItemInProcess.setCampusCode(this.campusCode);
358        }
359    
360        /**
361         * @see org.kuali.rice.kns.bo.BusinessObjectBase#toStringMapper()
362         */
363        @Override
364        protected LinkedHashMap toStringMapper() {
365            LinkedHashMap pkMap = new LinkedHashMap();
366            pkMap.put("campusCode", this.campusCode);
367            pkMap.put("referenceFinancialDocumentNumber", this.referenceFinancialDocumentNumber);
368            pkMap.put("transactionStarted", this.transactionStarted);
369            return pkMap;
370        }
371    
372        /**
373         * Gets the checks attribute.
374         * 
375         * @return Returns the checks.
376         */
377        public List getChecks() {
378            return getMoneyInChecks();
379        }
380    
381        /**
382         * Sets the checks attribute value.
383         * 
384         * @param checks The checks to set.
385         */
386        public void setChecks(List checks) {
387            moneyInChecks = new ArrayList<Check>();
388            for (Object o : checks) {
389                moneyInChecks.add((Check) o);
390            }
391        }
392    
393        /**
394         * Gets the number of checks, since Sun doesn't have a direct getter for collection size
395         * 
396         * @return the number of checks
397         */
398        public int getCheckCount() {
399            int count = 0;
400            if (moneyInChecks != null) {
401                count = moneyInChecks.size();
402            }
403            return count;
404        }
405    
406    
407        /**
408         * Adds a new check to the list.
409         * 
410         * @param check
411         */
412        public void addCheck(Check check) {
413            check.setSequenceId(this.nextCheckSequenceId);
414    
415            this.moneyInChecks.add(check);
416    
417            this.nextCheckSequenceId = new Integer(this.nextCheckSequenceId.intValue() + 1);
418        }
419    
420        /**
421         * Retrieve a particular check at a given index in the list of checks.
422         * 
423         * @param index
424         * @return Check
425         */
426        public Check getCheck(int index) {
427            while (this.moneyInChecks.size() <= index) {
428                moneyInChecks.add(createNewCheck());
429            }
430            return (Check) moneyInChecks.get(index);
431        }
432    
433        /**
434         * @param checks
435         * @return Map containing Checks from the given List, indexed by their sequenceId
436         */
437        private Map buildCheckMap(List checks) {
438            Map checkMap = new HashMap();
439    
440            for (Iterator i = checks.iterator(); i.hasNext();) {
441                Check check = (Check) i.next();
442                Integer sequenceId = check.getSequenceId();
443    
444                Object oldCheck = checkMap.put(sequenceId, check);
445    
446                // verify that sequence numbers are unique...
447                if (oldCheck != null) {
448                    throw new IllegalStateException("sequence id collision detected for sequence id " + sequenceId);
449                }
450            }
451    
452            return checkMap;
453        }
454    
455        /**
456         * This method removes a check from the list and updates the total appropriately.
457         * 
458         * @param index
459         */
460        public void removeCheck(int index) {
461            Check check = (Check) moneyInChecks.remove(index);
462            KualiDecimal newTotalCheckAmount = getTotalCheckAmount().subtract(check.getAmount());
463            // if the totalCheckAmount goes negative, bring back to zero.
464            if (newTotalCheckAmount.isNegative()) {
465                newTotalCheckAmount = KualiDecimal.ZERO;
466            }
467        }
468    
469        public KualiDecimal getTotalCheckAmount() {
470            KualiDecimal result = KualiDecimal.ZERO;
471            for (Check c : moneyInChecks) {
472                if (c != null && c.getAmount() != null) {
473                    result = result.add(c.getAmount());
474                }
475            }
476            return result;
477        }
478    
479        /**
480         * Gets the nextCheckSequenceId attribute.
481         * 
482         * @return Returns the nextCheckSequenceId.
483         */
484        public Integer getNextCheckSequenceId() {
485            return nextCheckSequenceId;
486        }
487    
488        /**
489         * Sets the nextCheckSequenceId attribute value.
490         * 
491         * @param nextCheckSequenceId The nextCheckSequenceId to set.
492         */
493        public void setNextCheckSequenceId(Integer nextCheckSequenceId) {
494            this.nextCheckSequenceId = nextCheckSequenceId;
495        }
496    
497        public Check createNewCheck() {
498            Check newCheck = new CheckBase();
499            newCheck.setFinancialDocumentTypeCode(DETAIL_DOCUMENT_TYPE);
500            newCheck.setCashieringRecordSource(KFSConstants.CheckSources.CASH_MANAGEMENT);
501            return newCheck;
502        }
503    
504        /**
505         * This method calculates how much money has been paid back in all items in process
506         * 
507         * @return the calculated amount
508         */
509        public KualiDecimal getPaidBackItemsInProcessAmount() {
510            KualiDecimal amount = KualiDecimal.ZERO;
511            if (this.openItemsInProcess != null) {
512                for (CashieringItemInProcess itemInProcess : this.openItemsInProcess) {
513                    if (itemInProcess.getCurrentPayment() != null && itemInProcess.getCurrentPayment().isGreaterThan(KualiDecimal.ZERO)) {
514                        amount = amount.add(itemInProcess.getCurrentPayment());
515                    }
516                }
517            }
518            return amount;
519        }
520    
521        /**
522         * @return current List of baseline checks for use in update detection
523         */
524        public List getBaselineChecks() {
525            return baselineChecks;
526        }
527    
528        /**
529         * Sets the current List of baseline checks to the given List
530         * 
531         * @param baselineChecks
532         */
533        public void setBaselineChecks(List baselineChecks) {
534            this.baselineChecks = baselineChecks;
535        }
536    
537        /**
538         * @param index
539         * @return true if a baselineCheck with the given index exists
540         */
541        public boolean hasBaselineCheck(int index) {
542            boolean has = false;
543    
544            if ((index >= 0) && (index <= baselineChecks.size())) {
545                has = true;
546            }
547    
548            return has;
549        }
550    
551        /**
552         * Implementation creates empty Checks as a side-effect, so that Struts' efforts to set fields of lines which haven't been
553         * created will succeed rather than causing a NullPointerException.
554         * 
555         * @param index
556         * @return baseline Check at the given index
557         */
558        public Check getBaselineCheck(int index) {
559            while (baselineChecks.size() <= index) {
560                baselineChecks.add(this.createNewCheck());
561            }
562            return (Check) baselineChecks.get(index);
563        }
564    
565        /**
566         * This method calcuates how much money has come in to the "Money In" side of the transaction
567         * 
568         * @return the amount calculated
569         */
570        public KualiDecimal getMoneyInTotal() {
571            KualiDecimal result = KualiDecimal.ZERO;
572            result = result.add(this.moneyInCurrency.getTotalAmount());
573            result = result.add(this.moneyInCoin.getTotalAmount());
574            result = result.add(this.getTotalCheckAmount());
575            if (this.newItemInProcess.isPopulated()) {
576                result = result.add(this.newItemInProcess.getItemAmount());
577            }
578            return result;
579        }
580    
581        /**
582         * This method calculates how much money has gone out through the "Money Out" side of the transaction
583         * 
584         * @return the amount calculated
585         */
586        public KualiDecimal getMoneyOutTotal() {
587            KualiDecimal result = KualiDecimal.ZERO;
588            result = result.add(this.moneyOutCurrency.getTotalAmount());
589            result = result.add(this.moneyOutCoin.getTotalAmount());
590            result = result.add(this.getPaidBackItemsInProcessAmount());
591            return result;
592        }
593    
594        /**
595         * @param checkTotal
596         * @deprecated
597         */
598        public void setCheckTotal(KualiDecimal checkTotal) {
599            this.checkTotal = checkTotal;
600        }
601    
602    }