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.module.endow.document.validation.impl;
017    
018    import java.math.BigDecimal;
019    import java.util.ArrayList;
020    import java.util.HashMap;
021    import java.util.List;
022    import java.util.Map;
023    
024    import org.apache.commons.lang.StringUtils;
025    import org.kuali.kfs.module.endow.EndowConstants;
026    import org.kuali.kfs.module.endow.EndowKeyConstants;
027    import org.kuali.kfs.module.endow.EndowPropertyConstants;
028    import org.kuali.kfs.module.endow.businessobject.EndowmentSourceTransactionLine;
029    import org.kuali.kfs.module.endow.businessobject.EndowmentTransactionCode;
030    import org.kuali.kfs.module.endow.businessobject.EndowmentTransactionLine;
031    import org.kuali.kfs.module.endow.businessobject.EndowmentTransactionSecurity;
032    import org.kuali.kfs.module.endow.businessobject.EndowmentTransactionTaxLotLine;
033    import org.kuali.kfs.module.endow.businessobject.HoldingTaxLot;
034    import org.kuali.kfs.module.endow.businessobject.KEMID;
035    import org.kuali.kfs.module.endow.businessobject.KEMIDCurrentAvailableBalance;
036    import org.kuali.kfs.module.endow.businessobject.Security;
037    import org.kuali.kfs.module.endow.document.EndowmentSecurityDetailsDocumentBase;
038    import org.kuali.kfs.module.endow.document.EndowmentTransactionLinesDocument;
039    import org.kuali.kfs.module.endow.document.EndowmentTransactionLinesDocumentBase;
040    import org.kuali.kfs.module.endow.document.EndowmentTransactionalDocument;
041    import org.kuali.kfs.module.endow.document.SecurityTransferDocument;
042    import org.kuali.kfs.module.endow.document.service.EndowmentTransactionCodeService;
043    import org.kuali.kfs.module.endow.document.service.EndowmentTransactionDocumentService;
044    import org.kuali.kfs.module.endow.document.service.EndowmentTransactionLinesDocumentService;
045    import org.kuali.kfs.module.endow.document.service.HoldingTaxLotService;
046    import org.kuali.kfs.module.endow.document.service.KEMIDService;
047    import org.kuali.kfs.module.endow.document.validation.AddTransactionLineRule;
048    import org.kuali.kfs.module.endow.document.validation.DeleteTransactionLineRule;
049    import org.kuali.kfs.module.endow.document.validation.RefreshTransactionLineRule;
050    import org.kuali.kfs.sys.KFSKeyConstants;
051    import org.kuali.kfs.sys.context.SpringContext;
052    import org.kuali.rice.kns.document.Document;
053    import org.kuali.rice.kns.service.BusinessObjectService;
054    import org.kuali.rice.kns.service.DictionaryValidationService;
055    import org.kuali.rice.kns.util.AbstractKualiDecimal;
056    import org.kuali.rice.kns.util.GlobalVariables;
057    import org.kuali.rice.kns.util.KualiDecimal;
058    import org.kuali.rice.kns.util.ObjectUtils;
059    
060    public class EndowmentTransactionLinesDocumentBaseRules extends EndowmentTransactionalDocumentBaseRule implements AddTransactionLineRule<EndowmentTransactionLinesDocument, EndowmentTransactionLine>, DeleteTransactionLineRule<EndowmentTransactionLinesDocument, EndowmentTransactionLine>, RefreshTransactionLineRule<EndowmentTransactionLinesDocument, EndowmentTransactionLine, Number> {
061    
062    
063        /**
064         * @see org.kuali.kfs.module.endow.document.validation.DeleteTransactionLineRule#processDeleteTransactionLineRules(org.kuali.kfs.module.endow.document.EndowmentTransactionLinesDocument,
065         *      org.kuali.kfs.module.endow.businessobject.EndowmentTransactionLine)
066         */
067        public boolean processDeleteTransactionLineRules(EndowmentTransactionLinesDocument endowmentTransactionLinesDocument, EndowmentTransactionLine endowmentTransactionLine) {
068            return true;
069        }
070    
071        /**
072         * @see org.kuali.kfs.module.endow.document.validation.AddTransactionLineRule#processAddTransactionLineRules(org.kuali.kfs.module.endow.document.EndowmentTransactionLinesDocument,
073         *      org.kuali.kfs.module.endow.businessobject.EndowmentTransactionLine)
074         */
075        public boolean processAddTransactionLineRules(EndowmentTransactionLinesDocument document, EndowmentTransactionLine line) {
076            return validateTransactionLine((EndowmentTransactionLinesDocumentBase) document, line, -1);
077        }
078    
079        /**
080         * @see org.kuali.kfs.module.endow.document.validation.RefreshTransactionLineRule#processRefreshTransactionLineRules(org.kuali.kfs.module.endow.document.EndowmentTransactionLinesDocument,
081         *      org.kuali.kfs.module.endow.businessobject.EndowmentTransactionLine, java.lang.Number)
082         */
083        public boolean processRefreshTransactionLineRules(EndowmentTransactionLinesDocument endowmentTransactionLinesDocument, EndowmentTransactionLine endowmentTransactionLine, Number index) {
084            return validateTransactionLine((EndowmentTransactionLinesDocumentBase) endowmentTransactionLinesDocument, endowmentTransactionLine, (Integer) index);
085        }
086    
087        /**
088         * @see org.kuali.rice.kns.rules.DocumentRuleBase#processCustomRouteDocumentBusinessRules(org.kuali.rice.kns.document.Document)
089         */
090        @Override
091        protected boolean processCustomRouteDocumentBusinessRules(Document document) {
092            boolean isValid = super.processCustomRouteDocumentBusinessRules(document);
093            isValid &= !GlobalVariables.getMessageMap().hasErrors();
094    
095            EndowmentTransactionLinesDocumentBase endowmentTransactionLinesDocumentBase = null;
096    
097            if (isValid) {
098                endowmentTransactionLinesDocumentBase = (EndowmentTransactionLinesDocumentBase) document;
099    
100                // validate source transaction lines
101                if (endowmentTransactionLinesDocumentBase.getSourceTransactionLines() != null) {
102                    for (int i = 0; i < endowmentTransactionLinesDocumentBase.getSourceTransactionLines().size(); i++) {
103                        EndowmentTransactionLine transactionLine = endowmentTransactionLinesDocumentBase.getSourceTransactionLines().get(i);
104                        validateTransactionLine(endowmentTransactionLinesDocumentBase, transactionLine, i);
105                    }
106                }
107    
108                // validate target transaction lines
109                if (endowmentTransactionLinesDocumentBase.getTargetTransactionLines() != null) {
110                    for (int i = 0; i < endowmentTransactionLinesDocumentBase.getTargetTransactionLines().size(); i++) {
111                        EndowmentTransactionLine transactionLine = endowmentTransactionLinesDocumentBase.getTargetTransactionLines().get(i);
112                        validateTransactionLine(endowmentTransactionLinesDocumentBase, transactionLine, i);
113                    }
114                }
115    
116            }
117    
118            return GlobalVariables.getMessageMap().getErrorCount() == 0;
119        }
120    
121        /**
122         * This method obtains Prefix for Error fields in UI.
123         * 
124         * @param line
125         * @param index
126         * @return
127         */
128        public String getErrorPrefix(EndowmentTransactionLine line, int index) {
129            String ERROR_PREFIX = null;
130            if (line instanceof EndowmentSourceTransactionLine) {
131                if (index == -1) {
132                    ERROR_PREFIX = EndowPropertyConstants.SOURCE_TRANSACTION_LINE_PREFIX;
133                }
134                else {
135                    ERROR_PREFIX = EndowPropertyConstants.EXISTING_SOURCE_TRANSACTION_LINE_PREFIX + "[" + index + "].";
136                }
137            }
138            else {
139                if (index == -1) {
140                    ERROR_PREFIX = EndowPropertyConstants.TARGET_TRANSACTION_LINE_PREFIX;
141                }
142                else {
143                    ERROR_PREFIX = EndowPropertyConstants.EXISTING_TARGET_TRANSACTION_LINE_PREFIX + "[" + index + "].";
144                }
145            }
146            return ERROR_PREFIX;
147    
148        }
149    
150        /**
151         * This method obtains security code from a document.
152         * 
153         * @param endowmentTransactionLinesDocumentBase
154         * @param line
155         * @return
156         */
157        public String getSecurityIDForValidation(EndowmentTransactionLinesDocument endowmentTransactionLinesDocumentBase, boolean isSource) {
158            EndowmentSecurityDetailsDocumentBase document = (EndowmentSecurityDetailsDocumentBase) endowmentTransactionLinesDocumentBase;
159            if (isSource)
160                return document.getSourceTransactionSecurity().getSecurityID();
161            else
162                return document.getTargetTransactionSecurity().getSecurityID();
163        }
164    
165        /**
166         * This method obtains Registration code from a document.
167         * 
168         * @param endowmentTransactionLinesDocumentBase
169         * @param line
170         * @return
171         */
172        public String getRegistrationForValidation(EndowmentTransactionLinesDocument endowmentTransactionLinesDocumentBase, boolean isSource) {
173            EndowmentSecurityDetailsDocumentBase document = (EndowmentSecurityDetailsDocumentBase) endowmentTransactionLinesDocumentBase;
174            if (isSource)
175                return document.getSourceTransactionSecurity().getRegistrationCode();
176            else
177                return document.getTargetTransactionSecurity().getRegistrationCode();
178        }
179    
180        /**
181         * This method obtains security from a document.
182         * 
183         * @param endowmentTransactionLinesDocumentBase
184         * @param line
185         * @return
186         */
187        public Security getSecurityForValidation(EndowmentTransactionLinesDocument endowmentTransactionLinesDocumentBase, boolean isSource) {
188            EndowmentSecurityDetailsDocumentBase document = (EndowmentSecurityDetailsDocumentBase) endowmentTransactionLinesDocumentBase;
189            if (isSource)
190                return document.getSourceTransactionSecurity().getSecurity();
191            else
192                return document.getTargetTransactionSecurity().getSecurity();
193        }
194    
195        /**
196         * This method validates a transaction line.
197         * 
198         * @param line
199         * @param index
200         * @return
201         */
202        protected boolean validateTransactionLine(EndowmentTransactionLinesDocument endowmentTransactionLinesDocument, EndowmentTransactionLine line, int index) {
203            boolean isValid = true;
204            int originalErrorCount = GlobalVariables.getMessageMap().getErrorCount();
205            getDictionaryValidationService().validateBusinessObject(line);
206           
207            isValid &= GlobalVariables.getMessageMap().getErrorCount() == originalErrorCount;
208    
209            String ERROR_PREFIX = getErrorPrefix(line, index);
210    
211            if (isValid) {
212                GlobalVariables.getMessageMap().clearErrorPath();
213    
214                // General not null validation for KemID
215                SpringContext.getBean(DictionaryValidationService.class).validateAttributeRequired(line.getClass().getName(), "kemid", line.getKemid(), false, ERROR_PREFIX + EndowPropertyConstants.KEMID);
216    
217                // Validate KemID
218                if (!validateKemId(line, ERROR_PREFIX))
219                    return false;
220    
221                // Active Kemid
222                isValid &= isActiveKemId(line, ERROR_PREFIX);
223    
224                // Validate no restriction transaction restriction
225                isValid &= validateNoTransactionRestriction(line, ERROR_PREFIX);
226    
227                // Validate Income/Principal DropDown
228                SpringContext.getBean(DictionaryValidationService.class).validateAttributeRequired(line.getClass().getName(), "transactionIPIndicatorCode", line.getTransactionIPIndicatorCode(), false, ERROR_PREFIX + EndowPropertyConstants.TRANSACTION_IPINDICATOR);
229                isValid &= GlobalVariables.getMessageMap().getErrorCount() == 0 ? true : false;
230                if (!isValid)
231                    return isValid;
232    
233                // This error is checked in addition save rule method since the sub type is used for determining chart code.
234                if (!isSubTypeEmpty(endowmentTransactionLinesDocument))
235                    return false;
236    
237                // If non-cash transactions
238                if (nonCashTransaction(endowmentTransactionLinesDocument) && hasEtranCode(endowmentTransactionLinesDocument)) {
239                    // Is Etran code empty
240                    if (isEndowmentTransactionCodeEmpty(line, ERROR_PREFIX))
241                        return false;
242    
243                    // Validate ETran code
244                    if (!validateEndowmentTransactionCode(line, ERROR_PREFIX))
245                        return false;
246    
247                    // Validate ETran code as E or I
248                    isValid &= validateEndowmentTransactionTypeCode(endowmentTransactionLinesDocument, line, ERROR_PREFIX);
249    
250                    // Validate if a KEMID can have a principal transaction when IP indicator is P
251                    if (!canKEMIDHaveAPrincipalTransaction(line, ERROR_PREFIX))
252                        return false;
253    
254                    // Validate if the chart is matched between the KEMID and EtranCode
255                    isValid &= validateChartMatch(line, ERROR_PREFIX);
256    
257                    // Set Corpus Indicator
258                    line.setCorpusIndicator(SpringContext.getBean(EndowmentTransactionLinesDocumentService.class).getCorpusIndicatorValueforAnEndowmentTransactionLine(line.getKemid(), line.getEtranCode(), line.getTransactionIPIndicatorCode()));
259                }
260    
261                // Refresh all references for the given KemId
262                // line.getKemidObj().refreshNonUpdateableReferences();
263    
264    
265            }
266    
267            return GlobalVariables.getMessageMap().getErrorCount() == 0;
268        }
269    
270        /**
271         * This method checks if this is a non-cash transaction.
272         * 
273         * @param endowmentTransactionLinesDocumentBase
274         * @return
275         */
276        protected boolean nonCashTransaction(EndowmentTransactionLinesDocument endowmentTransactionLinesDocument) {
277            if (EndowConstants.TransactionSubTypeCode.NON_CASH.equalsIgnoreCase(endowmentTransactionLinesDocument.getTransactionSubTypeCode()))
278                return true;
279            else
280                return false;
281        }
282    
283        /**
284         * Tells if the document has an etran code. Override this method in the BR class specific to your document to return whether the
285         * document has etran code or not.
286         * 
287         * @param endowmentTransactionLinesDocument
288         * @return true by default. It should return true if the document has an etran code or false otherwise.
289         */
290        protected boolean hasEtranCode(EndowmentTransactionLinesDocument endowmentTransactionLinesDocument) {
291            return true;
292        }
293    
294        /**
295         * This method validates the KEMID code.
296         * 
297         * @param tranSecurity
298         * @return
299         */
300        protected boolean isKemIdCodeEmpty(EndowmentTransactionLine line, String prefix) {
301            if (StringUtils.isEmpty(line.getKemid())) {
302                putFieldError(prefix + EndowPropertyConstants.KEMID, EndowKeyConstants.EndowmentTransactionDocumentConstants.ERROR_TRANSACTION_LINE_KEMID_REQUIRED);
303                return true;
304            }
305    
306            return false;
307        }
308    
309        /**
310         * This method validates the KemId code and tries to create a KEMID object from the code.
311         * 
312         * @param line
313         * @return
314         */
315        protected boolean validateKemId(EndowmentTransactionLine line, String prefix) {
316            boolean success = true;
317    
318            KEMID kemId = (KEMID) SpringContext.getBean(KEMIDService.class).getByPrimaryKey(line.getKemid());
319            line.setKemidObj(kemId);
320            if (null == kemId) {
321                putFieldError(prefix + EndowPropertyConstants.KEMID, EndowKeyConstants.EndowmentTransactionDocumentConstants.ERROR_TRANSACTION_LINE_KEMID_INVALID);
322                success = false;
323            }
324    
325            return success;
326        }
327    
328        /**
329         * This method determines if the KEMID is active.
330         * 
331         * @param line
332         * @return
333         */
334        protected boolean isActiveKemId(EndowmentTransactionLine line, String prefix) {
335            if (line.getKemidObj().isClose()) {
336                putFieldError(prefix + EndowPropertyConstants.KEMID, EndowKeyConstants.EndowmentTransactionDocumentConstants.ERROR_TRANSACTION_LINE_KEMID_INACTIVE);
337                return false;
338    
339            }
340            else {
341                return true;
342            }
343        }
344    
345        /**
346         * This method checks if the KEMID restriction code is "NTRAN"
347         * 
348         * @param line
349         * @return
350         */
351        protected boolean validateNoTransactionRestriction(EndowmentTransactionLine line, String prefix) {
352            if (line.getKemidObj().getTransactionRestrictionCode().equalsIgnoreCase(EndowConstants.TransactionRestrictionCode.TRAN_RESTR_CD_NTRAN)) {
353                putFieldError(prefix + EndowPropertyConstants.KEMID, EndowKeyConstants.EndowmentTransactionDocumentConstants.ERROR_TRANSACTION_LINE_KEMID_NO_TRAN_CODE);
354                return false;
355            }
356            else {
357                return true;
358            }
359    
360        }
361    
362        /**
363         * This method checks is the Transaction amount entered is greater than Zero.
364         * 
365         * @param line
366         * @return
367         */
368        protected boolean validateTransactionAmountGreaterThanZero(EndowmentTransactionLine line, String prefix) {
369            if (line.getTransactionAmount() != null && line.getTransactionAmount().isGreaterThan(AbstractKualiDecimal.ZERO))
370                return true;
371            else {
372                putFieldError(prefix + EndowPropertyConstants.TRANSACTION_LINE_TRANSACTION_AMOUNT, EndowKeyConstants.EndowmentTransactionDocumentConstants.ERROR_TRANSACTION_LINE_AMOUNT_GREATER_THAN_ZERO);
373                return false;
374            }
375        }
376    
377        /**
378         * This method checks is the Transaction amount entered is greater than Zero.
379         * 
380         * @param line
381         * @return
382         */
383        protected boolean validateTransactionAmountLessThanZero(EndowmentTransactionLine line, String prefix) {
384            if (line.getTransactionAmount() != null && line.getTransactionAmount().isLessThan(AbstractKualiDecimal.ZERO))
385                return true;
386            else {
387                putFieldError(prefix + EndowPropertyConstants.TRANSACTION_LINE_TRANSACTION_AMOUNT, EndowKeyConstants.EndowmentTransactionDocumentConstants.ERROR_TRANSACTION_LINE_AMOUNT_LESS_THAN_ZERO);
388                return false;
389            }
390        }
391    
392        /**
393         * This method checks is the Transaction Units entered is greater than Zero.
394         * 
395         * @param line
396         * @return
397         */
398        protected boolean validateTransactionUnitsGreaterThanZero(EndowmentTransactionLine line, String prefix) {
399            if (line.getTransactionUnits() != null && line.getTransactionUnits().isGreaterThan(AbstractKualiDecimal.ZERO))
400                return true;
401            else {
402                putFieldError(prefix + EndowPropertyConstants.TRANSACTION_LINE_TRANSACTION_UNITS, EndowKeyConstants.EndowmentTransactionDocumentConstants.ERROR_TRANSACTION_UNITS_GREATER_THAN_ZERO);
403                return false;
404            }
405        }
406    
407        /**
408         * This method checks is the Transaction Units entered is greater than Zero.
409         * 
410         * @param line
411         * @return
412         */
413        protected boolean validateTransactionUnitsLessThanZero(EndowmentTransactionLine line, String prefix) {
414            if (line.getTransactionUnits() != null && line.getTransactionUnits().isLessThan(AbstractKualiDecimal.ZERO))
415                return true;
416            else {
417                putFieldError(prefix + EndowPropertyConstants.TRANSACTION_LINE_TRANSACTION_UNITS, EndowKeyConstants.EndowmentTransactionDocumentConstants.ERROR_TRANSACTION_UNITS_LESS_THAN_ZERO);
418                return false;
419            }
420        }
421    
422        /**
423         * This method checks is the Transaction Units & Amount entered are equal.
424         * 
425         * @param line
426         * @param prefix
427         * @return
428         */
429        protected boolean validateTransactionUnitsAmountEqual(EndowmentTransactionLine line, String prefix) {
430            if (line.getTransactionUnits() != null && line.getTransactionAmount() != null && line.getTransactionUnits().compareTo(line.getTransactionAmount()) == 0)
431                return true;
432            else {
433                putFieldError(prefix + EndowPropertyConstants.TRANSACTION_LINE_TRANSACTION_AMOUNT, EndowKeyConstants.EndowmentTransactionDocumentConstants.ERROR_TRANSACTION_AMOUNT_UNITS_EQUAL);
434                return false;
435            }
436        }
437    
438        /**
439         * This method checks if the ETRAN code has a type code of E or I.
440         * 
441         * @param line
442         * @return
443         */
444        protected boolean validateEndowmentTransactionTypeCode(EndowmentTransactionLinesDocument endowmentTransactionLinesDocument, EndowmentTransactionLine line, String prefix) {
445            if (line.getEtranCodeObj().getEndowmentTransactionTypeCode().equalsIgnoreCase(EndowConstants.EndowmentTransactionTypeCodes.INCOME_TYPE_CODE) || line.getEtranCodeObj().getEndowmentTransactionTypeCode().equalsIgnoreCase(EndowConstants.EndowmentTransactionTypeCodes.EXPENSE_TYPE_CODE))
446                return true;
447            else {
448                putFieldError(prefix + EndowPropertyConstants.TRANSACTION_LINE_ENDOWMENT_TRANSACTION_CODE, EndowKeyConstants.EndowmentTransactionDocumentConstants.ERROR_ENDOWMENT_TRANSACTION_TYPE_CODE_VALIDITY);
449                return false;
450            }
451        }
452    
453        /**
454         * This method validates the EndowmentTransaction code.
455         * 
456         * @param tranSecurity
457         * @return
458         */
459        protected boolean isEndowmentTransactionCodeEmpty(EndowmentTransactionLine line, String prefix) {
460            if (StringUtils.isEmpty(line.getEtranCode())) {
461                putFieldError(prefix + EndowPropertyConstants.TRANSACTION_LINE_ENDOWMENT_TRANSACTION_CODE, EndowKeyConstants.EndowmentTransactionDocumentConstants.ERROR_TRANSACTION_LINE_ETRAN_REQUIRED);
462                return true;
463            }
464    
465            return false;
466        }
467    
468        /**
469         * This method validates the EndowmentTransaction code and tries to create a EndowmentTransactionCode object from the code.
470         * 
471         * @param line
472         * @return
473         */
474        protected boolean validateEndowmentTransactionCode(EndowmentTransactionLine line, String prefix) {
475            boolean success = true;
476    
477            EndowmentTransactionCode etran = (EndowmentTransactionCode) SpringContext.getBean(EndowmentTransactionCodeService.class).getByPrimaryKey(line.getEtranCode());
478            line.setEtranCodeObj(etran);
479            if (null == etran) {
480                putFieldError(prefix + EndowPropertyConstants.TRANSACTION_LINE_ENDOWMENT_TRANSACTION_CODE, EndowKeyConstants.EndowmentTransactionDocumentConstants.ERROR_TRANSACTION_LINE_ETRAN_INVALID);
481                success = false;
482            }
483    
484            return success;
485        }
486    
487        /**
488         * This method validates if a KEMID can have a principal transaction when IP indicator is equal to P.
489         * 
490         * @param line
491         * @return
492         */
493        protected boolean canKEMIDHaveAPrincipalTransaction(EndowmentTransactionLine line, String prefix) {
494            boolean canHaveTransaction = true;
495            String ipIndicatorCode = line.getTransactionIPIndicatorCode();
496            if (EndowConstants.IncomePrincipalIndicator.PRINCIPAL.equalsIgnoreCase(ipIndicatorCode)) {
497                String kemid = line.getKemid();
498                if (!SpringContext.getBean(EndowmentTransactionLinesDocumentService.class).canKEMIDHaveAPrincipalActivity(kemid, ipIndicatorCode)) {
499                    putFieldError(prefix + EndowPropertyConstants.TRANSACTION_LINE_IP_INDICATOR_CODE, EndowKeyConstants.EndowmentTransactionDocumentConstants.ERROR_TRANSACTION_LINE_KEMID_CAN_NOT_HAVE_A_PRINCIPAL_TRANSACTION);
500                    canHaveTransaction = false;
501                }
502            }
503            return canHaveTransaction;
504        }
505    
506        /**
507         * This method validates if the chart is matched between GL Account in the KEMID and GL Link in the Endowment Transaction Code.
508         * If the Chart codes do not match, the Etran Code field should be highlighted.
509         * 
510         * @param line
511         * @return
512         */
513        protected boolean validateChartMatch(EndowmentTransactionLine line, String prefix) {
514            boolean isChartMatched = true;
515            String kemid = line.getKemid();
516            String etranCode = line.getEtranCode();
517            String ipIndicatorCode = line.getTransactionIPIndicatorCode();
518            if (!SpringContext.getBean(EndowmentTransactionDocumentService.class).matchChartBetweenKEMIDAndETranCode(kemid, etranCode, ipIndicatorCode)) {
519                if (EndowConstants.IncomePrincipalIndicator.PRINCIPAL.equalsIgnoreCase(ipIndicatorCode))
520                    putFieldError(prefix + EndowPropertyConstants.TRANSACTION_LINE_ENDOWMENT_TRANSACTION_CODE, EndowKeyConstants.EndowmentTransactionDocumentConstants.ERROR_TRANSACTION_LINE_CHART_CODE_DOES_NOT_MATCH_FOR_PRINCIPAL);
521                else
522                    putFieldError(prefix + EndowPropertyConstants.TRANSACTION_LINE_ENDOWMENT_TRANSACTION_CODE, EndowKeyConstants.EndowmentTransactionDocumentConstants.ERROR_TRANSACTION_LINE_CHART_CODE_DOES_NOT_MATCH_FOR_INCOME);
523    
524                isChartMatched = false;
525            }
526            return isChartMatched;
527        }
528    
529        /**
530         * Validates that the security chart and the etran code chart match.
531         * 
532         * @param endowmentTransactionLinesDocument
533         * @param line
534         * @param prefix
535         * @param isSource
536         * @return true if valid, false otherwise
537         */
538        protected boolean validateSecurityEtranChartMatch(EndowmentTransactionLinesDocument endowmentTransactionLinesDocument, EndowmentTransactionLine line, String prefix, boolean isSource) {
539            boolean isChartMatched = true;
540            Security security = getSecurityForValidation(endowmentTransactionLinesDocument, isSource);
541            String kemID = line.getKemid();
542            String ipIndicatorCode = line.getTransactionIPIndicatorCode();
543            if (!SpringContext.getBean(EndowmentTransactionDocumentService.class).matchChartBetweenSecurityAndETranCode(security, kemID, ipIndicatorCode)) {
544                if (EndowConstants.IncomePrincipalIndicator.PRINCIPAL.equalsIgnoreCase(ipIndicatorCode))
545                    putFieldError(prefix + EndowPropertyConstants.KEMID, EndowKeyConstants.EndowmentTransactionDocumentConstants.ERROR_TRANSACTION_LINE_SECURITY_KEMID_CHART_CODE_DOES_NOT_MATCH, EndowConstants.PRINCIPAL);
546                else
547                    putFieldError(prefix + EndowPropertyConstants.KEMID, EndowKeyConstants.EndowmentTransactionDocumentConstants.ERROR_TRANSACTION_LINE_SECURITY_KEMID_CHART_CODE_DOES_NOT_MATCH, EndowConstants.INCOME);
548    
549                isChartMatched = false;
550            }
551            return isChartMatched;
552        }
553    
554        /**
555         * For a true endowment, when the END_TRAN_LN_T: TRAN_IP_IND_CD is equal to P, a warning message will be placed in the document
556         * transaction line notifying the viewer that the transaction will reduce the value of the endowment at the time the transaction
557         * line is added. WARNING: This transaction will reduce permanently restricted funds!. However, the transaction line would be
558         * added on successfully.
559         * 
560         * @param line
561         * @return
562         */
563        protected void checkWhetherReducePermanentlyRestrictedFund(EndowmentTransactionLine line, String prefix) {
564            String ipIndicatorCode = line.getTransactionIPIndicatorCode();
565            String kemid = line.getKemid();
566            if (EndowConstants.IncomePrincipalIndicator.PRINCIPAL.equalsIgnoreCase(ipIndicatorCode) && SpringContext.getBean(KEMIDService.class).isTrueEndowment(kemid)) {
567                GlobalVariables.getMessageMap().putWarning(prefix + EndowPropertyConstants.TRANSACTION_LINE_IP_INDICATOR_CODE, EndowKeyConstants.EndowmentTransactionDocumentConstants.WARNING_REDUCE_PERMANENTLY_RESTRICTED_FUNDS);
568            }
569        }
570    
571        /**
572         * Upon adding the transaction line, the system will check to see if there are sufficient funds to process the transaction
573         * (END_AVAIL_CSH_T). If there are not, a warning message will be placed in the document transaction line notifying the viewer
574         * that there are not sufficient funds. -If END_TRAN_LN_T: TRAN_IP_IND_CD is equal to I verify against END_AVAIL_CSH_T:
575         * AVAIL_TOT_CSH -If END_TRAN_LN_T: TRAN_IP_IND_CD is equal to P verify against END_AVAIL_CSH_T: AVAIL_PRIN_CSH However, the
576         * transaction line would be added on successfully.
577         * 
578         * @param line
579         * @return
580         */
581        protected void checkWhetherHaveSufficientFundsForCashBasedTransaction(EndowmentTransactionLine line, String prefix) {
582            String ipIndicatorCode = line.getTransactionIPIndicatorCode();
583            String kemid = line.getKemid();
584            KualiDecimal amount = line.getTransactionAmount();
585    
586            Map criteria = new HashMap();
587            criteria.put(EndowPropertyConstants.KEMID, kemid);
588            KEMIDCurrentAvailableBalance theKEMIDCurrentAvailableBalance = (KEMIDCurrentAvailableBalance) SpringContext.getBean(BusinessObjectService.class).findByPrimaryKey(KEMIDCurrentAvailableBalance.class, criteria);
589    
590            if (ObjectUtils.isNotNull(theKEMIDCurrentAvailableBalance)) {
591                if (EndowConstants.IncomePrincipalIndicator.PRINCIPAL.equalsIgnoreCase(ipIndicatorCode)) {
592                    if (amount.isGreaterThan(new KualiDecimal(theKEMIDCurrentAvailableBalance.getAvailablePrincipalCash()))) {
593                        GlobalVariables.getMessageMap().putWarning(prefix + EndowPropertyConstants.TRANSACTION_LINE_IP_INDICATOR_CODE, EndowKeyConstants.EndowmentTransactionDocumentConstants.WARNING_NO_SUFFICIENT_FUNDS);
594                    }
595                }
596                else {
597                    if (amount.isGreaterThan(new KualiDecimal(theKEMIDCurrentAvailableBalance.getAvailableTotalCash()))) {
598                        GlobalVariables.getMessageMap().putWarning(prefix + EndowPropertyConstants.TRANSACTION_LINE_IP_INDICATOR_CODE, EndowKeyConstants.EndowmentTransactionDocumentConstants.WARNING_NO_SUFFICIENT_FUNDS);
599                    }
600                }
601            }
602        }
603    
604    
605        protected boolean templateMethod(EndowmentTransactionLine line) {
606            boolean success = true;
607    
608            return success;
609        }
610    
611        /**
612         * This methods checks to ensure for cash Tx do not have a Etran.
613         * 
614         * @param endowmentTransactionLinesDocumentBase
615         * @param line
616         * @param prefix
617         * @return
618         */
619        protected boolean checkCashTransactionEndowmentCode(EndowmentTransactionLinesDocument endowmentTransactionLinesDocument, EndowmentTransactionLine line, String prefix) {
620            // For Cash based Tx the Etran code must be empty,If Tx is Cash based, check for Etran code and if not null display Error
621            // message.
622            if (!nonCashTransaction(endowmentTransactionLinesDocument) && (!StringUtils.isEmpty(line.getEtranCode()))) {
623                // 
624                putFieldError(prefix + EndowPropertyConstants.TRANSACTION_LINE_ENDOWMENT_TRANSACTION_CODE, EndowKeyConstants.EndowmentTransactionDocumentConstants.ERROR_TRANSACTION_LINE_ETRAN_BLANK);
625                return false;
626            }
627    
628            return true;
629        }
630    
631        /**
632         * Checks that the document has at least one transaction line.
633         * 
634         * @param document
635         * @param isSource
636         * @return true if valid, false otherwise
637         */
638        protected boolean transactionLineSizeGreaterThanZero(EndowmentTransactionLinesDocumentBase document, boolean isSource) {
639            List<EndowmentTransactionLine> transactionLineList = null;
640            if (isSource) {
641                transactionLineList = document.getSourceTransactionLines();
642                if (transactionLineList == null || transactionLineList.size() == 0) {
643                    putFieldError(EndowPropertyConstants.SOURCE_TRANSACTION_LINE_PREFIX, EndowKeyConstants.EndowmentTransactionDocumentConstants.ERROR_FROM_TRANSACTION_LINE_COUNT_INSUFFICIENT);
644                    return false;
645                }
646            }
647            else {
648                transactionLineList = document.getTargetTransactionLines();
649                if (transactionLineList == null || transactionLineList.size() == 0) {
650                    putFieldError(EndowPropertyConstants.TARGET_TRANSACTION_LINE_PREFIX, EndowKeyConstants.EndowmentTransactionDocumentConstants.ERROR_TO_TRANSACTION_LINE_COUNT_INSUFFICIENT);
651                    return false;
652                }
653            }
654    
655            return true;
656        }
657    
658        /**
659         * This method is a collection if validation performed on Registration Code. The validations are not null & valid registration
660         * code
661         * 
662         * @param isValid
663         * @param liabilityIncreaseDocument
664         * @return
665         */
666        protected boolean validateRegistration(boolean isValid, EndowmentSecurityDetailsDocumentBase document, boolean isSource) {
667            // Checks if registration code is empty
668            if (isRegistrationCodeEmpty(document, isSource))
669                return false;
670    
671            // Validate Registration code.
672            if (!validateRegistrationCode(document, isSource))
673                return false;
674    
675            // Checks if registration code is active
676            isValid &= isRegistrationCodeActive(document, isSource);
677            return isValid;
678        }
679    
680        /**
681         * This method is a collection if validation performed on Security. The validations are not null, valid security,active & class
682         * code matches L
683         * 
684         * @param isValid
685         * @param document
686         * @return
687         */
688        protected boolean validateSecurity(boolean isValid, EndowmentSecurityDetailsDocumentBase document, boolean isSource) {
689            // Checks if Security Code is empty.
690            if (isSecurityCodeEmpty(document, isSource))
691                return false;
692    
693            // Validates Security Code.
694            if (!validateSecurityCode(document, isSource))
695                return false;
696    
697            // Checks if Security is Active
698            isValid &= isSecurityActive(document, isSource);
699    
700            // Validates Security class code
701            isValid &= validateSecurityClassTypeCode(document, isSource, EndowConstants.ClassCodeTypes.LIABILITY);
702            return isValid;
703        }
704    
705        /**
706         * This method validates that the source and target security lines are different from each other.
707         * 
708         * @param document
709         * @return True if source and target security codes are different
710         */
711        protected boolean validateNonDuplicateSecurityCodes(EndowmentSecurityDetailsDocumentBase document) {
712    
713            Security sourceSecurity = getSecurityForValidation(document, true);
714            Security targetSecurity = getSecurityForValidation(document, false);
715    
716            if (sourceSecurity != null && targetSecurity != null) {
717                if (sourceSecurity.getId().equalsIgnoreCase(targetSecurity.getId())) {
718                    putFieldError(getEndowmentTransactionSecurityPrefix(document, false) + EndowPropertyConstants.TRANSACTION_SECURITY_ID, EndowKeyConstants.EndowmentTransactionDocumentConstants.ERROR_TRANSACTION_SECURITY_CODE_EQUAL);
719                }
720            }
721    
722            return true;
723        }
724    
725        /**
726         * Validates that the KEMID has sufficient units in the tax lots to perform the transaction.
727         * 
728         * @param endowmentTransactionLinesDocumentBase
729         * @param line
730         * @param index
731         * @return true if valid, false otherwise
732         */
733        public boolean validateSufficientUnits(boolean isAdd, EndowmentTransactionLinesDocument endowmentTransactionLinesDocument, EndowmentTransactionLine line, int transLineIndex, int taxLotIndex) {
734            EndowmentTransactionSecurity endowmentTransactionSecurity = getEndowmentTransactionSecurity(endowmentTransactionLinesDocument, true);
735            boolean isValid = true;
736            List<HoldingTaxLot> holdingTaxLots = new ArrayList<HoldingTaxLot>();
737    
738            if (isAdd) {
739                holdingTaxLots = SpringContext.getBean(HoldingTaxLotService.class).getAllTaxLots(line.getKemid(), endowmentTransactionSecurity.getSecurityID(), endowmentTransactionSecurity.getRegistrationCode(), line.getTransactionIPIndicatorCode());
740            }
741            else {
742                List<EndowmentTransactionTaxLotLine> existingTransactionLines = line.getTaxLotLines();
743                for (int i = 0; i < existingTransactionLines.size(); i++) {
744                    // don't take into account the tax lot line we are now deleting
745                    if (i != taxLotIndex) {
746    
747                        EndowmentTransactionTaxLotLine endowmentTransactionTaxLotLine = (EndowmentTransactionTaxLotLine) existingTransactionLines.get(i);
748                        HoldingTaxLot holdingTaxLot = SpringContext.getBean(HoldingTaxLotService.class).getByPrimaryKey(line.getKemid(), endowmentTransactionSecurity.getSecurityID(), endowmentTransactionSecurity.getRegistrationCode(), endowmentTransactionTaxLotLine.getTransactionHoldingLotNumber(), line.getTransactionIPIndicatorCode());
749    
750                        if (ObjectUtils.isNotNull(holdingTaxLot)) {
751                            holdingTaxLots.add(holdingTaxLot);
752                        }
753                    }
754                }
755            }
756    
757            BigDecimal totalTaxLotsUnits = BigDecimal.ZERO;
758    
759            if (holdingTaxLots != null && holdingTaxLots.size() > 0) {
760                for (HoldingTaxLot holdingTaxLot : holdingTaxLots) {
761                    totalTaxLotsUnits = totalTaxLotsUnits.add(holdingTaxLot.getUnits());
762                }
763            }
764    
765            BigDecimal lineUnits = null;
766            lineUnits = line.getTransactionUnits().bigDecimalValue();
767    
768            if (lineUnits.compareTo(totalTaxLotsUnits) == 1) {
769                isValid = false;
770                putFieldError(getErrorPrefix(line, transLineIndex) + EndowPropertyConstants.TRANSACTION_LINE_TRANSACTION_UNITS, EndowKeyConstants.EndowmentTransactionDocumentConstants.ERROR_ASSET_DECREASE_INSUFFICIENT_UNITS);
771            }
772            return isValid;
773        }
774    
775        /**
776         * Validates that the tax lots for a transaction line correspond to the information in that transaction line. It might be
777         * possible that the user has changed the KEMID or Security related data without refreshing the tax lot lines. On save we need
778         * to check that the tax lot lines relate to the KEMID in the transaction lines. Take the first tax lot for the transaction line
779         * and check if it is in the holding tax lots for that KEMID. This validation is needed in documents that support tax lot lines
780         * deletion as in that case the tax lot lines are not automatically refreshed on save/submit.
781         * 
782         * @param endowmentTransactionLinesDocument
783         * @param transLine
784         * @param transLineIndex
785         * @return
786         */
787        protected boolean validateTaxLots(EndowmentTransactionLinesDocument endowmentTransactionLinesDocument, EndowmentTransactionLine transLine, int transLineIndex) {
788            boolean isValid = true;
789    
790            EndowmentTransactionSecurity endowmentTransactionSecurity = getEndowmentTransactionSecurity(endowmentTransactionLinesDocument, true);
791    
792            // as it might be possible that the user has changed the KEMID without refreshing the tax lot lines, on save we need to
793            // check that the tax lot lines relate to the KEMID in the transaction lines. Take the first tax lot for the transaction
794            // line and check if it is in the holding tax lots for that KEMID.
795            if (transLine.getTaxLotLines() != null && transLine.getTaxLotLines().size() > 0) {
796                EndowmentTransactionTaxLotLine transactionTaxLotLine = transLine.getTaxLotLines().get(0);
797    
798                boolean isMatchingSecurity = endowmentTransactionSecurity.getSecurityID().equalsIgnoreCase(transactionTaxLotLine.getSecurityID());
799                boolean isMatchingRegistrationCode = endowmentTransactionSecurity.getRegistrationCode().equalsIgnoreCase(transactionTaxLotLine.getRegistrationCode());
800                boolean isMatchingKemid = transLine.getKemid().equalsIgnoreCase(transactionTaxLotLine.getKemid());
801                boolean isMatchingIpIndicator = transLine.getTransactionIPIndicatorCode().equalsIgnoreCase(transactionTaxLotLine.getIpIndicator());
802    
803                if (!isMatchingSecurity || !isMatchingRegistrationCode || !isMatchingKemid || !isMatchingIpIndicator) {
804                    isValid = false;
805                    putFieldError(getErrorPrefix(transLine, transLineIndex) + EndowPropertyConstants.KEMID, EndowKeyConstants.EndowmentTransactionDocumentConstants.ERROR_TRANSACTION_LINE_TAX_LOT_DONT_CORRESPOND);
806                }
807    
808            }
809            return isValid;
810        }
811    
812        /**
813         * Checks that the transaction line units match the tax lot lines total number of units.
814         * 
815         * @param document
816         * @param transactionLine
817         * @param index
818         * @return true if valid, false otherwise
819         */
820        protected boolean validateTotalUnits(EndowmentTransactionalDocument document, EndowmentTransactionLine transactionLine, int index) {
821            boolean isValid = true;
822    
823            BigDecimal transactionLineUnits = transactionLine.getTransactionUnits().bigDecimalValue();
824            BigDecimal taxLotLinesTotalUnits = BigDecimal.ZERO;
825    
826            if (transactionLine.getTaxLotLines() != null && transactionLine.getTaxLotLines().size() > 0) {
827    
828                for (EndowmentTransactionTaxLotLine taxLotLine : transactionLine.getTaxLotLines()) {
829                    taxLotLinesTotalUnits = taxLotLinesTotalUnits.add(taxLotLine.getLotUnits());
830                }
831            }
832    
833            if (transactionLine instanceof EndowmentSourceTransactionLine) {
834                taxLotLinesTotalUnits = taxLotLinesTotalUnits.negate();
835            }
836    
837            if (transactionLineUnits.compareTo(taxLotLinesTotalUnits) != 0) {
838                isValid = false;
839    
840                putFieldError(getErrorPrefix(transactionLine, index) + EndowPropertyConstants.TRANSACTION_LINE_TRANSACTION_UNITS, EndowKeyConstants.EndowmentTransactionDocumentConstants.ERROR_TRANSACTION_LINE_TAX_LOT_UNITS_DONT_CORRESPOND);
841            }
842    
843            return isValid;
844        }
845    
846    
847        /**
848         * This method Check if value of Endowment is being reduced.
849         * 
850         * @param endowmentTransactionLinesDocumentBase
851         * @param line
852         * @param ERRORPREFIX
853         * @return
854         */
855        /*
856         * protected boolean checkEndowmentValueReduction(EndowmentTransactionLinesDocument endowmentTransactionLinesDocument,
857         * EndowmentTransactionLine line, String ERRORPREFIX) { if(
858         * EndowConstants.IncomePrincipalIndicator.PRINCIPAL.equalsIgnoreCase(line.getTransactionIPIndicatorCode()) ) {
859         * line.getKemidObj().refreshNonUpdateableReferences(); line.getKemidObj().getType().refreshNonUpdateableReferences();
860         * if(line.getKemidObj().getTypeRestrictionCodeForPrincipalRestrictionCode().getPermanentIndicator()) {
861         * GlobalVariables.getMessageMap().putWarningWithoutFullErrorPath(EndowConstants.ENDOWMENT_TRANSACTION_LINE_ERRORS,
862         * EndowKeyConstants.EndowmentTransactionDocumentConstants.WARNING_TRANSACTION_LINE_ENDOWMENT_VALUE_REDUCTION); return false; }
863         * } return true; }
864         */
865    
866        /**
867         * This method validates if the source & target units are equal.
868         * 
869         * @param securityTransferDocument
870         * @return
871         */
872        protected boolean validateSourceTargetUnitsEqual(SecurityTransferDocument securityTransferDocument) {
873            if (!securityTransferDocument.getTargetTotalUnits().equals(securityTransferDocument.getSourceTotalUnits())) {
874                GlobalVariables.getMessageMap().putErrorWithoutFullErrorPath(EndowConstants.TRANSACTION_LINE_ERRORS, EndowKeyConstants.EndowmentTransactionDocumentConstants.ERROR_TRANSACTION_LINE_SOURCE_TARGET_UNITS_EQUAL);
875                return false;
876            }
877    
878            return true;
879        }
880    
881        /**
882         * This method validates if the source & target units are equal.
883         * 
884         * @param securityTransferDocument
885         * @return
886         */
887        protected boolean validateSourceTargetAmountEqual(SecurityTransferDocument securityTransferDocument) {
888            if (!securityTransferDocument.getTargetTotalAmount().equals(securityTransferDocument.getSourceTotalAmount())) {
889                GlobalVariables.getMessageMap().putErrorWithoutFullErrorPath(EndowConstants.TRANSACTION_LINE_ERRORS, EndowKeyConstants.EndowmentTransactionDocumentConstants.ERROR_TRANSACTION_LINE_SOURCE_TARGET_AMOUNT_EQUAL);
890                return false;
891            }
892    
893            return true;
894        }
895    
896    }