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.fp.document;
018    
019    import static org.kuali.kfs.sys.KFSConstants.GL_CREDIT_CODE;
020    import static org.kuali.kfs.sys.KFSConstants.GL_DEBIT_CODE;
021    
022    import java.sql.Date;
023    import java.sql.Timestamp;
024    import java.text.MessageFormat;
025    import java.util.ArrayList;
026    import java.util.Arrays;
027    import java.util.Calendar;
028    import java.util.List;
029    
030    import org.apache.commons.lang.StringUtils;
031    import org.apache.log4j.Logger;
032    import org.kuali.kfs.coa.businessobject.ObjectCode;
033    import org.kuali.kfs.coa.service.ObjectTypeService;
034    import org.kuali.kfs.fp.businessobject.DisbursementVoucherDocumentationLocation;
035    import org.kuali.kfs.fp.businessobject.DisbursementVoucherNonEmployeeTravel;
036    import org.kuali.kfs.fp.businessobject.DisbursementVoucherNonResidentAlienTax;
037    import org.kuali.kfs.fp.businessobject.DisbursementVoucherPayeeDetail;
038    import org.kuali.kfs.fp.businessobject.DisbursementVoucherPreConferenceDetail;
039    import org.kuali.kfs.fp.businessobject.DisbursementVoucherPreConferenceRegistrant;
040    import org.kuali.kfs.fp.businessobject.DisbursementVoucherWireTransfer;
041    import org.kuali.kfs.fp.businessobject.WireCharge;
042    import org.kuali.kfs.fp.businessobject.options.DisbursementVoucherDocumentationLocationValuesFinder;
043    import org.kuali.kfs.fp.businessobject.options.PaymentMethodValuesFinder;
044    import org.kuali.kfs.fp.document.service.DisbursementVoucherPayeeService;
045    import org.kuali.kfs.fp.document.service.DisbursementVoucherPaymentReasonService;
046    import org.kuali.kfs.fp.document.service.DisbursementVoucherTaxService;
047    import org.kuali.kfs.sys.KFSConstants;
048    import org.kuali.kfs.sys.KFSKeyConstants;
049    import org.kuali.kfs.sys.KFSPropertyConstants;
050    import org.kuali.kfs.sys.KFSConstants.AdHocPaymentIndicator;
051    import org.kuali.kfs.sys.businessobject.AccountingLine;
052    import org.kuali.kfs.sys.businessobject.Bank;
053    import org.kuali.kfs.sys.businessobject.ChartOrgHolder;
054    import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntry;
055    import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySequenceHelper;
056    import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySourceDetail;
057    import org.kuali.kfs.sys.context.SpringContext;
058    import org.kuali.kfs.sys.document.AccountingDocumentBase;
059    import org.kuali.kfs.sys.document.AmountTotaling;
060    import org.kuali.kfs.sys.document.service.AccountingDocumentRuleHelperService;
061    import org.kuali.kfs.sys.document.service.DebitDeterminerService;
062    import org.kuali.kfs.sys.document.validation.impl.AccountingDocumentRuleBaseConstants.GENERAL_LEDGER_PENDING_ENTRY_CODE;
063    import org.kuali.kfs.sys.service.BankService;
064    import org.kuali.kfs.sys.service.FlexibleOffsetAccountService;
065    import org.kuali.kfs.sys.service.GeneralLedgerPendingEntryService;
066    import org.kuali.kfs.sys.service.HomeOriginationService;
067    import org.kuali.kfs.sys.service.OptionsService;
068    import org.kuali.kfs.sys.service.UniversityDateService;
069    import org.kuali.kfs.vnd.VendorConstants;
070    import org.kuali.kfs.vnd.businessobject.VendorAddress;
071    import org.kuali.kfs.vnd.businessobject.VendorDetail;
072    import org.kuali.kfs.vnd.document.service.VendorService;
073    import org.kuali.rice.kew.exception.WorkflowException;
074    import org.kuali.rice.kim.bo.Person;
075    import org.kuali.rice.kim.bo.entity.KimEntityAddress;
076    import org.kuali.rice.kim.bo.entity.KimEntityEntityType;
077    import org.kuali.rice.kim.bo.entity.dto.KimEntityEntityTypeInfo;
078    import org.kuali.rice.kim.bo.entity.dto.KimEntityInfo;
079    import org.kuali.rice.kim.service.IdentityManagementService;
080    import org.kuali.rice.kim.service.PersonService;
081    import org.kuali.rice.kim.util.KimConstants;
082    import org.kuali.rice.kns.bo.DocumentHeader;
083    import org.kuali.rice.kns.document.Copyable;
084    import org.kuali.rice.kns.service.BusinessObjectService;
085    import org.kuali.rice.kns.service.DateTimeService;
086    import org.kuali.rice.kns.service.KualiConfigurationService;
087    import org.kuali.rice.kns.service.ParameterEvaluator;
088    import org.kuali.rice.kns.service.ParameterService;
089    import org.kuali.rice.kns.util.GlobalVariables;
090    import org.kuali.rice.kns.util.KNSConstants;
091    import org.kuali.rice.kns.util.KualiDecimal;
092    import org.kuali.rice.kns.util.ObjectUtils;
093    
094    /**
095     * This is the business object that represents the DisbursementVoucher document in Kuali.
096     */
097    public class DisbursementVoucherDocument extends AccountingDocumentBase implements Copyable, AmountTotaling {
098        protected static Logger LOG = Logger.getLogger(DisbursementVoucherDocument.class);
099        
100        protected static final String PAYEE_IS_PURCHASE_ORDER_VENDOR_SPLIT = "PayeeIsPurchaseOrderVendor";
101        protected static final String PURCHASE_ORDER_VENDOR_TYPE = "PO";
102        protected static final String DOCUMENT_REQUIRES_TAX_REVIEW_SPLIT = "RequiresTaxReview";
103        protected static final String DOCUMENT_REQUIRES_TRAVEL_REVIEW_SPLIT = "RequiresTravelReview";
104        
105        protected static final String PAYMENT_REASONS_REQUIRING_TAX_REVIEW_PARAMETER_NAME = "PAYMENT_REASONS_REQUIRING_TAX_REVIEW";
106        protected static final String USE_DEFAULT_EMPLOYEE_ADDRESS_PARAMETER_NAME = "USE_DEFAULT_EMPLOYEE_ADDRESS_IND";
107        protected static final String DEFAULT_EMPLOYEE_ADDRESS_TYPE_PARAMETER_NAME = "DEFAULT_EMPLOYEE_ADDRESS_TYPE";
108    
109        protected static final String TAX_CONTROL_BACKUP_HOLDING = "B";
110        protected static final String TAX_CONTROL_HOLD_PAYMENTS = "H";
111        
112        protected static transient PersonService<Person> personService;
113        protected static transient ParameterService parameterService;
114        protected static transient VendorService vendorService;
115        protected static transient BusinessObjectService businessObjectService;
116        protected static transient DateTimeService dateTimeService;
117        protected static transient DisbursementVoucherPaymentReasonService dvPymentReasonService;
118        protected static transient IdentityManagementService identityManagementService;
119    
120        protected Integer finDocNextRegistrantLineNbr;
121        protected String disbVchrContactPersonName;
122        protected String disbVchrContactPhoneNumber;
123        protected String disbVchrContactEmailId;
124        protected Date disbursementVoucherDueDate;
125        protected boolean disbVchrAttachmentCode;
126        protected boolean disbVchrSpecialHandlingCode;
127        protected KualiDecimal disbVchrCheckTotalAmount;
128        protected boolean disbVchrForeignCurrencyInd;
129        protected String disbursementVoucherDocumentationLocationCode;
130        protected String disbVchrCheckStubText;
131        protected boolean dvCheckStubOverflowCode;
132        protected String campusCode;
133        protected String disbVchrPayeeTaxControlCode;
134        protected boolean disbVchrPayeeChangedInd;
135        protected String disbursementVoucherCheckNbr;
136        protected Timestamp disbursementVoucherCheckDate;
137        protected boolean disbVchrPayeeW9CompleteCode;
138        protected String disbVchrPaymentMethodCode;
139        protected boolean exceptionIndicator;
140        protected boolean disbExcptAttachedIndicator;
141        protected Date extractDate;
142        protected Date paidDate;
143        protected Date cancelDate;
144        protected String disbVchrBankCode;
145        protected String disbVchrPdpBankCode;
146    
147        protected boolean payeeAssigned = false;
148        protected boolean editW9W8BENbox = false;
149        
150        
151        protected DocumentHeader financialDocument;
152        protected DisbursementVoucherDocumentationLocation disbVchrDocumentationLoc;
153        protected DisbursementVoucherNonEmployeeTravel dvNonEmployeeTravel;
154        protected DisbursementVoucherNonResidentAlienTax dvNonResidentAlienTax;
155        protected DisbursementVoucherPayeeDetail dvPayeeDetail;
156        protected DisbursementVoucherPreConferenceDetail dvPreConferenceDetail;
157        protected DisbursementVoucherWireTransfer dvWireTransfer;
158    
159        protected Bank bank;
160    
161        /**
162         * Default no-arg constructor.
163         */
164        public DisbursementVoucherDocument() {
165            super();
166            exceptionIndicator = false;
167            finDocNextRegistrantLineNbr = new Integer(1);
168            dvNonEmployeeTravel = new DisbursementVoucherNonEmployeeTravel();
169            dvNonResidentAlienTax = new DisbursementVoucherNonResidentAlienTax();
170            dvPayeeDetail = new DisbursementVoucherPayeeDetail();
171            dvPreConferenceDetail = new DisbursementVoucherPreConferenceDetail();
172            dvWireTransfer = new DisbursementVoucherWireTransfer();
173            disbVchrCheckTotalAmount = KualiDecimal.ZERO;
174            bank = new Bank();
175        }
176    
177    
178        /**
179         * @see org.kuali.kfs.sys.document.AccountingDocumentBase#getPendingLedgerEntriesForSufficientFundsChecking()
180         */
181        @Override
182        public List<GeneralLedgerPendingEntry> getPendingLedgerEntriesForSufficientFundsChecking() {
183            List<GeneralLedgerPendingEntry> ples = new ArrayList<GeneralLedgerPendingEntry>();
184    
185            KualiConfigurationService kualiConfigurationService = SpringContext.getBean(KualiConfigurationService.class);
186            FlexibleOffsetAccountService flexibleOffsetAccountService = SpringContext.getBean(FlexibleOffsetAccountService.class);
187    
188            ObjectTypeService objectTypeService = (ObjectTypeService) SpringContext.getBean(ObjectTypeService.class);
189    
190            for (GeneralLedgerPendingEntry ple : this.getGeneralLedgerPendingEntries()) {
191                List<String> expenseObjectTypes = objectTypeService.getExpenseObjectTypes(ple.getUniversityFiscalYear());
192                if (expenseObjectTypes.contains(ple.getFinancialObjectTypeCode())) {
193                    // is an expense object type, keep checking
194                    ple.refreshNonUpdateableReferences();
195                    if (ple.getAccount().isPendingAcctSufficientFundsIndicator() && ple.getAccount().getAccountSufficientFundsCode().equals(KFSConstants.SF_TYPE_CASH_AT_ACCOUNT)) {
196                        // is a cash account
197                        if (flexibleOffsetAccountService.getByPrimaryIdIfEnabled(ple.getChartOfAccountsCode(), ple.getAccountNumber(), ple.getChart().getFinancialCashObjectCode()) == null && flexibleOffsetAccountService.getByPrimaryIdIfEnabled(ple.getChartOfAccountsCode(), ple.getAccountNumber(), ple.getChart().getFinAccountsPayableObjectCode()) == null) {
198                            // does not have a flexible offset for cash or liability, set the object code to cash and add to list of
199                            // PLEs to check for SF
200    
201                            ple = new GeneralLedgerPendingEntry(ple);
202                            ple.setFinancialObjectCode(ple.getChart().getFinancialCashObjectCode());
203                            ple.setTransactionDebitCreditCode(ple.getTransactionDebitCreditCode().equals(KFSConstants.GL_DEBIT_CODE) ? KFSConstants.GL_CREDIT_CODE : KFSConstants.GL_DEBIT_CODE);
204                            ples.add(ple);
205                        }
206    
207                    }
208                    else {
209                        // is not a cash account, process as normal
210                        ples.add(ple);
211                    }
212                }
213            }
214    
215            return ples;
216        }
217    
218    
219        /**
220         * Gets the finDocNextRegistrantLineNbr attribute.
221         * 
222         * @return Returns the finDocNextRegistrantLineNbr
223         */
224        public Integer getFinDocNextRegistrantLineNbr() {
225            return finDocNextRegistrantLineNbr;
226        }
227    
228    
229        /**
230         * Sets the finDocNextRegistrantLineNbr attribute.
231         * 
232         * @param finDocNextRegistrantLineNbr The finDocNextRegistrantLineNbr to set.
233         */
234        public void setFinDocNextRegistrantLineNbr(Integer finDocNextRegistrantLineNbr) {
235            this.finDocNextRegistrantLineNbr = finDocNextRegistrantLineNbr;
236        }
237    
238        /**
239         * Gets the disbVchrContactPersonName attribute.
240         * 
241         * @return Returns the disbVchrContactPersonName
242         */
243        public String getDisbVchrContactPersonName() {
244            return disbVchrContactPersonName;
245        }
246    
247    
248        /**
249         * Sets the disbVchrContactPersonName attribute.
250         * 
251         * @param disbVchrContactPersonName The disbVchrContactPersonName to set.
252         */
253        public void setDisbVchrContactPersonName(String disbVchrContactPersonName) {
254            this.disbVchrContactPersonName = disbVchrContactPersonName;
255        }
256    
257        /**
258         * Gets the disbVchrContactPhoneNumber attribute.
259         * 
260         * @return Returns the disbVchrContactPhoneNumber
261         */
262        public String getDisbVchrContactPhoneNumber() {
263            return disbVchrContactPhoneNumber;
264        }
265    
266    
267        /**
268         * Sets the disbVchrContactPhoneNumber attribute.
269         * 
270         * @param disbVchrContactPhoneNumber The disbVchrContactPhoneNumber to set.
271         */
272        public void setDisbVchrContactPhoneNumber(String disbVchrContactPhoneNumber) {
273            this.disbVchrContactPhoneNumber = disbVchrContactPhoneNumber;
274        }
275    
276        /**
277         * Gets the disbVchrContactEmailId attribute.
278         * 
279         * @return Returns the disbVchrContactEmailId
280         */
281        public String getDisbVchrContactEmailId() {
282            return disbVchrContactEmailId;
283        }
284    
285    
286        /**
287         * Sets the disbVchrContactEmailId attribute.
288         * 
289         * @param disbVchrContactEmailId The disbVchrContactEmailId to set.
290         */
291        public void setDisbVchrContactEmailId(String disbVchrContactEmailId) {
292            this.disbVchrContactEmailId = disbVchrContactEmailId;
293        }
294    
295        /**
296         * Gets the disbursementVoucherDueDate attribute.
297         * 
298         * @return Returns the disbursementVoucherDueDate
299         */
300        public Date getDisbursementVoucherDueDate() {
301            return disbursementVoucherDueDate;
302        }
303    
304    
305        /**
306         * Sets the disbursementVoucherDueDate attribute.
307         * 
308         * @param disbursementVoucherDueDate The disbursementVoucherDueDate to set.
309         */
310        public void setDisbursementVoucherDueDate(Date disbursementVoucherDueDate) {
311            this.disbursementVoucherDueDate = disbursementVoucherDueDate;
312        }
313    
314        /**
315         * Gets the disbVchrAttachmentCode attribute.
316         * 
317         * @return Returns the disbVchrAttachmentCode
318         */
319        public boolean isDisbVchrAttachmentCode() {
320            return disbVchrAttachmentCode;
321        }
322    
323    
324        /**
325         * Sets the disbVchrAttachmentCode attribute.
326         * 
327         * @param disbVchrAttachmentCode The disbVchrAttachmentCode to set.
328         */
329        public void setDisbVchrAttachmentCode(boolean disbVchrAttachmentCode) {
330            this.disbVchrAttachmentCode = disbVchrAttachmentCode;
331        }
332    
333        /**
334         * Gets the disbVchrSpecialHandlingCode attribute.
335         * 
336         * @return Returns the disbVchrSpecialHandlingCode
337         */
338        public boolean isDisbVchrSpecialHandlingCode() {
339            return disbVchrSpecialHandlingCode;
340        }
341    
342    
343        /**
344         * Sets the disbVchrSpecialHandlingCode attribute.
345         * 
346         * @param disbVchrSpecialHandlingCode The disbVchrSpecialHandlingCode to set.
347         */
348        public void setDisbVchrSpecialHandlingCode(boolean disbVchrSpecialHandlingCode) {
349            this.disbVchrSpecialHandlingCode = disbVchrSpecialHandlingCode;
350        }
351    
352        /**
353         * Gets the disbVchrCheckTotalAmount attribute.
354         * 
355         * @return Returns the disbVchrCheckTotalAmount
356         */
357        public KualiDecimal getDisbVchrCheckTotalAmount() {
358            return disbVchrCheckTotalAmount;
359        }
360    
361    
362        /**
363         * Sets the disbVchrCheckTotalAmount attribute.
364         * 
365         * @param disbVchrCheckTotalAmount The disbVchrCheckTotalAmount to set.
366         */
367        public void setDisbVchrCheckTotalAmount(KualiDecimal disbVchrCheckTotalAmount) {
368            if (disbVchrCheckTotalAmount != null) {
369                this.disbVchrCheckTotalAmount = disbVchrCheckTotalAmount;
370            }
371        }
372    
373        /**
374         * Gets the disbVchrForeignCurrencyInd attribute.
375         * 
376         * @return Returns the disbVchrForeignCurrencyInd
377         */
378        public boolean isDisbVchrForeignCurrencyInd() {
379            return disbVchrForeignCurrencyInd;
380        }
381    
382    
383        /**
384         * Sets the disbVchrForeignCurrencyInd attribute.
385         * 
386         * @param disbVchrForeignCurrencyInd The disbVchrForeignCurrencyInd to set.
387         */
388        public void setDisbVchrForeignCurrencyInd(boolean disbVchrForeignCurrencyInd) {
389            this.disbVchrForeignCurrencyInd = disbVchrForeignCurrencyInd;
390        }
391    
392        /**
393         * Gets the disbursementVoucherDocumentationLocationCode attribute.
394         * 
395         * @return Returns the disbursementVoucherDocumentationLocationCode
396         */
397        public String getDisbursementVoucherDocumentationLocationCode() {
398            return disbursementVoucherDocumentationLocationCode;
399        }
400    
401    
402        /**
403         * Sets the disbursementVoucherDocumentationLocationCode attribute.
404         * 
405         * @param disbursementVoucherDocumentationLocationCode The disbursementVoucherDocumentationLocationCode to set.
406         */
407        public void setDisbursementVoucherDocumentationLocationCode(String disbursementVoucherDocumentationLocationCode) {
408            this.disbursementVoucherDocumentationLocationCode = disbursementVoucherDocumentationLocationCode;
409        }
410    
411        /**
412         * Gets the disbVchrCheckStubText attribute.
413         * 
414         * @return Returns the disbVchrCheckStubText
415         */
416        public String getDisbVchrCheckStubText() {
417            return disbVchrCheckStubText;
418        }
419    
420    
421        /**
422         * Sets the disbVchrCheckStubText attribute.
423         * 
424         * @param disbVchrCheckStubText The disbVchrCheckStubText to set.
425         */
426        public void setDisbVchrCheckStubText(String disbVchrCheckStubText) {
427            this.disbVchrCheckStubText = disbVchrCheckStubText;
428        }
429    
430        /**
431         * Gets the dvCheckStubOverflowCode attribute.
432         * 
433         * @return Returns the dvCheckStubOverflowCode
434         */
435        public boolean getDvCheckStubOverflowCode() {
436            return dvCheckStubOverflowCode;
437        }
438    
439    
440        /**
441         * Sets the dvCheckStubOverflowCode attribute.
442         * 
443         * @param dvCheckStubOverflowCode The dvCheckStubOverflowCode to set.
444         */
445        public void setDvCheckStubOverflowCode(boolean dvCheckStubOverflowCode) {
446            this.dvCheckStubOverflowCode = dvCheckStubOverflowCode;
447        }
448    
449        /**
450         * Gets the campusCode attribute.
451         * 
452         * @return Returns the campusCode
453         */
454        public String getCampusCode() {
455            return campusCode;
456        }
457    
458    
459        /**
460         * Sets the campusCode attribute.
461         * 
462         * @param campusCode The campusCode to set.
463         */
464        public void setCampusCode(String campusCode) {
465            this.campusCode = campusCode;
466        }
467    
468        /**
469         * Gets the disbVchrPayeeTaxControlCode attribute.
470         * 
471         * @return Returns the disbVchrPayeeTaxControlCode
472         */
473        public String getDisbVchrPayeeTaxControlCode() {
474            return disbVchrPayeeTaxControlCode;
475        }
476    
477    
478        /**
479         * Sets the disbVchrPayeeTaxControlCode attribute.
480         * 
481         * @param disbVchrPayeeTaxControlCode The disbVchrPayeeTaxControlCode to set.
482         */
483        public void setDisbVchrPayeeTaxControlCode(String disbVchrPayeeTaxControlCode) {
484            this.disbVchrPayeeTaxControlCode = disbVchrPayeeTaxControlCode;
485        }
486    
487        /**
488         * Gets the disbVchrPayeeChangedInd attribute.
489         * 
490         * @return Returns the disbVchrPayeeChangedInd
491         */
492        public boolean isDisbVchrPayeeChangedInd() {
493            return disbVchrPayeeChangedInd;
494        }
495    
496    
497        /**
498         * Sets the disbVchrPayeeChangedInd attribute.
499         * 
500         * @param disbVchrPayeeChangedInd The disbVchrPayeeChangedInd to set.
501         */
502        public void setDisbVchrPayeeChangedInd(boolean disbVchrPayeeChangedInd) {
503            this.disbVchrPayeeChangedInd = disbVchrPayeeChangedInd;
504        }
505    
506        /**
507         * Gets the disbursementVoucherCheckNbr attribute.
508         * 
509         * @return Returns the disbursementVoucherCheckNbr
510         */
511        public String getDisbursementVoucherCheckNbr() {
512            return disbursementVoucherCheckNbr;
513        }
514    
515    
516        /**
517         * Sets the disbursementVoucherCheckNbr attribute.
518         * 
519         * @param disbursementVoucherCheckNbr The disbursementVoucherCheckNbr to set.
520         */
521        public void setDisbursementVoucherCheckNbr(String disbursementVoucherCheckNbr) {
522            this.disbursementVoucherCheckNbr = disbursementVoucherCheckNbr;
523        }
524    
525        /**
526         * Gets the disbursementVoucherCheckDate attribute.
527         * 
528         * @return Returns the disbursementVoucherCheckDate
529         */
530        public Timestamp getDisbursementVoucherCheckDate() {
531            return disbursementVoucherCheckDate;
532        }
533    
534    
535        /**
536         * Sets the disbursementVoucherCheckDate attribute.
537         * 
538         * @param disbursementVoucherCheckDate The disbursementVoucherCheckDate to set.
539         */
540        public void setDisbursementVoucherCheckDate(Timestamp disbursementVoucherCheckDate) {
541            this.disbursementVoucherCheckDate = disbursementVoucherCheckDate;
542        }
543    
544        /**
545         * Gets the disbVchrPayeeW9CompleteCode attribute.
546         * 
547         * @return Returns the disbVchrPayeeW9CompleteCode
548         */
549        public boolean getDisbVchrPayeeW9CompleteCode() {
550            return disbVchrPayeeW9CompleteCode;
551        }
552    
553    
554        /**
555         * Sets the disbVchrPayeeW9CompleteCode attribute.
556         * 
557         * @param disbVchrPayeeW9CompleteCode The disbVchrPayeeW9CompleteCode to set.
558         */
559        public void setDisbVchrPayeeW9CompleteCode(boolean disbVchrPayeeW9CompleteCode) {
560            this.disbVchrPayeeW9CompleteCode = disbVchrPayeeW9CompleteCode;
561        }
562    
563        /**
564         * Gets the disbVchrPaymentMethodCode attribute.
565         * 
566         * @return Returns the disbVchrPaymentMethodCode
567         */
568        public String getDisbVchrPaymentMethodCode() {
569            return disbVchrPaymentMethodCode;
570        }
571    
572    
573        /**
574         * Sets the disbVchrPaymentMethodCode attribute.
575         * 
576         * @param disbVchrPaymentMethodCode The disbVchrPaymentMethodCode to set.
577         */
578        public void setDisbVchrPaymentMethodCode(String disbVchrPaymentMethodCode) {
579            this.disbVchrPaymentMethodCode = disbVchrPaymentMethodCode;
580        }
581    
582        /**
583         * Gets the financialDocument attribute.
584         * 
585         * @return Returns the financialDocument
586         */
587        public DocumentHeader getFinancialDocument() {
588            return financialDocument;
589        }
590    
591    
592        /**
593         * Sets the financialDocument attribute.
594         * 
595         * @param financialDocument The financialDocument to set.
596         * @deprecated
597         */
598        public void setFinancialDocument(DocumentHeader financialDocument) {
599            this.financialDocument = financialDocument;
600        }
601    
602        /**
603         * Gets the disbVchrDocumentationLoc attribute.
604         * 
605         * @return Returns the disbVchrDocumentationLoc
606         */
607        public DisbursementVoucherDocumentationLocation getDisbVchrDocumentationLoc() {
608            return disbVchrDocumentationLoc;
609        }
610    
611    
612        /**
613         * Sets the disbVchrDocumentationLoc attribute.
614         * 
615         * @param disbVchrDocumentationLoc The disbVchrDocumentationLoc to set.
616         * @deprecated
617         */
618        public void setDisbVchrDocumentationLoc(DisbursementVoucherDocumentationLocation disbVchrDocumentationLoc) {
619            this.disbVchrDocumentationLoc = disbVchrDocumentationLoc;
620        }
621    
622    
623        /**
624         * @return Returns the dvNonEmployeeTravel.
625         */
626        public DisbursementVoucherNonEmployeeTravel getDvNonEmployeeTravel() {
627            return dvNonEmployeeTravel;
628        }
629    
630        /**
631         * @param dvNonEmployeeTravel The dvNonEmployeeTravel to set.
632         */
633        public void setDvNonEmployeeTravel(DisbursementVoucherNonEmployeeTravel dvNonEmployeeTravel) {
634            this.dvNonEmployeeTravel = dvNonEmployeeTravel;
635        }
636    
637        /**
638         * @return Returns the dvNonResidentAlienTax.
639         */
640        public DisbursementVoucherNonResidentAlienTax getDvNonResidentAlienTax() {
641            return dvNonResidentAlienTax;
642        }
643    
644        /**
645         * @param dvNonResidentAlienTax The dvNonResidentAlienTax to set.
646         */
647        public void setDvNonResidentAlienTax(DisbursementVoucherNonResidentAlienTax dvNonResidentAlienTax) {
648            this.dvNonResidentAlienTax = dvNonResidentAlienTax;
649        }
650    
651        /**
652         * @return Returns the dvPayeeDetail.
653         */
654        public DisbursementVoucherPayeeDetail getDvPayeeDetail() {
655            return dvPayeeDetail;
656        }
657    
658        /**
659         * @param dvPayeeDetail The dvPayeeDetail to set.
660         */
661        public void setDvPayeeDetail(DisbursementVoucherPayeeDetail dvPayeeDetail) {
662            this.dvPayeeDetail = dvPayeeDetail;
663        }
664    
665        /**
666         * @return Returns the dvPreConferenceDetail.
667         */
668        public DisbursementVoucherPreConferenceDetail getDvPreConferenceDetail() {
669            return dvPreConferenceDetail;
670        }
671    
672        /**
673         * @param dvPreConferenceDetail The dvPreConferenceDetail to set.
674         */
675        public void setDvPreConferenceDetail(DisbursementVoucherPreConferenceDetail dvPreConferenceDetail) {
676            this.dvPreConferenceDetail = dvPreConferenceDetail;
677        }
678    
679        /**
680         * @return Returns the dvWireTransfer.
681         */
682        public DisbursementVoucherWireTransfer getDvWireTransfer() {
683            return dvWireTransfer;
684        }
685    
686        /**
687         * @param dvWireTransfer The dvWireTransfer to set.
688         */
689        public void setDvWireTransfer(DisbursementVoucherWireTransfer dvWireTransfer) {
690            this.dvWireTransfer = dvWireTransfer;
691        }
692    
693        /**
694         * @return Returns the exceptionIndicator.
695         */
696        public boolean isExceptionIndicator() {
697            return exceptionIndicator;
698        }
699    
700        /**
701         * @param exceptionIndicator The exceptionIndicator to set.
702         */
703        public void setExceptionIndicator(boolean exceptionIndicator) {
704            this.exceptionIndicator = exceptionIndicator;
705        }
706    
707        /**
708         * Gets the cancelDate attribute.
709         * 
710         * @return Returns the cancelDate.
711         */
712        public Date getCancelDate() {
713            return cancelDate;
714        }
715    
716        /**
717         * Sets the cancelDate attribute value.
718         * 
719         * @param cancelDate The cancelDate to set.
720         */
721        public void setCancelDate(Date cancelDate) {
722            this.cancelDate = cancelDate;
723        }
724    
725        /**
726         * Gets the extractDate attribute.
727         * 
728         * @return Returns the extractDate.
729         */
730        public Date getExtractDate() {
731            return extractDate;
732        }
733    
734        /**
735         * Sets the extractDate attribute value.
736         * 
737         * @param extractDate The extractDate to set.
738         */
739        public void setExtractDate(Date extractDate) {
740            this.extractDate = extractDate;
741        }
742    
743        /**
744         * Gets the paidDate attribute.
745         * 
746         * @return Returns the paidDate.
747         */
748        public Date getPaidDate() {
749            return paidDate;
750        }
751    
752        /**
753         * Sets the paidDate attribute value.
754         * 
755         * @param paidDate The paidDate to set.
756         */
757        public void setPaidDate(Date paidDate) {
758            this.paidDate = paidDate;
759        }
760    
761        /**
762         * Based on which pdp dates are present (extract, paid, canceled), determines a String for the status
763         * 
764         * @return a String representation of the status
765         */
766        public String getDisbursementVoucherPdpStatus() {
767            if (cancelDate != null) {
768                return "Canceled";
769            }
770            else if (paidDate != null) {
771                return "Paid";
772            }
773            else if (extractDate != null) {
774                return "Extracted";
775            }
776            else {
777                return "Pre-Extraction";
778            }
779        }
780    
781        /**
782         * Pretends to set the PDP status for this document
783         * 
784         * @param status the status to pretend to set
785         */
786        public void setDisbursementVoucherPdpStatus(String status) {
787            // don't do nothing, 'cause this ain't a real field
788        }
789    
790        /**
791         * Adds a dv pre-paid registrant line
792         * 
793         * @param line
794         */
795        public void addDvPrePaidRegistrantLine(DisbursementVoucherPreConferenceRegistrant line) {
796            line.setFinancialDocumentLineNumber(getFinDocNextRegistrantLineNbr());
797            this.getDvPreConferenceDetail().getDvPreConferenceRegistrants().add(line);
798            this.finDocNextRegistrantLineNbr = new Integer(getFinDocNextRegistrantLineNbr().intValue() + 1);
799        }
800    
801        /**
802         * Returns the name associated with the payment method code
803         * 
804         * @return String
805         */
806        public String getDisbVchrPaymentMethodName() {
807            return new PaymentMethodValuesFinder().getKeyLabel(disbVchrPaymentMethodCode);
808        }
809    
810        /**
811         * This method...
812         * 
813         * @param method
814         * @deprecated This method should not be used. There is no protected attribute to store this value. The associated getter
815         *             retrieves the value remotely.
816         */
817        public void setDisbVchrPaymentMethodName(String method) {
818        }
819    
820        /**
821         * Returns the name associated with the documentation location name
822         * 
823         * @return String
824         */
825        public String getDisbursementVoucherDocumentationLocationName() {
826            return new DisbursementVoucherDocumentationLocationValuesFinder().getKeyLabel(disbursementVoucherDocumentationLocationCode);
827        }
828    
829        /**
830         * This method...
831         * 
832         * @param name
833         * @deprecated This method should not be used. There is no protected attribute to store this value. The associated getter
834         *             retrieves the value remotely.
835         */
836        public void setDisbursementVoucherDocumentationLocationName(String name) {
837        }
838    
839    
840        /**
841         * Gets the disbVchrBankCode attribute.
842         * 
843         * @return Returns the disbVchrBankCode.
844         */
845        public String getDisbVchrBankCode() {
846            return disbVchrBankCode;
847        }
848    
849    
850        /**
851         * Sets the disbVchrBankCode attribute value.
852         * 
853         * @param disbVchrBankCode The disbVchrBankCode to set.
854         */
855        public void setDisbVchrBankCode(String disbVchrBankCode) {
856            this.disbVchrBankCode = disbVchrBankCode;
857        }
858    
859        /**
860         * Gets the bank attribute.
861         * 
862         * @return Returns the bank.
863         */
864        public Bank getBank() {
865            return bank;
866        }
867    
868    
869        /**
870         * Sets the bank attribute value.
871         * 
872         * @param bank The bank to set.
873         */
874        public void setBank(Bank bank) {
875            this.bank = bank;
876        }
877    
878    
879        /**
880         * Convenience method to set dv payee detail fields based on a given vendor.
881         * 
882         * @param vendor
883         */
884        public void templateVendor(VendorDetail vendor, VendorAddress vendorAddress) {
885            if (vendor == null) {
886                return;
887            }
888    
889            this.getDvPayeeDetail().setDisbursementVoucherPayeeTypeCode(DisbursementVoucherConstants.DV_PAYEE_TYPE_VENDOR);
890            this.getDvPayeeDetail().setDisbVchrPayeeIdNumber(vendor.getVendorNumber());
891            this.getDvPayeeDetail().setDisbVchrPayeePersonName(vendor.getVendorName());
892    
893            this.getDvPayeeDetail().setDisbVchrAlienPaymentCode(vendor.getVendorHeader().getVendorForeignIndicator());
894    
895            if (ObjectUtils.isNull(vendorAddress) || ObjectUtils.isNull(vendorAddress.getVendorAddressGeneratedIdentifier())) {
896                for (VendorAddress addr : vendor.getVendorAddresses()) {
897                    if (addr.isVendorDefaultAddressIndicator()) {
898                        vendorAddress = addr;
899                        break;
900                    }
901                }
902            }
903    
904            if (ObjectUtils.isNotNull(vendorAddress) && ObjectUtils.isNotNull(vendorAddress.getVendorAddressGeneratedIdentifier())) {
905                this.getDvPayeeDetail().setDisbVchrVendorAddressIdNumber(vendorAddress.getVendorAddressGeneratedIdentifier().toString());
906                this.getDvPayeeDetail().setDisbVchrPayeeLine1Addr(vendorAddress.getVendorLine1Address());
907                this.getDvPayeeDetail().setDisbVchrPayeeLine2Addr(vendorAddress.getVendorLine2Address());
908                this.getDvPayeeDetail().setDisbVchrPayeeCityName(vendorAddress.getVendorCityName());
909                this.getDvPayeeDetail().setDisbVchrPayeeStateCode(vendorAddress.getVendorStateCode());
910                this.getDvPayeeDetail().setDisbVchrPayeeZipCode(vendorAddress.getVendorZipCode());
911                this.getDvPayeeDetail().setDisbVchrPayeeCountryCode(vendorAddress.getVendorCountryCode());
912            }
913            else {
914                this.getDvPayeeDetail().setDisbVchrVendorAddressIdNumber("");
915                this.getDvPayeeDetail().setDisbVchrPayeeLine1Addr("");
916                this.getDvPayeeDetail().setDisbVchrPayeeLine2Addr("");
917                this.getDvPayeeDetail().setDisbVchrPayeeCityName("");
918                this.getDvPayeeDetail().setDisbVchrPayeeStateCode("");
919                this.getDvPayeeDetail().setDisbVchrPayeeZipCode("");
920                this.getDvPayeeDetail().setDisbVchrPayeeCountryCode("");      
921            }
922    
923            this.getDvPayeeDetail().setDisbVchrAlienPaymentCode(vendor.getVendorHeader().getVendorForeignIndicator());
924            this.getDvPayeeDetail().setDvPayeeSubjectPaymentCode(VendorConstants.VendorTypes.SUBJECT_PAYMENT.equals(vendor.getVendorHeader().getVendorTypeCode()));
925            this.getDvPayeeDetail().setDisbVchrEmployeePaidOutsidePayrollCode(getVendorService().isVendorInstitutionEmployee(vendor.getVendorHeaderGeneratedIdentifier()));
926    
927            this.getDvPayeeDetail().setHasMultipleVendorAddresses(1 < vendor.getVendorAddresses().size());
928    
929            boolean w9AndW8Checked = false;
930            if ( (ObjectUtils.isNotNull(vendor.getVendorHeader().getVendorW9ReceivedIndicator()) && vendor.getVendorHeader().getVendorW9ReceivedIndicator() == true) || 
931                 (ObjectUtils.isNotNull(vendor.getVendorHeader().getVendorW8BenReceivedIndicator()) && vendor.getVendorHeader().getVendorW8BenReceivedIndicator() == true) ) {           
932                    
933                w9AndW8Checked = true;            
934            }
935            
936        //    this.disbVchrPayeeW9CompleteCode = vendor.getVendorHeader().getVendorW8BenReceivedIndicator()  == null ? false : vendor.getVendorHeader().getVendorW8BenReceivedIndicator();
937            this.disbVchrPayeeW9CompleteCode = w9AndW8Checked;
938            
939            Date vendorFederalWithholdingTaxBeginDate = vendor.getVendorHeader().getVendorFederalWithholdingTaxBeginningDate();
940            Date vendorFederalWithholdingTaxEndDate = vendor.getVendorHeader().getVendorFederalWithholdingTaxEndDate();
941            java.util.Date today = getDateTimeService().getCurrentDate();
942            if ((vendorFederalWithholdingTaxBeginDate != null && vendorFederalWithholdingTaxBeginDate.before(today)) && (vendorFederalWithholdingTaxEndDate == null || vendorFederalWithholdingTaxEndDate.after(today))) {
943                this.disbVchrPayeeTaxControlCode = DisbursementVoucherConstants.TAX_CONTROL_CODE_BEGIN_WITHHOLDING;
944            }
945            
946            // if vendor is foreign, default alien payment code to true
947            if (getVendorService().isVendorForeign(vendor.getVendorHeaderGeneratedIdentifier())) {
948                getDvPayeeDetail().setDisbVchrAlienPaymentCode(true);
949            }
950        }
951    
952        /**
953         * Convenience method to set dv payee detail fields based on a given Employee.
954         * 
955         * @param employee
956         */
957        public void templateEmployee(Person employee) {
958            if (employee == null) {
959                return;
960            }
961    
962            this.getDvPayeeDetail().setDisbursementVoucherPayeeTypeCode(DisbursementVoucherConstants.DV_PAYEE_TYPE_EMPLOYEE);
963            this.getDvPayeeDetail().setDisbVchrPayeeIdNumber(employee.getEmployeeId());
964            this.getDvPayeeDetail().setDisbVchrPayeePersonName(employee.getName());
965    
966            final ParameterService parameterService = this.getParameterService();
967            
968            if (parameterService.parameterExists(DisbursementVoucherDocument.class, DisbursementVoucherDocument.USE_DEFAULT_EMPLOYEE_ADDRESS_PARAMETER_NAME) && parameterService.getIndicatorParameter(DisbursementVoucherDocument.class, DisbursementVoucherDocument.USE_DEFAULT_EMPLOYEE_ADDRESS_PARAMETER_NAME)) {
969                this.getDvPayeeDetail().setDisbVchrPayeeLine1Addr(employee.getAddressLine1Unmasked());
970                this.getDvPayeeDetail().setDisbVchrPayeeLine2Addr(employee.getAddressLine2Unmasked());
971                this.getDvPayeeDetail().setDisbVchrPayeeCityName(employee.getAddressCityNameUnmasked());
972                this.getDvPayeeDetail().setDisbVchrPayeeStateCode(employee.getAddressStateCodeUnmasked());
973                this.getDvPayeeDetail().setDisbVchrPayeeZipCode(employee.getAddressPostalCodeUnmasked());
974                this.getDvPayeeDetail().setDisbVchrPayeeCountryCode(employee.getAddressCountryCodeUnmasked());
975            } else {
976                final KimEntityAddress address = getNonDefaultAddress(employee);
977                if (address != null) {
978                    this.getDvPayeeDetail().setDisbVchrPayeeLine1Addr(address.getLine1Unmasked());
979                    this.getDvPayeeDetail().setDisbVchrPayeeLine2Addr(address.getLine2Unmasked());
980                    this.getDvPayeeDetail().setDisbVchrPayeeCityName(address.getCityNameUnmasked());
981                    this.getDvPayeeDetail().setDisbVchrPayeeStateCode(address.getStateCodeUnmasked());
982                    this.getDvPayeeDetail().setDisbVchrPayeeZipCode(address.getPostalCodeUnmasked());
983                    this.getDvPayeeDetail().setDisbVchrPayeeCountryCode(address.getCountryCodeUnmasked());
984                }
985                else {
986                    this.getDvPayeeDetail().setDisbVchrPayeeLine1Addr("");
987                    this.getDvPayeeDetail().setDisbVchrPayeeLine2Addr("");
988                    this.getDvPayeeDetail().setDisbVchrPayeeCityName("");
989                    this.getDvPayeeDetail().setDisbVchrPayeeStateCode("");
990                    this.getDvPayeeDetail().setDisbVchrPayeeZipCode("");
991                    this.getDvPayeeDetail().setDisbVchrPayeeCountryCode("");            
992                }
993            }
994    
995            this.getDvPayeeDetail().setDisbVchrPayeeEmployeeCode(true);
996            // I'm assuming that if a tax id type code other than 'TAX' is present, then the employee must be foreign
997            for ( String externalIdentifierTypeCode : employee.getExternalIdentifiers().keySet() ) {
998                if (KimConstants.PersonExternalIdentifierTypes.TAX.equals(externalIdentifierTypeCode)) {
999                    this.getDvPayeeDetail().setDisbVchrAlienPaymentCode(false);
1000                }
1001            }
1002            // Determine if employee is a research subject
1003            ParameterEvaluator researchPaymentReasonCodeEvaluator = getParameterService().getParameterEvaluator(DisbursementVoucherDocument.class, DisbursementVoucherConstants.RESEARCH_PAYMENT_REASONS_PARM_NM, this.getDvPayeeDetail().getDisbVchrPaymentReasonCode());
1004            if (researchPaymentReasonCodeEvaluator.evaluationSucceeds()) {
1005                if (getParameterService().parameterExists(DisbursementVoucherDocument.class, DisbursementVoucherConstants.RESEARCH_NON_VENDOR_PAY_LIMIT_AMOUNT_PARM_NM)) {
1006                    String researchPayLimit = getParameterService().getParameterValue(DisbursementVoucherDocument.class, DisbursementVoucherConstants.RESEARCH_NON_VENDOR_PAY_LIMIT_AMOUNT_PARM_NM);
1007                    if (StringUtils.isNotBlank(researchPayLimit)) {
1008                        KualiDecimal payLimit = new KualiDecimal(researchPayLimit);
1009    
1010                        if (getDisbVchrCheckTotalAmount().isLessThan(payLimit)) {
1011                            this.getDvPayeeDetail().setDvPayeeSubjectPaymentCode(true);
1012                        }
1013                    }
1014                }
1015            }
1016    
1017            this.disbVchrPayeeTaxControlCode = "";
1018            this.disbVchrPayeeW9CompleteCode = true;
1019        }
1020        
1021        /**
1022         * Finds the address for the given employee, matching the type in the KFS-FP / Disbursement Voucher/ DEFAULT_EMPLOYEE_ADDRESS_TYPE parameter,
1023         * to use as the address for the employee
1024         * @param employee the employee to find a non-default address for
1025         * @return the non-default address, or null if not found
1026         */
1027        protected KimEntityAddress getNonDefaultAddress(Person employee) {
1028            final String addressType = parameterService.getParameterValue(DisbursementVoucherDocument.class, DisbursementVoucherDocument.DEFAULT_EMPLOYEE_ADDRESS_TYPE_PARAMETER_NAME);
1029            final KimEntityInfo entityInfo = getIdentityManagementService().getEntityInfoByPrincipalId(employee.getPrincipalId());
1030            if (entityInfo != null) {
1031                final KimEntityEntityType entityEntityType = getPersonEntityEntityType(entityInfo);
1032                if (entityEntityType != null) {
1033                    final List<? extends KimEntityAddress> addresses = entityEntityType.getAddresses();
1034            
1035                    return findAddressByType(addresses, addressType);
1036                }
1037            }
1038            return null;
1039        }
1040        
1041        /**
1042         * Someday this method will be in Rice.  But...'til it is...lazy loop through the entity entity types in the given
1043         * KimEntityInfo and return the one who has the type of "PERSON"
1044         * @param entityInfo the entity info to loop through entity entity types of
1045         * @return a found entity entity type or null if a PERSON entity entity type is not associated with the given KimEntityInfo record
1046         */
1047        protected KimEntityEntityType getPersonEntityEntityType(KimEntityInfo entityInfo) {
1048            final List<KimEntityEntityTypeInfo> entityEntityTypes = entityInfo.getEntityTypes();
1049            int count = 0;
1050            KimEntityEntityType foundInfo = null;
1051            
1052            while (count < entityEntityTypes.size() && foundInfo == null) {
1053                if (entityEntityTypes.get(count).getEntityTypeCode().equals(KimConstants.EntityTypes.PERSON)) {
1054                    foundInfo = entityEntityTypes.get(count);
1055                }
1056                count += 1;
1057            }
1058            
1059            return foundInfo;
1060        }
1061        
1062        /**
1063         * Given a List of KimEntityAddress and an address type, finds the address in the List with the given type (or null if no matching KimEntityAddress is found)
1064         * @param addresses the List of KimEntityAddress records to search
1065         * @param addressType the address type of the address to return
1066         * @return the found KimEntityAddress, or null if not found
1067         */
1068        protected KimEntityAddress findAddressByType(List<? extends KimEntityAddress> addresses, String addressType) {
1069            KimEntityAddress foundAddress = null;
1070            int count = 0;
1071            
1072            while (count < addresses.size() && foundAddress == null) {
1073                final KimEntityAddress currentAddress = addresses.get(count);
1074                if (currentAddress.getAddressTypeCode().equals(addressType)) {
1075                    foundAddress = currentAddress;
1076                }
1077                count += 1;
1078            }
1079            
1080            return foundAddress;
1081        }
1082    
1083        /**
1084         * @see org.kuali.rice.kns.document.Document#prepareForSave()
1085         */
1086        @Override
1087        public void prepareForSave() {
1088            if (this instanceof AmountTotaling) {
1089                getDocumentHeader().setFinancialDocumentTotalAmount(((AmountTotaling) this).getTotalDollarAmount());
1090            }
1091    
1092            if (dvWireTransfer != null) {
1093                dvWireTransfer.setDocumentNumber(this.documentNumber);
1094            }
1095    
1096            if (dvNonResidentAlienTax != null) {
1097                dvNonResidentAlienTax.setDocumentNumber(this.documentNumber);
1098            }
1099    
1100            dvPayeeDetail.setDocumentNumber(this.documentNumber);
1101    
1102            if (dvNonEmployeeTravel != null) {
1103                dvNonEmployeeTravel.setDocumentNumber(this.documentNumber);
1104                dvNonEmployeeTravel.setTotalTravelAmount(dvNonEmployeeTravel.getTotalTravelAmount());
1105            }
1106    
1107            if (dvPreConferenceDetail != null) {
1108                dvPreConferenceDetail.setDocumentNumber(this.documentNumber);
1109                dvPreConferenceDetail.setDisbVchrConferenceTotalAmt(dvPreConferenceDetail.getDisbVchrConferenceTotalAmt());
1110            }
1111            
1112            if (shouldClearSpecialHandling()) {
1113                clearSpecialHandling();
1114            }
1115        }
1116        
1117        /**
1118         * Determines if the special handling fields should be cleared, based on whether the special handling has been turned off and whether the current node is CAMPUS
1119         * @return true if special handling should be cleared, false otherwise
1120         */
1121        protected boolean shouldClearSpecialHandling() {
1122            if (!isDisbVchrSpecialHandlingCode()) {
1123                // are we at the campus route node?
1124                try {
1125                    List<String> currentNodes = Arrays.asList(getDocumentHeader().getWorkflowDocument().getNodeNames());
1126                    return (currentNodes.contains(DisbursementVoucherConstants.RouteLevelNames.CAMPUS));
1127                }
1128                catch (WorkflowException we) {
1129                    throw new RuntimeException("Workflow Exception while attempting to check route levels", we);
1130                }
1131            }
1132            return false;
1133        }
1134        
1135        /**
1136         * Clears all set special handling fields
1137         */
1138        protected void clearSpecialHandling() {
1139            DisbursementVoucherPayeeDetail payeeDetail = getDvPayeeDetail();
1140            
1141            if (!StringUtils.isBlank(payeeDetail.getDisbVchrSpecialHandlingPersonName())) {
1142                payeeDetail.setDisbVchrSpecialHandlingPersonName(null);
1143            }
1144            if (!StringUtils.isBlank(payeeDetail.getDisbVchrSpecialHandlingLine1Addr())) {
1145                payeeDetail.setDisbVchrSpecialHandlingLine1Addr(null);
1146            }
1147            if (!StringUtils.isBlank(payeeDetail.getDisbVchrSpecialHandlingLine2Addr())) {
1148                payeeDetail.setDisbVchrSpecialHandlingLine2Addr(null);
1149            }
1150            if (!StringUtils.isBlank(payeeDetail.getDisbVchrSpecialHandlingCityName())) {
1151                payeeDetail.setDisbVchrSpecialHandlingCityName(null);
1152            }
1153            if (!StringUtils.isBlank(payeeDetail.getDisbVchrSpecialHandlingStateCode())) {
1154                payeeDetail.setDisbVchrSpecialHandlingStateCode(null);
1155            }
1156            if (!StringUtils.isBlank(payeeDetail.getDisbVchrSpecialHandlingZipCode())) {
1157                payeeDetail.setDisbVchrSpecialHandlingZipCode(null);
1158            }
1159            if (!StringUtils.isBlank(payeeDetail.getDisbVchrSpecialHandlingCountryCode())) {
1160                payeeDetail.setDisbVchrSpecialHandlingCountryCode(null);
1161            }
1162        }
1163    
1164        /**
1165         * This method is overridden to populate some local variables that are not persisted to the database. These values need to be
1166         * computed and saved to the DV Payee Detail BO so they can be serialized to XML for routing. Some of the routing rules rely on
1167         * these variables.
1168         * 
1169         * @see org.kuali.rice.kns.document.DocumentBase#populateDocumentForRouting()
1170         */
1171        @Override
1172        public void populateDocumentForRouting() {
1173            DisbursementVoucherPayeeDetail payeeDetail = getDvPayeeDetail();
1174    
1175            if (payeeDetail.isVendor()) {
1176                payeeDetail.setDisbVchrPayeeEmployeeCode(getVendorService().isVendorInstitutionEmployee(payeeDetail.getDisbVchrVendorHeaderIdNumberAsInteger()));
1177                payeeDetail.setDvPayeeSubjectPaymentCode(getVendorService().isSubjectPaymentVendor(payeeDetail.getDisbVchrVendorHeaderIdNumberAsInteger()));
1178            }
1179            else if (payeeDetail.isEmployee()) {
1180    
1181                // Determine if employee is a research subject
1182                ParameterEvaluator researchPaymentReasonCodeEvaluator = getParameterService().getParameterEvaluator(DisbursementVoucherDocument.class, DisbursementVoucherConstants.RESEARCH_PAYMENT_REASONS_PARM_NM, payeeDetail.getDisbVchrPaymentReasonCode());
1183                if (researchPaymentReasonCodeEvaluator.evaluationSucceeds()) {
1184                    if (getParameterService().parameterExists(DisbursementVoucherDocument.class, DisbursementVoucherConstants.RESEARCH_NON_VENDOR_PAY_LIMIT_AMOUNT_PARM_NM)) {
1185                        String researchPayLimit = getParameterService().getParameterValue(DisbursementVoucherDocument.class, DisbursementVoucherConstants.RESEARCH_NON_VENDOR_PAY_LIMIT_AMOUNT_PARM_NM);
1186                        if (StringUtils.isNotBlank(researchPayLimit)) {
1187                            KualiDecimal payLimit = new KualiDecimal(researchPayLimit);
1188    
1189                            if (getDisbVchrCheckTotalAmount().isLessThan(payLimit)) {
1190                                payeeDetail.setDvPayeeSubjectPaymentCode(true);
1191                            }
1192                        }
1193                    }
1194                }
1195            }
1196    
1197            super.populateDocumentForRouting(); // Call last, serializes to XML
1198        }
1199    
1200        /**
1201         * Clears information that might have been entered for sub tables, but because of changes to the document is longer needed and
1202         * should not be persisted.
1203         */
1204        protected void cleanDocumentData() {
1205            // TODO: warren: this method ain't called!!! maybe this should be called by prepare for save above
1206            if (!DisbursementVoucherConstants.PAYMENT_METHOD_WIRE.equals(this.getDisbVchrPaymentMethodCode()) && !DisbursementVoucherConstants.PAYMENT_METHOD_DRAFT.equals(this.getDisbVchrPaymentMethodCode())) {
1207                getBusinessObjectService().delete(dvWireTransfer);
1208                dvWireTransfer = null;
1209            }
1210    
1211            if (!dvPayeeDetail.isDisbVchrAlienPaymentCode()) {
1212                getBusinessObjectService().delete(dvNonResidentAlienTax);
1213                dvNonResidentAlienTax = null;
1214            }
1215    
1216            DisbursementVoucherPaymentReasonService paymentReasonService = SpringContext.getBean(DisbursementVoucherPaymentReasonService.class);
1217            String paymentReasonCode = this.getDvPayeeDetail().getDisbVchrPaymentReasonCode();
1218            if (!paymentReasonService.isNonEmployeeTravelPaymentReason(paymentReasonCode)) {
1219                getBusinessObjectService().delete(dvNonEmployeeTravel);
1220                dvNonEmployeeTravel = null;
1221            }
1222    
1223            if (!paymentReasonService.isPrepaidTravelPaymentReason(paymentReasonCode)) {
1224                getBusinessObjectService().delete(dvPreConferenceDetail);
1225                dvPreConferenceDetail = null;
1226            }
1227        }
1228    
1229        /**
1230         * @see org.kuali.kfs.sys.document.AccountingDocumentBase#toCopy()
1231         */
1232        @Override
1233        public void toCopy() throws WorkflowException {
1234            super.toCopy();
1235            initiateDocument();
1236    
1237            // clear fields
1238            setDisbVchrContactPhoneNumber(StringUtils.EMPTY);
1239            setDisbVchrContactEmailId(StringUtils.EMPTY);
1240            getDvPayeeDetail().setDisbVchrPayeePersonName(StringUtils.EMPTY);
1241    
1242            getDvPayeeDetail().setDisbVchrPayeeLine1Addr(StringUtils.EMPTY);
1243            getDvPayeeDetail().setDisbVchrPayeeLine2Addr(StringUtils.EMPTY);
1244            getDvPayeeDetail().setDisbVchrPayeeCityName(StringUtils.EMPTY);
1245            getDvPayeeDetail().setDisbVchrPayeeStateCode(StringUtils.EMPTY);
1246            getDvPayeeDetail().setDisbVchrPayeeZipCode(StringUtils.EMPTY);
1247            getDvPayeeDetail().setDisbVchrPayeeCountryCode(StringUtils.EMPTY);
1248    
1249            setDisbVchrPayeeTaxControlCode(StringUtils.EMPTY);
1250    
1251            // clear nra
1252            SpringContext.getBean(DisbursementVoucherTaxService.class).clearNRATaxLines(this);
1253            setDvNonResidentAlienTax(new DisbursementVoucherNonResidentAlienTax());
1254    
1255            // clear waive wire
1256            getDvWireTransfer().setDisbursementVoucherWireTransferFeeWaiverIndicator(false);
1257    
1258            // check vendor id number to see if still valid, if so retrieve their last information and set in the detail inform.
1259            if (!StringUtils.isBlank(getDvPayeeDetail().getDisbVchrPayeeIdNumber())) {
1260                VendorDetail vendorDetail = getVendorService().getVendorDetail(dvPayeeDetail.getDisbVchrVendorHeaderIdNumberAsInteger(), dvPayeeDetail.getDisbVchrVendorDetailAssignedIdNumberAsInteger());
1261                VendorAddress vendorAddress = new VendorAddress();
1262                vendorAddress.setVendorAddressGeneratedIdentifier(dvPayeeDetail.getDisbVchrVendorAddressIdNumberAsInteger());
1263                vendorAddress = (VendorAddress) getBusinessObjectService().retrieve(vendorAddress);
1264    
1265                if (vendorDetail == null) {
1266                    getDvPayeeDetail().setDisbVchrPayeeIdNumber(StringUtils.EMPTY);
1267                    GlobalVariables.getMessageList().add(KFSKeyConstants.WARNING_DV_PAYEE_NONEXISTANT_CLEARED);
1268                }
1269                else {
1270                    templateVendor(vendorDetail, vendorAddress);
1271                }
1272            }
1273            
1274            // this copied DV has not been extracted
1275            this.extractDate = null;
1276            this.paidDate = null;
1277            this.cancelDate = null;
1278            getDocumentHeader().setFinancialDocumentStatusCode(KFSConstants.DocumentStatusCodes.INITIATED);
1279        }
1280    
1281        /**
1282         * generic, shared logic used to initiate a dv document
1283         */
1284        public void initiateDocument() {
1285            Person currentUser = GlobalVariables.getUserSession().getPerson();
1286            setDisbVchrContactPersonName(currentUser.getName());
1287            setDisbVchrContactPhoneNumber(currentUser.getPhoneNumber());
1288            setDisbVchrContactEmailId(currentUser.getEmailAddress());
1289            ChartOrgHolder chartOrg = SpringContext.getBean(org.kuali.kfs.sys.service.FinancialSystemUserService.class).getPrimaryOrganization(currentUser, KFSConstants.ParameterNamespaces.FINANCIAL);
1290            
1291            // Does a valid campus code exist for this person?  If so, simply grab
1292            // the campus code via the business object service.  
1293            if (chartOrg != null && chartOrg.getOrganization() != null) {
1294                setCampusCode(chartOrg.getOrganization().getOrganizationPhysicalCampusCode());
1295            }
1296            // A valid campus code was not found; therefore, use the default affiliated
1297            // campus code.
1298            else {
1299                String affiliatedCampusCode = currentUser.getCampusCode();
1300                setCampusCode(affiliatedCampusCode);
1301            }
1302    
1303            // due date
1304            Calendar calendar = getDateTimeService().getCurrentCalendar();
1305            calendar.add(Calendar.DAY_OF_MONTH, 1);
1306            setDisbursementVoucherDueDate(new Date(calendar.getTimeInMillis()));
1307    
1308            // default doc location
1309            if (StringUtils.isBlank(getDisbursementVoucherDocumentationLocationCode())) {
1310                setDisbursementVoucherDocumentationLocationCode(getParameterService().getParameterValue(DisbursementVoucherDocument.class, DisbursementVoucherConstants.DEFAULT_DOC_LOCATION_PARM_NM));
1311            }
1312    
1313            // default bank code
1314            Bank defaultBank = SpringContext.getBean(BankService.class).getDefaultBankByDocType(this.getClass());
1315            if (defaultBank != null) {
1316                this.disbVchrBankCode = defaultBank.getBankCode();
1317                this.bank = defaultBank;
1318            }
1319        }
1320    
1321        /**
1322         * @see org.kuali.rice.kns.document.DocumentBase#buildListOfDeletionAwareLists()
1323         */
1324        @SuppressWarnings("unchecked")
1325        @Override
1326        public List buildListOfDeletionAwareLists() {
1327            List managedLists = super.buildListOfDeletionAwareLists();
1328    
1329            if (dvNonEmployeeTravel != null) {
1330                managedLists.add(dvNonEmployeeTravel.getDvNonEmployeeExpenses());
1331                managedLists.add(dvNonEmployeeTravel.getDvPrePaidEmployeeExpenses());
1332            }
1333    
1334            if (dvPreConferenceDetail != null) {
1335                managedLists.add(dvPreConferenceDetail.getDvPreConferenceRegistrants());
1336            }
1337    
1338            return managedLists;
1339        }
1340    
1341        /**
1342         * Returns check total.
1343         * 
1344         * @see org.kuali.kfs.sys.document.AccountingDocumentBase#getTotalDollarAmount()
1345         * @return KualiDecimal
1346         */
1347        @Override
1348        public KualiDecimal getTotalDollarAmount() {
1349            return this.getDisbVchrCheckTotalAmount();
1350        }
1351    
1352        /**
1353         * Returns true if accounting line debit
1354         * 
1355         * @param financialDocument submitted accounting document
1356         * @param accountingLine accounting line in accounting document
1357         * @return true if document is debit
1358         * @see IsDebitUtils#isDebitConsideringNothingPositiveOnly(FinancialDocumentRuleBase, FinancialDocument, AccountingLine)
1359         * @see org.kuali.rice.kns.rule.AccountingLineRule#isDebit(org.kuali.rice.kns.document.FinancialDocument,
1360         *      org.kuali.rice.kns.bo.AccountingLine)
1361         */
1362        public boolean isDebit(GeneralLedgerPendingEntrySourceDetail postable) {
1363            // disallow error corrections
1364            DebitDeterminerService isDebitUtils = SpringContext.getBean(DebitDeterminerService.class);
1365            isDebitUtils.disallowErrorCorrectionDocumentCheck(this);
1366    
1367            if (getDvNonResidentAlienTax() != null && getDvNonResidentAlienTax().getFinancialDocumentAccountingLineText() != null && getDvNonResidentAlienTax().getFinancialDocumentAccountingLineText().contains(((AccountingLine) postable).getSequenceNumber().toString())) {
1368                return postable.getAmount().isPositive();
1369            }
1370    
1371            return isDebitUtils.isDebitConsideringNothingPositiveOnly(this, (AccountingLine) postable);
1372        }
1373    
1374        /**
1375         * Override to change the doc type based on payment method. This is needed to pick up different offset definitions.
1376         * 
1377         * @param financialDocument submitted accounting document
1378         * @param accountingLine accounting line in submitted accounting document
1379         * @param explicitEntry explicit GLPE
1380         * @see org.kuali.module.financial.rules.FinancialDocumentRuleBase#customizeExplicitGeneralLedgerPendingEntry(org.kuali.rice.kns.document.FinancialDocument,
1381         *      org.kuali.rice.kns.bo.AccountingLine, org.kuali.module.gl.bo.GeneralLedgerPendingEntry)
1382         */
1383        @Override
1384        public void customizeExplicitGeneralLedgerPendingEntry(GeneralLedgerPendingEntrySourceDetail accountingLine, GeneralLedgerPendingEntry explicitEntry) {
1385    
1386            /* change document type based on payment method to pick up different offsets */
1387            if (DisbursementVoucherConstants.PAYMENT_METHOD_CHECK.equals(getDisbVchrPaymentMethodCode())) {
1388                LOG.debug("changing doc type on pending entry " + explicitEntry.getTransactionLedgerEntrySequenceNumber() + " to " + DisbursementVoucherConstants.DOCUMENT_TYPE_CHECKACH);
1389                explicitEntry.setFinancialDocumentTypeCode(DisbursementVoucherConstants.DOCUMENT_TYPE_CHECKACH);
1390            }
1391            else {
1392                LOG.debug("changing doc type on pending entry " + explicitEntry.getTransactionLedgerEntrySequenceNumber() + " to " + DisbursementVoucherConstants.DOCUMENT_TYPE_CHECKACH);
1393                explicitEntry.setFinancialDocumentTypeCode(DisbursementVoucherConstants.DOCUMENT_TYPE_WTFD);
1394            }
1395        }
1396    
1397        /**
1398         * Return true if GLPE's are generated successfully (i.e. there are either 0 GLPE's or 1 GLPE in disbursement voucher document)
1399         * 
1400         * @param financialDocument submitted financial document
1401         * @param sequenceHelper helper class to keep track of GLPE sequence
1402         * @return true if GLPE's are generated successfully
1403         * @see org.kuali.rice.kns.rule.GenerateGeneralLedgerDocumentPendingEntriesRule#processGenerateDocumentGeneralLedgerPendingEntries(org.kuali.rice.kns.document.FinancialDocument,org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySequenceHelper)
1404         */
1405        @Override
1406        public boolean generateDocumentGeneralLedgerPendingEntries(GeneralLedgerPendingEntrySequenceHelper sequenceHelper) {
1407            if (getGeneralLedgerPendingEntries() == null || getGeneralLedgerPendingEntries().size() < 2) {
1408                LOG.warn("No gl entries for accounting lines.");
1409                return true;
1410            }
1411    
1412            /*
1413             * only generate additional charge entries for payment method wire charge, and if the fee has not been waived
1414             */
1415            if (DisbursementVoucherConstants.PAYMENT_METHOD_WIRE.equals(getDisbVchrPaymentMethodCode()) && !getDvWireTransfer().isDisbursementVoucherWireTransferFeeWaiverIndicator()) {
1416                LOG.debug("generating wire charge gl pending entries.");
1417    
1418                // retrieve wire charge
1419                WireCharge wireCharge = retrieveWireCharge();
1420    
1421                // generate debits
1422                GeneralLedgerPendingEntry chargeEntry = processWireChargeDebitEntries(sequenceHelper, wireCharge);
1423    
1424                // generate credits
1425                processWireChargeCreditEntries(sequenceHelper, wireCharge, chargeEntry);
1426            }
1427    
1428            // for wire or drafts generate bank offset entry (if enabled), for ACH and checks offset will be generated by PDP
1429            if (DisbursementVoucherConstants.PAYMENT_METHOD_WIRE.equals(getDisbVchrPaymentMethodCode()) || DisbursementVoucherConstants.PAYMENT_METHOD_DRAFT.equals(getDisbVchrPaymentMethodCode())) {
1430                generateDocumentBankOffsetEntries(sequenceHelper);
1431            }
1432    
1433            return true;
1434        }
1435    
1436        /**
1437         * Builds an explicit and offset for the wire charge debit. The account associated with the first accounting is used for the
1438         * debit. The explicit and offset entries for the first accounting line and copied and customized for the wire charge.
1439         * 
1440         * @param dvDocument submitted disbursement voucher document
1441         * @param sequenceHelper helper class to keep track of GLPE sequence
1442         * @param wireCharge wireCharge object from current fiscal year
1443         * @return GeneralLedgerPendingEntry generated wire charge debit
1444         */
1445        protected GeneralLedgerPendingEntry processWireChargeDebitEntries(GeneralLedgerPendingEntrySequenceHelper sequenceHelper, WireCharge wireCharge) {
1446            LOG.info("processWireChargeDebitEntries started");
1447            
1448            // grab the explicit entry for the first accounting line and adjust for wire charge entry
1449            GeneralLedgerPendingEntry explicitEntry = new GeneralLedgerPendingEntry(getGeneralLedgerPendingEntry(0));
1450            explicitEntry.setTransactionLedgerEntrySequenceNumber(new Integer(sequenceHelper.getSequenceCounter()));
1451            explicitEntry.setFinancialObjectCode(wireCharge.getExpenseFinancialObjectCode());
1452            explicitEntry.setFinancialSubObjectCode(GENERAL_LEDGER_PENDING_ENTRY_CODE.getBlankFinancialSubObjectCode());
1453            explicitEntry.setTransactionDebitCreditCode(GL_DEBIT_CODE);
1454            
1455            String objectTypeCode = SpringContext.getBean(OptionsService.class).getCurrentYearOptions().getFinObjTypeExpenditureexpCd();
1456            explicitEntry.setFinancialObjectTypeCode(objectTypeCode);
1457            
1458            String originationCode = SpringContext.getBean(HomeOriginationService.class).getHomeOrigination().getFinSystemHomeOriginationCode();
1459            explicitEntry.setFinancialSystemOriginationCode(originationCode);
1460    
1461            if (KFSConstants.COUNTRY_CODE_UNITED_STATES.equals(getDvWireTransfer().getDisbVchrBankCountryCode())) {
1462                explicitEntry.setTransactionLedgerEntryAmount(wireCharge.getDomesticChargeAmt());
1463            }
1464            else {
1465                explicitEntry.setTransactionLedgerEntryAmount(wireCharge.getForeignChargeAmt());
1466            }
1467    
1468            explicitEntry.setTransactionLedgerEntryDescription("Automatic debit for wire transfer fee");
1469    
1470            addPendingEntry(explicitEntry);
1471            sequenceHelper.increment();
1472    
1473            // handle the offset entry
1474            GeneralLedgerPendingEntry offsetEntry = new GeneralLedgerPendingEntry(explicitEntry);
1475            GeneralLedgerPendingEntryService glpeService = SpringContext.getBean(GeneralLedgerPendingEntryService.class);
1476            glpeService.populateOffsetGeneralLedgerPendingEntry(getPostingYear(), explicitEntry, sequenceHelper, offsetEntry);
1477    
1478            addPendingEntry(offsetEntry);
1479            sequenceHelper.increment();
1480    
1481            return explicitEntry;
1482        }
1483    
1484        /**
1485         * Builds an explicit and offset for the wire charge credit. The account and income object code found in the wire charge table
1486         * is used for the entry.
1487         * 
1488         * @param dvDocument submitted disbursement voucher document
1489         * @param sequenceHelper helper class to keep track of GLPE sequence
1490         * @param chargeEntry GLPE charge
1491         * @param wireCharge wireCharge object from current fiscal year
1492         */
1493        protected void processWireChargeCreditEntries(GeneralLedgerPendingEntrySequenceHelper sequenceHelper, WireCharge wireCharge, GeneralLedgerPendingEntry chargeEntry) {
1494            LOG.info("processWireChargeCreditEntries started");
1495            
1496            // copy the charge entry and adjust for credit
1497            GeneralLedgerPendingEntry explicitEntry = new GeneralLedgerPendingEntry(chargeEntry);
1498            explicitEntry.setTransactionLedgerEntrySequenceNumber(new Integer(sequenceHelper.getSequenceCounter()));
1499            explicitEntry.setChartOfAccountsCode(wireCharge.getChartOfAccountsCode());
1500            explicitEntry.setAccountNumber(wireCharge.getAccountNumber());
1501            explicitEntry.setFinancialObjectCode(wireCharge.getIncomeFinancialObjectCode());
1502    
1503            // retrieve object type
1504            ObjectCode objectCode = wireCharge.getIncomeFinancialObject();
1505            explicitEntry.setFinancialObjectTypeCode(objectCode.getFinancialObjectTypeCode());
1506            
1507            explicitEntry.setTransactionDebitCreditCode(GL_CREDIT_CODE);
1508    
1509            explicitEntry.setFinancialSubObjectCode(GENERAL_LEDGER_PENDING_ENTRY_CODE.getBlankFinancialSubObjectCode());
1510            explicitEntry.setSubAccountNumber(GENERAL_LEDGER_PENDING_ENTRY_CODE.getBlankSubAccountNumber());
1511            explicitEntry.setProjectCode(GENERAL_LEDGER_PENDING_ENTRY_CODE.getBlankProjectCode());
1512    
1513            explicitEntry.setTransactionLedgerEntryDescription("Automatic credit for wire transfer fee");
1514    
1515            addPendingEntry(explicitEntry);
1516            sequenceHelper.increment();
1517            
1518            // handle the offset entry
1519            GeneralLedgerPendingEntry offsetEntry = new GeneralLedgerPendingEntry(explicitEntry);
1520            GeneralLedgerPendingEntryService glpeService = SpringContext.getBean(GeneralLedgerPendingEntryService.class);
1521            glpeService.populateOffsetGeneralLedgerPendingEntry(getPostingYear(), explicitEntry, sequenceHelper, offsetEntry);
1522    
1523            addPendingEntry(offsetEntry);
1524            sequenceHelper.increment();
1525        }
1526    
1527        /**
1528         * If bank specification is enabled generates bank offsetting entries for the document amount
1529         * 
1530         * @param sequenceHelper helper class to keep track of GLPE sequence
1531         * @param paymentMethodCode the payment method of this DV
1532         */
1533        public boolean generateDocumentBankOffsetEntries(GeneralLedgerPendingEntrySequenceHelper sequenceHelper) {
1534            boolean success = true;
1535    
1536            if (!SpringContext.getBean(BankService.class).isBankSpecificationEnabled()) {
1537                return success;
1538            }
1539    
1540            this.refreshReferenceObject(KFSPropertyConstants.BANK);
1541    
1542            GeneralLedgerPendingEntryService glpeService = SpringContext.getBean(GeneralLedgerPendingEntryService.class);
1543    
1544            final KualiDecimal bankOffsetAmount = glpeService.getOffsetToCashAmount(this).negated();
1545            GeneralLedgerPendingEntry bankOffsetEntry = new GeneralLedgerPendingEntry();
1546            success &= glpeService.populateBankOffsetGeneralLedgerPendingEntry(getBank(), bankOffsetAmount, this, getPostingYear(), sequenceHelper, bankOffsetEntry, KNSConstants.DOCUMENT_PROPERTY_NAME + "." + KFSPropertyConstants.DISB_VCHR_BANK_CODE);
1547    
1548            if (success) {
1549                AccountingDocumentRuleHelperService accountingDocumentRuleUtil = SpringContext.getBean(AccountingDocumentRuleHelperService.class);
1550                bankOffsetEntry.setTransactionLedgerEntryDescription(accountingDocumentRuleUtil.formatProperty(KFSKeyConstants.Bank.DESCRIPTION_GLPE_BANK_OFFSET));
1551                bankOffsetEntry.setFinancialDocumentTypeCode(DisbursementVoucherConstants.DOCUMENT_TYPE_WTFD);
1552                addPendingEntry(bankOffsetEntry);
1553                sequenceHelper.increment();
1554    
1555                GeneralLedgerPendingEntry offsetEntry = new GeneralLedgerPendingEntry(bankOffsetEntry);
1556                success &= glpeService.populateOffsetGeneralLedgerPendingEntry(getPostingYear(), bankOffsetEntry, sequenceHelper, offsetEntry);
1557                bankOffsetEntry.setFinancialDocumentTypeCode(DisbursementVoucherConstants.DOCUMENT_TYPE_WTFD);
1558                addPendingEntry(offsetEntry);
1559                sequenceHelper.increment();
1560            }
1561    
1562            return success;
1563        }
1564    
1565        /**
1566         * Retrieves the wire transfer information for the current fiscal year.
1567         * 
1568         * @return <code>WireCharge</code>
1569         */
1570        protected WireCharge retrieveWireCharge() {
1571            WireCharge wireCharge = new WireCharge();
1572            wireCharge.setUniversityFiscalYear(SpringContext.getBean(UniversityDateService.class).getCurrentFiscalYear());
1573    
1574            wireCharge = (WireCharge) getBusinessObjectService().retrieve(wireCharge);
1575            if (wireCharge == null) {
1576                LOG.error("Wire charge information not found for current fiscal year.");
1577                throw new RuntimeException("Wire charge information not found for current fiscal year.");
1578            }
1579    
1580            return wireCharge;
1581        }
1582    
1583    
1584        /**
1585         * Gets the payeeAssigned attribute. This method returns a flag that is used to indicate if the payee type and value has been
1586         * set on the DV. This value is used to determine the correct page that should be loaded by the DV flow.
1587         * 
1588         * @return Returns the payeeAssigned.
1589         */
1590        public boolean isPayeeAssigned() {
1591            // If value is false, check state of document. We should assume payee is assigned if document has been saved.
1592            // Otherwise, value will be set during creation process.
1593            if (!payeeAssigned) {
1594                payeeAssigned = !this.getDocumentHeader().getWorkflowDocument().stateIsInitiated();
1595            }
1596            return payeeAssigned;
1597        }
1598    
1599    
1600        /**
1601         * Sets the payeeAssigned attribute value.
1602         * 
1603         * @param payeeAssigned The payeeAssigned to set.
1604         */
1605        public void setPayeeAssigned(boolean payeeAssigned) {
1606            this.payeeAssigned = payeeAssigned;
1607        }
1608    
1609        /**
1610         * Gets the editW9W8BENbox attribute. This method returns a flag that is used to indicate if the W9/W8BEN check box can be edited
1611         * by the initiator on the DV. 
1612         * 
1613         * @return Returns the editW9W8BENbox.
1614         */
1615        public boolean isEditW9W8BENbox() {
1616            String initiatorPrincipalID = this.getDocumentHeader().getWorkflowDocument().getRouteHeader().getInitiatorPrincipalId();
1617            if (GlobalVariables.getUserSession().getPrincipalId().equals(initiatorPrincipalID)) {
1618                editW9W8BENbox = true;            
1619            }
1620            return editW9W8BENbox;
1621        }
1622    
1623        /**
1624         * Sets the editW9W8BENbox attribute value.
1625         * 
1626         * @param editW9W8BENbox The editW9W8BENbox to set.
1627         */
1628        public void setEditW9W8BENbox(boolean editW9W8BENbox) {
1629            this.editW9W8BENbox = editW9W8BENbox;
1630        }
1631        
1632        /**
1633         * Gets the disbVchrPdpBankCode attribute.
1634         * 
1635         * @return Returns the disbVchrPdpBankCode.
1636         */
1637        public String getDisbVchrPdpBankCode() {
1638            return disbVchrPdpBankCode;
1639        }
1640    
1641        /**
1642         * Sets the disbVchrPdpBankCode attribute value.
1643         * 
1644         * @param disbVchrPdpBankCode The disbVchrPDPBankCode to set.
1645         */
1646        public void setDisbVchrPdpBankCode(String disbVchrPdpBankCode) {
1647            this.disbVchrPdpBankCode = disbVchrPdpBankCode;
1648        }   
1649    
1650        /**
1651         * @see org.kuali.rice.kns.document.DocumentBase#getDocumentTitle()
1652         */
1653        @Override
1654        public String getDocumentTitle() {
1655            String documentTitle = super.getDocumentTitle();
1656            return this.buildDocumentTitle(documentTitle);
1657        }
1658    
1659        /**
1660         * build document title based on the properties of current document
1661         * 
1662         * @param the default document title
1663         * @return the combine information of the given title and additional payment indicators 
1664         */
1665        protected String buildDocumentTitle(String title) {  
1666            DisbursementVoucherPayeeDetail payee = getDvPayeeDetail();
1667            if(payee == null) {
1668                return title;
1669            }
1670            
1671            Object[] indicators = new String[3];        
1672            indicators[0] = payee.isEmployee() ? AdHocPaymentIndicator.EMPLOYEE_PAYEE : AdHocPaymentIndicator.OTHER;
1673            indicators[1] = payee.isDisbVchrAlienPaymentCode() ? AdHocPaymentIndicator.ALIEN_PAYEE : AdHocPaymentIndicator.OTHER;
1674            
1675            DisbursementVoucherPaymentReasonService paymentReasonService = SpringContext.getBean(DisbursementVoucherPaymentReasonService.class);
1676            boolean isTaxReviewRequired = paymentReasonService.isTaxReviewRequired(payee.getDisbVchrPaymentReasonCode());
1677            indicators[2] = isTaxReviewRequired ? AdHocPaymentIndicator.PAYMENT_REASON_REQUIRING_TAX_REVIEW : AdHocPaymentIndicator.OTHER;
1678            
1679            for(Object indicator : indicators) {
1680                if(!AdHocPaymentIndicator.OTHER.equals(indicator)) {
1681                    String titlePattern = title + " [{0}:{1}:{2}]";
1682                    return MessageFormat.format(titlePattern, indicators);
1683                }
1684            }
1685        
1686            return title;
1687        }
1688    
1689    
1690        /**
1691         * Provides answers to the following splits: PayeeIsPurchaseOrderVendor RequiresTaxReview RequiresTravelReview
1692         * 
1693         * @see org.kuali.kfs.sys.document.FinancialSystemTransactionalDocumentBase#answerSplitNodeQuestion(java.lang.String)
1694         */
1695        @Override
1696        public boolean answerSplitNodeQuestion(String nodeName) throws UnsupportedOperationException {
1697            if (nodeName.equals(DisbursementVoucherDocument.PAYEE_IS_PURCHASE_ORDER_VENDOR_SPLIT))
1698                return isPayeePurchaseOrderVendor();
1699            if (nodeName.equals(DisbursementVoucherDocument.DOCUMENT_REQUIRES_TAX_REVIEW_SPLIT))
1700                return isTaxReviewRequired();
1701            if (nodeName.equals(DisbursementVoucherDocument.DOCUMENT_REQUIRES_TRAVEL_REVIEW_SPLIT))
1702                return isTravelReviewRequired();
1703            throw new UnsupportedOperationException("Cannot answer split question for this node you call \""+nodeName+"\"");
1704        }
1705        
1706        /**
1707         * @return true if the payee is a purchase order vendor and therefore should receive vendor review, false otherwise
1708         */
1709        protected boolean isPayeePurchaseOrderVendor() {
1710            if (!this.getDvPayeeDetail().getDisbursementVoucherPayeeTypeCode().equals(DisbursementVoucherConstants.DV_PAYEE_TYPE_VENDOR)) {
1711                return false;
1712            }
1713            
1714            VendorDetail vendor = getVendorService().getByVendorNumber(this.getDvPayeeDetail().getDisbVchrPayeeIdNumber());
1715            if (vendor == null) {
1716                return false;
1717            }
1718            
1719            vendor.refreshReferenceObject("vendorHeader");
1720            return vendor.getVendorHeader().getVendorTypeCode().equals(DisbursementVoucherDocument.PURCHASE_ORDER_VENDOR_TYPE);
1721        }
1722        
1723        /**
1724         * Tax review is required under the following circumstances: the payee was an employee the payee was a non-resident alien vendor
1725         * the tax control code = "B" or "H" the payment reason code was "D" the payment reason code was "M" and the campus was listed
1726         * in the CAMPUSES_TAXED_FOR_MOVING_REIMBURSEMENTS_PARAMETER_NAME parameter
1727         * 
1728         * @return true if any of the above conditions exist and this document should receive tax review, false otherwise
1729         */
1730        protected boolean isTaxReviewRequired() {
1731            if (isPayeePurchaseOrderVendorHasWithholding()) {
1732                return true;
1733            }
1734    
1735            boolean isEmployee = this.getDvPayeeDetail().isDisbVchrPayeeEmployeeCode();
1736            if (isEmployee) {
1737                return true;
1738            }
1739            
1740            String payeeTypeCode = this.getDvPayeeDetail().getDisbursementVoucherPayeeTypeCode();
1741            if (payeeTypeCode.equals(DisbursementVoucherConstants.DV_PAYEE_TYPE_EMPLOYEE)) {
1742                return true;
1743            }
1744            
1745            if (payeeTypeCode.equals(DisbursementVoucherConstants.DV_PAYEE_TYPE_VENDOR) && this.getVendorService().isVendorForeign(getDvPayeeDetail().getDisbVchrVendorHeaderIdNumberAsInteger())) {
1746                return true;
1747            }
1748    
1749            String taxControlCode = this.getDisbVchrPayeeTaxControlCode();
1750            if (StringUtils.equals(taxControlCode, DisbursementVoucherDocument.TAX_CONTROL_BACKUP_HOLDING) || StringUtils.equals(taxControlCode,DisbursementVoucherDocument.TAX_CONTROL_HOLD_PAYMENTS)) {
1751                return true;
1752            }
1753    
1754            String paymentReasonCode = this.getDvPayeeDetail().getDisbVchrPaymentReasonCode();
1755            if (this.getDvPymentReasonService().isDecedentCompensationPaymentReason(paymentReasonCode)) {
1756                return true;
1757            }
1758    
1759            if (this.getDvPymentReasonService().isMovingPaymentReason(paymentReasonCode) && taxedCampusForMovingReimbursements()) {
1760                return true;
1761            }
1762            
1763            if (this.getParameterService().getParameterEvaluator(this.getClass(), DisbursementVoucherDocument.PAYMENT_REASONS_REQUIRING_TAX_REVIEW_PARAMETER_NAME, paymentReasonCode).evaluationSucceeds()) {
1764                return true;
1765            }
1766            
1767            return false;
1768        }
1769        
1770        /**
1771         * @return true if the payee is a vendor and has withholding dates therefore should receive tax review, false otherwise
1772         */
1773        protected boolean isPayeePurchaseOrderVendorHasWithholding() {
1774            if (!this.getDvPayeeDetail().getDisbursementVoucherPayeeTypeCode().equals(DisbursementVoucherConstants.DV_PAYEE_TYPE_VENDOR)) {
1775                return false;
1776            }
1777            
1778            VendorDetail vendor = getVendorService().getByVendorNumber(this.getDvPayeeDetail().getDisbVchrPayeeIdNumber());
1779            if (vendor == null) {
1780                return false;
1781            }
1782            
1783            vendor.refreshReferenceObject("vendorHeader");
1784            return (vendor.getVendorHeader().getVendorFederalWithholdingTaxBeginningDate()!= null || vendor.getVendorHeader().getVendorFederalWithholdingTaxEndDate()!= null);
1785        }
1786        
1787        
1788        /**
1789         * Determines if the campus this DV is related to is taxed (and should get tax review routing) for moving reimbursements
1790         * 
1791         * @return true if the campus is taxed for moving reimbursements, false otherwise
1792         */
1793        protected boolean taxedCampusForMovingReimbursements() {
1794            return this.getParameterService().getParameterEvaluator(this.getClass(), DisbursementVoucherConstants.CAMPUSES_TAXED_FOR_MOVING_REIMBURSEMENTS_PARM_NM, this.getCampusCode()).evaluationSucceeds();
1795        }
1796        
1797        /**
1798         * Travel review is required under the following circumstances: payment reason code is "P" or "N"
1799         * 
1800         * @return
1801         */
1802        public boolean isTravelReviewRequired() {
1803            String paymentReasonCode = this.getDvPayeeDetail().getDisbVchrPaymentReasonCode();
1804    
1805            return this.getDvPymentReasonService().isPrepaidTravelPaymentReason(paymentReasonCode) || this.getDvPymentReasonService().isNonEmployeeTravelPaymentReason(paymentReasonCode);
1806            }
1807    
1808        protected PersonService<Person> getPersonService() {
1809            if ( personService == null ) {
1810                personService = SpringContext.getBean(PersonService.class);
1811            }
1812            return personService;
1813        }
1814    
1815    
1816        protected ParameterService getParameterService() {
1817            if ( parameterService == null ) {
1818                parameterService = SpringContext.getBean(ParameterService.class);
1819            }
1820            return parameterService;
1821        }
1822    
1823    
1824        protected VendorService getVendorService() {
1825            if ( vendorService == null ) {
1826                vendorService = SpringContext.getBean(VendorService.class);
1827            }
1828            return vendorService;
1829        }
1830    
1831    
1832        protected BusinessObjectService getBusinessObjectService() {
1833            if ( businessObjectService == null ) {
1834                businessObjectService = SpringContext.getBean(BusinessObjectService.class);
1835            }
1836            return businessObjectService;
1837        }
1838    
1839        /**
1840         * Gets the dvPymentReasonService attribute.
1841         * 
1842         * @return Returns the dvPymentReasonService.
1843         */
1844        public DisbursementVoucherPaymentReasonService getDvPymentReasonService() {
1845            if (dvPymentReasonService == null) {
1846                dvPymentReasonService = SpringContext.getBean(DisbursementVoucherPaymentReasonService.class);
1847            }
1848            return dvPymentReasonService;
1849        }
1850    
1851    
1852        /**
1853         * Gets the identityManagementService attribute. 
1854         * @return Returns the identityManagementService.
1855         */
1856        public static IdentityManagementService getIdentityManagementService() {
1857            if (identityManagementService == null) {
1858                identityManagementService = SpringContext.getBean(IdentityManagementService.class);
1859            }
1860            return identityManagementService;
1861        }
1862    
1863        /**
1864         * Sets the identityManagementService attribute value.
1865         * @param identityManagementService The identityManagementService to set.
1866         */
1867        public static void setIdentityManagementService(IdentityManagementService identityManagementService) {
1868            DisbursementVoucherDocument.identityManagementService = identityManagementService;
1869        }
1870        /**
1871         * @return Returns the disbExcptAttachedIndicator.
1872         */
1873        public boolean isDisbExcptAttachedIndicator() {
1874            return disbExcptAttachedIndicator;
1875        }
1876    
1877        /**
1878         * @param disbExcptAttachedIndicator The disbExcptAttachedIndicator to set.
1879         */
1880        public void setDisbExcptAttachedIndicator(boolean disbExcptAttachedIndicator) {
1881            this.disbExcptAttachedIndicator = disbExcptAttachedIndicator;
1882        }
1883    }