001    /*
002     * Copyright 2011 The Kuali Foundation.
003     * 
004     * Licensed under the Educational Community License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     * 
008     * http://www.opensource.org/licenses/ecl2.php
009     * 
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.kuali.kfs.module.ar.document;
017    
018    import java.util.ArrayList;
019    import java.util.Collection;
020    import java.util.HashMap;
021    import java.util.List;
022    import java.util.Map;
023    
024    import org.apache.commons.lang.StringUtils;
025    import org.kuali.kfs.coa.businessobject.Account;
026    import org.kuali.kfs.coa.businessobject.ObjectCode;
027    import org.kuali.kfs.coa.businessobject.OffsetDefinition;
028    import org.kuali.kfs.coa.service.BalanceTypeService;
029    import org.kuali.kfs.coa.service.OffsetDefinitionService;
030    import org.kuali.kfs.module.ar.ArConstants;
031    import org.kuali.kfs.module.ar.businessobject.AccountsReceivableDocumentHeader;
032    import org.kuali.kfs.module.ar.businessobject.CashControlDetail;
033    import org.kuali.kfs.module.ar.businessobject.CustomerInvoiceDetail;
034    import org.kuali.kfs.module.ar.businessobject.InvoicePaidApplied;
035    import org.kuali.kfs.module.ar.businessobject.NonAppliedDistribution;
036    import org.kuali.kfs.module.ar.businessobject.NonAppliedHolding;
037    import org.kuali.kfs.module.ar.businessobject.NonInvoiced;
038    import org.kuali.kfs.module.ar.businessobject.NonInvoicedDistribution;
039    import org.kuali.kfs.module.ar.businessobject.ReceivableCustomerInvoiceDetail;
040    import org.kuali.kfs.module.ar.businessobject.SystemInformation;
041    import org.kuali.kfs.module.ar.document.service.CustomerInvoiceDocumentService;
042    import org.kuali.kfs.module.ar.document.service.NonAppliedHoldingService;
043    import org.kuali.kfs.module.ar.document.service.PaymentApplicationDocumentService;
044    import org.kuali.kfs.module.ar.document.service.SystemInformationService;
045    import org.kuali.kfs.sys.KFSConstants;
046    import org.kuali.kfs.sys.businessobject.ChartOrgHolder;
047    import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntry;
048    import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySequenceHelper;
049    import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySourceDetail;
050    import org.kuali.kfs.sys.context.SpringContext;
051    import org.kuali.kfs.sys.document.GeneralLedgerPendingEntrySource;
052    import org.kuali.kfs.sys.document.GeneralLedgerPostingDocumentBase;
053    import org.kuali.kfs.sys.service.FinancialSystemUserService;
054    import org.kuali.kfs.sys.service.GeneralLedgerPendingEntryService;
055    import org.kuali.kfs.sys.service.UniversityDateService;
056    import org.kuali.rice.kew.dto.DocumentRouteStatusChangeDTO;
057    import org.kuali.rice.kew.exception.WorkflowException;
058    import org.kuali.rice.kim.bo.Person;
059    import org.kuali.rice.kim.service.PersonService;
060    import org.kuali.rice.kns.exception.ValidationException;
061    import org.kuali.rice.kns.rule.event.BlanketApproveDocumentEvent;
062    import org.kuali.rice.kns.rule.event.KualiDocumentEvent;
063    import org.kuali.rice.kns.rule.event.RouteDocumentEvent;
064    import org.kuali.rice.kns.service.BusinessObjectService;
065    import org.kuali.rice.kns.service.DataDictionaryService;
066    import org.kuali.rice.kns.service.DateTimeService;
067    import org.kuali.rice.kns.service.DocumentService;
068    import org.kuali.rice.kns.service.ParameterService;
069    import org.kuali.rice.kns.util.GlobalVariables;
070    import org.kuali.rice.kns.util.KualiDecimal;
071    import org.kuali.rice.kns.util.ObjectUtils;
072    
073    public class PaymentApplicationDocument extends GeneralLedgerPostingDocumentBase implements GeneralLedgerPendingEntrySource {
074    
075        protected static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(PaymentApplicationDocument.class);
076    
077        protected static final String LAUNCHED_FROM_BATCH = "LaunchedBySystemUser";
078        
079        protected String hiddenFieldForErrors;
080        protected List<InvoicePaidApplied> invoicePaidApplieds;
081        protected List<NonInvoiced> nonInvoiceds;
082        protected Collection<NonInvoicedDistribution> nonInvoicedDistributions;
083        protected Collection<NonAppliedDistribution> nonAppliedDistributions;
084        protected NonAppliedHolding nonAppliedHolding;
085        protected AccountsReceivableDocumentHeader accountsReceivableDocumentHeader;
086        
087        protected transient PaymentApplicationDocumentService paymentApplicationDocumentService;
088        protected transient CashControlDetail cashControlDetail;
089        protected transient FinancialSystemUserService fsUserService;
090        protected transient CustomerInvoiceDocumentService invoiceDocService;
091        protected transient DocumentService docService;
092        protected transient NonAppliedHoldingService nonAppliedHoldingService;
093        protected transient BusinessObjectService boService;
094        
095        // used for non-cash-control payapps
096        protected ArrayList<NonAppliedHolding> nonAppliedHoldingsForCustomer; // control docs for non-cash-control payapps
097    
098        public PaymentApplicationDocument() {
099            super();
100            this.invoicePaidApplieds = new ArrayList<InvoicePaidApplied>();
101            this.nonInvoiceds = new ArrayList<NonInvoiced>();
102            this.nonInvoicedDistributions = new ArrayList<NonInvoicedDistribution>();
103            this.nonAppliedDistributions = new ArrayList<NonAppliedDistribution>();
104            this.nonAppliedHoldingsForCustomer = new ArrayList<NonAppliedHolding>();
105        }
106    
107        /**
108         * Returns the PaymentMediumIdentifier on the associated CashControlDetail, 
109         * if one exists, otherwise returns null.
110         * @return CustomerPaymentMediumIdentifier from the associated CashControlDetail if 
111         *         one exists, otherwise null.
112         */
113        public String getPaymentNumber() {
114            return hasCashControlDetail() ? getCashControlDetail().getCustomerPaymentMediumIdentifier() : null;
115        }
116    
117        public boolean hasCashControlDocument() {
118            return (getCashControlDocument() != null);
119        }
120        
121        /**
122         * @return
123         * @throws WorkflowException
124         */
125        public CashControlDocument getCashControlDocument() {
126            CashControlDetail cashControlDetail = getCashControlDetail();
127            if(ObjectUtils.isNull(cashControlDetail)) {
128                return null;
129            }
130            return cashControlDetail.getCashControlDocument();
131        }
132    
133        public boolean hasCashControlDetail() {
134            return (null != getCashControlDetail());
135        }
136        
137        /**
138         * @return
139         * @throws WorkflowException
140         */
141        public CashControlDetail getCashControlDetail() {
142            if (cashControlDetail == null) {
143                cashControlDetail = getPaymentApplicationDocumentService().getCashControlDetailForPayAppDocNumber(getDocumentNumber());
144            }
145            return cashControlDetail;
146        }
147        
148        public void setCashControlDetail(CashControlDetail cashControlDetail) {
149            this.cashControlDetail = cashControlDetail;
150        }
151        
152        /**
153         * This method calculates the total amount available to be applied on this document.
154         * 
155         * @return The total from the cash control detail if this is a 
156         * cash-control based payapp.  Otherwise, it just returns the total 
157         * available to be applied from previously unapplied holdings.
158         */
159        public KualiDecimal getTotalFromControl() {
160            if (hasCashControlDetail()) {
161                return getCashControlDetail().getFinancialDocumentLineAmount();
162            }
163            else {
164                return getNonAppliedControlAvailableUnappliedAmount();
165            }
166        }
167     
168        /**
169         * This method calculates the total amount available to be applied from 
170         * previously unapplied funds for the associated customer.
171         * 
172         * @return The total amount of previously NonApplied funds available 
173         * to apply to invoices and other applications on this document.
174         */
175        public KualiDecimal getNonAppliedControlAvailableUnappliedAmount() {
176            KualiDecimal amount = KualiDecimal.ZERO;
177            for (NonAppliedHolding nonAppliedHolding : nonAppliedHoldingsForCustomer) {
178                amount = amount.add(nonAppliedHolding.getAvailableUnappliedAmount());
179            }
180            return amount;
181        }
182        
183        /**
184         * @return the sum of all invoice paid applieds.
185         */
186        public KualiDecimal getSumOfInvoicePaidApplieds() {
187            KualiDecimal amount = KualiDecimal.ZERO;
188            for(InvoicePaidApplied payment : getInvoicePaidApplieds()) {
189                KualiDecimal _amount = payment.getInvoiceItemAppliedAmount();
190                if (null == _amount) { _amount = KualiDecimal.ZERO; }
191                amount = amount.add(_amount);
192            }
193            return amount;
194        }
195        
196        /**
197         * @return the sum of all non-invoiced amounts
198         */
199        public KualiDecimal getSumOfNonInvoiceds() {
200            KualiDecimal total = KualiDecimal.ZERO;
201            for(NonInvoiced payment : getNonInvoiceds()) {
202                total = total.add(payment.getFinancialDocumentLineAmount());
203            }
204            return total;
205        }
206        
207        /**
208         * @return the sum of all non-invoiced distributions
209         */
210        public KualiDecimal getSumOfNonInvoicedDistributions() {
211            KualiDecimal amount = KualiDecimal.ZERO;
212            for(NonInvoicedDistribution nonInvoicedDistribution : getNonInvoicedDistributions()) {
213                amount = amount.add(nonInvoicedDistribution.getFinancialDocumentLineAmount());
214            }
215            return amount;
216        }
217        
218        /**
219         * @return the sum of all non-applied distributions
220         */
221        public KualiDecimal getSumOfNonAppliedDistributions() {
222            KualiDecimal amount = KualiDecimal.ZERO;
223            for(NonAppliedDistribution nonAppliedDistribution : getNonAppliedDistributions()) {
224                amount = amount.add(nonAppliedDistribution.getFinancialDocumentLineAmount());
225            }
226            return amount;
227        }
228        
229        /**
230         * @return the non-applied holding total.
231         */
232        public KualiDecimal getNonAppliedHoldingAmount() {
233            if(ObjectUtils.isNull(getNonAppliedHolding())) {
234                return KualiDecimal.ZERO;
235            }
236            if(ObjectUtils.isNull(getNonAppliedHolding().getFinancialDocumentLineAmount())) {
237                return KualiDecimal.ZERO;
238            }
239            return getNonAppliedHolding().getFinancialDocumentLineAmount();
240        }
241        
242        /**
243         * This method returns the total amount allocated against the cash
244         * control total.
245         * 
246         * @return
247         */
248        public KualiDecimal getTotalApplied() {
249            KualiDecimal amount = KualiDecimal.ZERO;
250            amount = amount.add(getSumOfInvoicePaidApplieds());
251            amount = amount.add(getSumOfNonInvoiceds());
252            amount = amount.add(getNonAppliedHoldingAmount());
253            return amount;
254        }
255        
256        /**
257         * This method subtracts the sum of the invoice paid applieds, non-ar and 
258         * unapplied totals from the outstanding amount received via the cash
259         * control document.
260         * 
261         * NOTE this method is not useful for a non-cash control PayApp, as it 
262         * doesnt have access to the control documents until it is saved.  Use 
263         * the same named method on the Form instead.
264         * 
265         * @return
266         * @throws WorkflowException
267         */
268        public KualiDecimal getUnallocatedBalance() {
269            
270            KualiDecimal amount = getTotalFromControl();
271            amount = amount.subtract(getTotalApplied());
272            return amount;
273        }
274        
275        public KualiDecimal getNonArTotal() {
276            KualiDecimal total = KualiDecimal.ZERO;
277            for (NonInvoiced item : getNonInvoiceds()) {
278                total = total.add(item.getFinancialDocumentLineAmount());
279            }
280            return total;
281        }
282    
283        public boolean isFinal() {
284            return isApproved();
285        }
286        
287        public boolean isApproved() {
288            return getDocumentHeader().getWorkflowDocument().stateIsApproved();
289        }
290        
291        /**
292         * 
293         * This method is very specialized for a specific use.  It 
294         * retrieves the list of invoices that have been paid-applied 
295         * by this PayApp document.  
296         * 
297         * It is only used to retrieve what invoices were applied to it, 
298         * when the document is being viewed in Final state.
299         * 
300         * @return
301         */
302        public List<CustomerInvoiceDocument> getInvoicesPaidAgainst() {
303            List<CustomerInvoiceDocument> invoices = new ArrayList<CustomerInvoiceDocument>();
304            
305            //  short circuit if no paidapplieds available
306            if (invoicePaidApplieds == null || invoicePaidApplieds.isEmpty()) {
307                return invoices;
308            }
309            
310            //  get the list of invoice docnumbers from paidapplieds
311            List<String> invoiceDocNumbers = new ArrayList<String>();
312            for (InvoicePaidApplied paidApplied : invoicePaidApplieds) {
313                invoiceDocNumbers.add(paidApplied.getFinancialDocumentReferenceInvoiceNumber());
314            }
315            
316            //  attempt to retrieve all the invoices paid applied against
317            try {
318                invoices.addAll(getDocService().getDocumentsByListOfDocumentHeaderIds(CustomerInvoiceDocument.class, invoiceDocNumbers));
319            }
320            catch (WorkflowException e) {
321                throw new RuntimeException("A WorkflowException was thrown while trying to retrieve documents.", e);
322            }
323            return invoices;
324        }
325    
326        /**
327         * 
328         * This is a very specialized method, that is only intended to be used once the 
329         * document is in a Final/Approved state.
330         * 
331         * It retrieves the PaymentApplication documents that were used as a control source 
332         * for this document, if any, or none, if none.
333         * 
334         * @return
335         */
336        public List<PaymentApplicationDocument> getPaymentApplicationDocumentsUsedAsControlDocuments() {
337            List<PaymentApplicationDocument> payApps = new ArrayList<PaymentApplicationDocument>();
338            
339            //  short circuit if no non-applied-distributions available
340            if ((nonAppliedDistributions == null || nonAppliedDistributions.isEmpty()) && 
341                    (nonInvoicedDistributions == null || nonInvoicedDistributions.isEmpty())) {
342                return payApps;
343            }
344            
345            //  get the list of payapp docnumbers from non-applied-distributions
346            List<String> payAppDocNumbers = new ArrayList<String>();
347            for (NonAppliedDistribution nonAppliedDistribution : nonAppliedDistributions) {
348                if (!payAppDocNumbers.contains(nonAppliedDistribution.getReferenceFinancialDocumentNumber())) {
349                    payAppDocNumbers.add(nonAppliedDistribution.getReferenceFinancialDocumentNumber());
350                }
351            }
352            
353            //  get the list of payapp docnumbers from non-applied-distributions
354            for (NonInvoicedDistribution nonInvoicedDistribution : nonInvoicedDistributions) {
355                if (!payAppDocNumbers.contains(nonInvoicedDistribution.getReferenceFinancialDocumentNumber())) {
356                    payAppDocNumbers.add(nonInvoicedDistribution.getReferenceFinancialDocumentNumber());
357                }
358            }
359            
360            //  exit out if no results, dont even try to retrieve
361            if (payAppDocNumbers.isEmpty()) {
362                return payApps;
363            }
364            
365            //  attempt to retrieve all the invoices paid applied against
366            try {
367                payApps.addAll(getDocService().getDocumentsByListOfDocumentHeaderIds(PaymentApplicationDocument.class, payAppDocNumbers));
368            }
369            catch (WorkflowException e) {
370                throw new RuntimeException("A WorkflowException was thrown while trying to retrieve documents.", e);
371            }
372            return payApps;
373        }
374        
375        public List<NonAppliedHolding> getNonAppliedHoldingsUsedAsControls() {
376            List<NonAppliedHolding> nonAppliedHoldingControls = new ArrayList<NonAppliedHolding>();
377            
378            //  short circuit if no non-applied-distributions available
379            if ((nonAppliedDistributions == null || nonAppliedDistributions.isEmpty()) && 
380                    (nonInvoicedDistributions == null || nonInvoicedDistributions.isEmpty())) {
381                return nonAppliedHoldingControls;
382            }
383            
384            //  get the list of payapp docnumbers from non-applied-distributions
385            List<String> payAppDocNumbers = new ArrayList<String>();
386            for (NonAppliedDistribution nonAppliedDistribution : nonAppliedDistributions) {
387                if (!payAppDocNumbers.contains(nonAppliedDistribution.getReferenceFinancialDocumentNumber())) {
388                    payAppDocNumbers.add(nonAppliedDistribution.getReferenceFinancialDocumentNumber());
389                }
390            }
391            
392            //  get the list of non-invoiced/non-ar distro payapp doc numbers
393            for (NonInvoicedDistribution nonInvoicedDistribution : nonInvoicedDistributions) {
394                if (!payAppDocNumbers.contains(nonInvoicedDistribution.getReferenceFinancialDocumentNumber())) {
395                    payAppDocNumbers.add(nonInvoicedDistribution.getReferenceFinancialDocumentNumber());
396                }
397            }
398    
399            //  attempt to retrieve all the non applied holdings used as controls
400            if (!payAppDocNumbers.isEmpty()) {
401                nonAppliedHoldingControls.addAll(getNonAppliedHoldingService().getNonAppliedHoldingsByListOfDocumentNumbers(payAppDocNumbers));
402            }
403            return nonAppliedHoldingControls;
404        }
405        
406        public List<InvoicePaidApplied> getInvoicePaidApplieds() {
407            return invoicePaidApplieds;
408        }
409    
410        public void setInvoicePaidApplieds(List<InvoicePaidApplied> appliedPayments) {
411            this.invoicePaidApplieds = appliedPayments;
412        }
413    
414        public List<NonInvoiced> getNonInvoiceds() {
415            return nonInvoiceds;
416        }
417    
418        public void setNonInvoiceds(List<NonInvoiced> nonInvoiceds) {
419            this.nonInvoiceds = nonInvoiceds;
420        }
421    
422        public Collection<NonInvoicedDistribution> getNonInvoicedDistributions() {
423            return nonInvoicedDistributions;
424        }
425    
426        public void setNonInvoicedDistributions(Collection<NonInvoicedDistribution> nonInvoicedDistributions) {
427            this.nonInvoicedDistributions = nonInvoicedDistributions;
428        }
429    
430        public Collection<NonAppliedDistribution> getNonAppliedDistributions() {
431            return nonAppliedDistributions;
432        }
433    
434        public void setNonAppliedDistributions(Collection<NonAppliedDistribution> nonAppliedDistributions) {
435            this.nonAppliedDistributions = nonAppliedDistributions;
436        }
437    
438        public NonAppliedHolding getNonAppliedHolding() {
439            return nonAppliedHolding;
440        }
441    
442        public void setNonAppliedHolding(NonAppliedHolding nonAppliedHolding) {
443            this.nonAppliedHolding = nonAppliedHolding;
444        }
445    
446        public AccountsReceivableDocumentHeader getAccountsReceivableDocumentHeader() {
447            return accountsReceivableDocumentHeader;
448        }
449    
450        public void setAccountsReceivableDocumentHeader(AccountsReceivableDocumentHeader accountsReceivableDocumentHeader) {
451            this.accountsReceivableDocumentHeader = accountsReceivableDocumentHeader;
452        }
453    
454        /**
455         * This method retrieves a specific applied payment from the list, by array index
456         * 
457         * @param index the index of the applied payment to retrieve
458         * @return an InvoicePaidApplied
459         */
460        public InvoicePaidApplied getInvoicePaidApplied(int index) {
461            
462            return index < getInvoicePaidApplieds().size() ? getInvoicePaidApplieds().get(index) : new InvoicePaidApplied();
463        }
464    
465        /**
466         * This method retrieves a specific non invoiced payment from the list, by array index
467         * 
468         * @param index the index of the non invoiced payment to retrieve
469         * @return an NonInvoiced
470         */
471        public NonInvoiced getNonInvoiced(int index) {
472            return index < getNonInvoiceds().size() ? getNonInvoiceds().get(index) : new NonInvoiced();
473        }
474    
475        /**
476         * This method gets an ObjectCode from an invoice document.
477         * 
478         * @param invoicePaidApplied
479         * @return
480         * @throws WorkflowException
481         */
482        protected ObjectCode getInvoiceReceivableObjectCode(InvoicePaidApplied invoicePaidApplied) throws WorkflowException {
483            CustomerInvoiceDocument customerInvoiceDocument = invoicePaidApplied.getCustomerInvoiceDocument();
484            CustomerInvoiceDetail customerInvoiceDetail = invoicePaidApplied.getInvoiceDetail();
485            ReceivableCustomerInvoiceDetail receivableInvoiceDetail = new ReceivableCustomerInvoiceDetail(customerInvoiceDetail, customerInvoiceDocument);
486            ObjectCode objectCode = null;
487            if(ObjectUtils.isNotNull(receivableInvoiceDetail) && ObjectUtils.isNotNull(receivableInvoiceDetail.getFinancialObjectCode())) {
488                objectCode = receivableInvoiceDetail.getObjectCode();
489            }
490            return objectCode;
491        }
492        
493        /**
494         * @param sequenceHelper
495         * @return the pending entries for the document
496         */
497        protected List<GeneralLedgerPendingEntry> createPendingEntries(GeneralLedgerPendingEntrySequenceHelper sequenceHelper) throws WorkflowException {
498            
499            // Collection of all generated entries
500            List<GeneralLedgerPendingEntry> generatedEntries = new ArrayList<GeneralLedgerPendingEntry>();
501            
502            // Get handles to the services we need
503            GeneralLedgerPendingEntryService glpeService = SpringContext.getBean(GeneralLedgerPendingEntryService.class);
504            BalanceTypeService balanceTypeService = SpringContext.getBean(BalanceTypeService.class);
505            UniversityDateService universityDateService = SpringContext.getBean(UniversityDateService.class);
506            SystemInformationService systemInformationService = SpringContext.getBean(SystemInformationService.class);
507            OffsetDefinitionService offsetDefinitionService = SpringContext.getBean(OffsetDefinitionService.class);
508            ParameterService parameterService = SpringContext.getBean(ParameterService.class);
509            
510            // Current fiscal year
511            Integer currentFiscalYear = universityDateService.getCurrentFiscalYear();
512            
513            // The processing chart and org comes from the the cash control document if there is one.
514            // If the payment application document is created from scratch though, then we pull it 
515            // from the current user.  Note that we're not checking here that the user actually belongs 
516            // to a billing or processing org, we're assuming that is handled elsewhere.
517            String processingChartCode = null;
518            String processingOrganizationCode = null;
519            if (hasCashControlDocument()) {
520                processingChartCode = getCashControlDocument().getAccountsReceivableDocumentHeader().getProcessingChartOfAccountCode();
521                processingOrganizationCode = getCashControlDocument().getAccountsReceivableDocumentHeader().getProcessingOrganizationCode();
522            }
523            else {
524                Person currentUser = GlobalVariables.getUserSession().getPerson();
525                ChartOrgHolder userOrg = getFsUserService().getPrimaryOrganization(currentUser.getPrincipalId(), ArConstants.AR_NAMESPACE_CODE);
526                processingChartCode = userOrg.getChartOfAccountsCode();
527                processingOrganizationCode = userOrg.getOrganizationCode();
528            }
529    
530            // Some information comes from the cash control document
531            CashControlDocument cashControlDocument = getCashControlDocument();
532            
533            // Get the System Information
534            SystemInformation unappliedSystemInformation = 
535                systemInformationService.getByProcessingChartOrgAndFiscalYear(
536                        processingChartCode, processingOrganizationCode, currentFiscalYear);
537            
538            // Get the university clearing account
539            unappliedSystemInformation.refreshReferenceObject("universityClearingAccount");
540            Account universityClearingAccount = unappliedSystemInformation.getUniversityClearingAccount();
541            
542            // Get the university clearing object, object type and sub-object code
543            String unappliedSubAccountNumber = unappliedSystemInformation.getUniversityClearingSubAccountNumber();
544            String unappliedObjectCode = unappliedSystemInformation.getUniversityClearingObjectCode();
545            String unappliedObjectTypeCode = unappliedSystemInformation.getUniversityClearingObject().getFinancialObjectTypeCode();
546            String unappliedSubObjectCode = unappliedSystemInformation.getUniversityClearingSubObjectCode();
547            
548            // Get the object code for the university clearing account.
549            SystemInformation universityClearingAccountSystemInformation = 
550                systemInformationService.getByProcessingChartOrgAndFiscalYear(
551                        processingChartCode, processingOrganizationCode, currentFiscalYear);
552            String universityClearingAccountObjectCode = universityClearingAccountSystemInformation.getUniversityClearingObjectCode();
553            
554            // Generate glpes for unapplied
555            NonAppliedHolding holding = getNonAppliedHolding();
556            if(ObjectUtils.isNotNull(holding)) {
557                GeneralLedgerPendingEntry actualCreditUnapplied = new GeneralLedgerPendingEntry();
558                actualCreditUnapplied.setUniversityFiscalYear(getPostingYear());
559                actualCreditUnapplied.setTransactionDebitCreditCode(KFSConstants.GL_CREDIT_CODE);
560                actualCreditUnapplied.setChartOfAccountsCode(universityClearingAccount.getChartOfAccountsCode());
561                actualCreditUnapplied.setAccountNumber(universityClearingAccount.getAccountNumber());
562                actualCreditUnapplied.setFinancialObjectCode(unappliedObjectCode);
563                actualCreditUnapplied.setFinancialObjectTypeCode(unappliedObjectTypeCode);
564                actualCreditUnapplied.setFinancialBalanceTypeCode(ArConstants.ACTUALS_BALANCE_TYPE_CODE);
565                actualCreditUnapplied.setFinancialDocumentTypeCode(KFSConstants.FinancialDocumentTypeCodes.PAYMENT_APPLICATION);
566                actualCreditUnapplied.setTransactionLedgerEntryAmount(holding.getFinancialDocumentLineAmount());
567                if (StringUtils.isBlank(unappliedSubAccountNumber)) {
568                    actualCreditUnapplied.setSubAccountNumber(KFSConstants.getDashSubAccountNumber());
569                }
570                else {
571                    actualCreditUnapplied.setSubAccountNumber(unappliedSubAccountNumber);
572                }
573                if (StringUtils.isBlank(unappliedSubObjectCode)) {
574                    actualCreditUnapplied.setFinancialSubObjectCode(KFSConstants.getDashFinancialSubObjectCode());
575                }
576                else {
577                    actualCreditUnapplied.setFinancialSubObjectCode(unappliedSubObjectCode);
578                }
579                actualCreditUnapplied.setProjectCode(KFSConstants.getDashProjectCode());
580                actualCreditUnapplied.setTransactionLedgerEntrySequenceNumber(sequenceHelper.getSequenceCounter());
581                actualCreditUnapplied.setTransactionLedgerEntryDescription(getDocumentHeader().getDocumentDescription());
582                generatedEntries.add(actualCreditUnapplied);
583                sequenceHelper.increment();
584                
585                GeneralLedgerPendingEntry offsetDebitUnapplied = new GeneralLedgerPendingEntry();
586                offsetDebitUnapplied.setUniversityFiscalYear(actualCreditUnapplied.getUniversityFiscalYear());
587                offsetDebitUnapplied.setTransactionDebitCreditCode(KFSConstants.GL_DEBIT_CODE);
588                offsetDebitUnapplied.setChartOfAccountsCode(actualCreditUnapplied.getChartOfAccountsCode());
589                offsetDebitUnapplied.setAccountNumber(actualCreditUnapplied.getAccountNumber());
590                OffsetDefinition offsetDebitDefinition = 
591                    offsetDefinitionService.getByPrimaryId(
592                        getPostingYear(), universityClearingAccount.getChartOfAccountsCode(), 
593                        KFSConstants.FinancialDocumentTypeCodes.PAYMENT_APPLICATION, ArConstants.ACTUALS_BALANCE_TYPE_CODE);
594                offsetDebitDefinition.refreshReferenceObject("financialObject");
595                offsetDebitUnapplied.setFinancialObjectCode(offsetDebitDefinition.getFinancialObjectCode());
596                offsetDebitUnapplied.setFinancialObjectTypeCode(offsetDebitDefinition.getFinancialObject().getFinancialObjectTypeCode());
597                offsetDebitUnapplied.setFinancialBalanceTypeCode(ArConstants.ACTUALS_BALANCE_TYPE_CODE);
598                offsetDebitUnapplied.setFinancialDocumentTypeCode(KFSConstants.FinancialDocumentTypeCodes.PAYMENT_APPLICATION);
599                offsetDebitUnapplied.setTransactionLedgerEntryAmount(actualCreditUnapplied.getTransactionLedgerEntryAmount());
600                offsetDebitUnapplied.setSubAccountNumber(KFSConstants.getDashSubAccountNumber());
601                offsetDebitUnapplied.setFinancialSubObjectCode(KFSConstants.getDashFinancialSubObjectCode());
602                offsetDebitUnapplied.setProjectCode(KFSConstants.getDashProjectCode());
603                offsetDebitUnapplied.setTransactionLedgerEntrySequenceNumber(sequenceHelper.getSequenceCounter());
604                offsetDebitUnapplied.setTransactionLedgerEntryDescription(getDocumentHeader().getDocumentDescription());
605                generatedEntries.add(offsetDebitUnapplied);
606                sequenceHelper.increment();
607    
608                GeneralLedgerPendingEntry actualDebitUnapplied = new GeneralLedgerPendingEntry();
609                actualDebitUnapplied.setUniversityFiscalYear(getPostingYear());
610                actualDebitUnapplied.setTransactionDebitCreditCode(KFSConstants.GL_DEBIT_CODE);
611                actualDebitUnapplied.setChartOfAccountsCode(universityClearingAccount.getChartOfAccountsCode());
612                actualDebitUnapplied.setAccountNumber(universityClearingAccount.getAccountNumber());
613                actualDebitUnapplied.setFinancialObjectCode(unappliedObjectCode);
614                actualDebitUnapplied.setFinancialObjectTypeCode(unappliedObjectTypeCode);
615                actualDebitUnapplied.setFinancialBalanceTypeCode(ArConstants.ACTUALS_BALANCE_TYPE_CODE);
616                actualDebitUnapplied.setFinancialDocumentTypeCode(KFSConstants.FinancialDocumentTypeCodes.PAYMENT_APPLICATION);
617                actualDebitUnapplied.setTransactionLedgerEntryAmount(holding.getFinancialDocumentLineAmount());
618                if (StringUtils.isBlank(unappliedSubAccountNumber)) {
619                    actualDebitUnapplied.setSubAccountNumber(KFSConstants.getDashSubAccountNumber());
620                }
621                else {
622                    actualDebitUnapplied.setSubAccountNumber(unappliedSubAccountNumber);
623                }
624                actualDebitUnapplied.setFinancialSubObjectCode(KFSConstants.getDashFinancialSubObjectCode());
625                actualDebitUnapplied.setProjectCode(KFSConstants.getDashProjectCode());
626                actualDebitUnapplied.setTransactionLedgerEntrySequenceNumber(sequenceHelper.getSequenceCounter());
627                actualDebitUnapplied.setTransactionLedgerEntryDescription(getDocumentHeader().getDocumentDescription());
628                generatedEntries.add(actualDebitUnapplied);
629                sequenceHelper.increment();
630                
631                // Offsets for unapplied entries are just offsets to themselves, same info.
632                // So set the values into the offsets based on the values in the actuals.
633                GeneralLedgerPendingEntry offsetCreditUnapplied = new GeneralLedgerPendingEntry();
634                offsetCreditUnapplied.setUniversityFiscalYear(actualDebitUnapplied.getUniversityFiscalYear());
635                offsetCreditUnapplied.setTransactionDebitCreditCode(KFSConstants.GL_CREDIT_CODE);
636                offsetCreditUnapplied.setChartOfAccountsCode(actualDebitUnapplied.getChartOfAccountsCode());
637                offsetCreditUnapplied.setAccountNumber(actualDebitUnapplied.getAccountNumber());
638                OffsetDefinition offsetCreditDefinition = 
639                    offsetDefinitionService.getByPrimaryId(
640                        getPostingYear(), universityClearingAccount.getChartOfAccountsCode(), 
641                        KFSConstants.FinancialDocumentTypeCodes.PAYMENT_APPLICATION, ArConstants.ACTUALS_BALANCE_TYPE_CODE);
642                offsetCreditDefinition.refreshReferenceObject("financialObject");
643                offsetCreditUnapplied.setFinancialObjectCode(offsetCreditDefinition.getFinancialObjectCode());
644                offsetCreditUnapplied.setFinancialObjectTypeCode(offsetCreditDefinition.getFinancialObject().getFinancialObjectTypeCode());
645                offsetCreditUnapplied.setFinancialBalanceTypeCode(ArConstants.ACTUALS_BALANCE_TYPE_CODE);
646                offsetCreditUnapplied.setFinancialDocumentTypeCode(KFSConstants.FinancialDocumentTypeCodes.PAYMENT_APPLICATION);
647                offsetCreditUnapplied.setTransactionLedgerEntryAmount(actualDebitUnapplied.getTransactionLedgerEntryAmount());
648                offsetCreditUnapplied.setSubAccountNumber(KFSConstants.getDashSubAccountNumber());
649                offsetCreditUnapplied.setFinancialSubObjectCode(KFSConstants.getDashFinancialSubObjectCode());
650                offsetCreditUnapplied.setProjectCode(KFSConstants.getDashProjectCode());
651                offsetCreditUnapplied.setTransactionLedgerEntrySequenceNumber(sequenceHelper.getSequenceCounter());
652                offsetCreditUnapplied.setTransactionLedgerEntryDescription(getDocumentHeader().getDocumentDescription());
653                generatedEntries.add(offsetCreditUnapplied);
654                sequenceHelper.increment();
655            }
656            
657            // Generate glpes for non-ar
658            for(NonInvoiced nonInvoiced : getNonInvoiceds()) {
659                // Actual entries
660                GeneralLedgerPendingEntry actualCreditEntry = new GeneralLedgerPendingEntry();
661                actualCreditEntry.setUniversityFiscalYear(getPostingYear());
662                actualCreditEntry.setTransactionDebitCreditCode(KFSConstants.GL_CREDIT_CODE);
663                actualCreditEntry.setChartOfAccountsCode(nonInvoiced.getChartOfAccountsCode());
664                actualCreditEntry.setAccountNumber(nonInvoiced.getAccountNumber());
665                actualCreditEntry.setFinancialObjectCode(nonInvoiced.getFinancialObjectCode());
666                nonInvoiced.refreshReferenceObject("financialObject");
667                actualCreditEntry.setFinancialObjectTypeCode(nonInvoiced.getFinancialObject().getFinancialObjectTypeCode());
668                actualCreditEntry.setFinancialBalanceTypeCode(ArConstants.ACTUALS_BALANCE_TYPE_CODE);
669                actualCreditEntry.setFinancialDocumentTypeCode(KFSConstants.FinancialDocumentTypeCodes.PAYMENT_APPLICATION);
670                actualCreditEntry.setTransactionLedgerEntryAmount(nonInvoiced.getFinancialDocumentLineAmount());            
671                if (StringUtils.isBlank(nonInvoiced.getSubAccountNumber())) {
672                    actualCreditEntry.setSubAccountNumber(KFSConstants.getDashSubAccountNumber());
673                }
674                else {
675                    actualCreditEntry.setSubAccountNumber(nonInvoiced.getSubAccountNumber());
676                }
677                if (StringUtils.isBlank(nonInvoiced.getFinancialSubObjectCode())) {
678                    actualCreditEntry.setFinancialSubObjectCode(KFSConstants.getDashFinancialSubObjectCode());
679                }
680                else {
681                    actualCreditEntry.setFinancialSubObjectCode(nonInvoiced.getFinancialSubObjectCode());
682                }
683                if (StringUtils.isBlank(nonInvoiced.getProjectCode())) {
684                    actualCreditEntry.setProjectCode(KFSConstants.getDashProjectCode());
685                }
686                else {
687                    actualCreditEntry.setProjectCode(nonInvoiced.getProjectCode());
688                }
689                actualCreditEntry.setTransactionLedgerEntrySequenceNumber(sequenceHelper.getSequenceCounter());
690                actualCreditEntry.setTransactionLedgerEntryDescription(getDocumentHeader().getDocumentDescription());
691                generatedEntries.add(actualCreditEntry);
692                sequenceHelper.increment();
693                
694                GeneralLedgerPendingEntry actualDebitEntry = new GeneralLedgerPendingEntry();
695                actualDebitEntry.setUniversityFiscalYear(getPostingYear());
696                actualDebitEntry.setTransactionDebitCreditCode(KFSConstants.GL_DEBIT_CODE);
697                actualDebitEntry.setChartOfAccountsCode(universityClearingAccount.getChartOfAccountsCode());
698                actualDebitEntry.setAccountNumber(universityClearingAccount.getAccountNumber());
699    
700                if (hasCashControlDocument()) {
701                    actualDebitEntry.setFinancialObjectCode(universityClearingAccountObjectCode);
702                    actualDebitEntry.setFinancialObjectTypeCode(nonInvoiced.getFinancialObject().getFinancialObjectTypeCode());
703                    actualDebitEntry.setFinancialSubObjectCode(KFSConstants.getDashFinancialSubObjectCode());
704                }
705                else {
706                    actualDebitEntry.setFinancialObjectCode(unappliedObjectCode);
707                    actualDebitEntry.setFinancialObjectTypeCode(unappliedObjectTypeCode);
708                    if (StringUtils.isBlank(unappliedSubObjectCode)) {
709                        actualDebitEntry.setFinancialSubObjectCode(KFSConstants.getDashFinancialSubObjectCode());
710                    }
711                    else {
712                        actualDebitEntry.setFinancialSubObjectCode(unappliedSubObjectCode);
713                    }
714                }
715                actualDebitEntry.setFinancialBalanceTypeCode(ArConstants.ACTUALS_BALANCE_TYPE_CODE);
716                actualDebitEntry.setFinancialDocumentTypeCode(KFSConstants.FinancialDocumentTypeCodes.PAYMENT_APPLICATION);
717                actualDebitEntry.setTransactionLedgerEntryAmount(nonInvoiced.getFinancialDocumentLineAmount());            
718                if (StringUtils.isBlank(unappliedSubAccountNumber)) {
719                    actualDebitEntry.setSubAccountNumber(KFSConstants.getDashSubAccountNumber());
720                }
721                else {
722                    actualDebitEntry.setSubAccountNumber(unappliedSubAccountNumber);
723                }
724                actualDebitEntry.setProjectCode(KFSConstants.getDashProjectCode());
725                actualDebitEntry.setTransactionLedgerEntrySequenceNumber(sequenceHelper.getSequenceCounter());
726                actualDebitEntry.setTransactionLedgerEntryDescription(getDocumentHeader().getDocumentDescription());
727                generatedEntries.add(actualDebitEntry);
728                sequenceHelper.increment();
729    
730                // Offset entries
731                GeneralLedgerPendingEntry offsetDebitEntry = new GeneralLedgerPendingEntry();
732                offsetDebitEntry.setTransactionDebitCreditCode(KFSConstants.GL_DEBIT_CODE);
733                offsetDebitEntry.setChartOfAccountsCode(nonInvoiced.getChartOfAccountsCode());
734                offsetDebitEntry.setAccountNumber(nonInvoiced.getAccountNumber());
735                offsetDebitEntry.setUniversityFiscalYear(getPostingYear());
736                OffsetDefinition debitOffsetDefinition = 
737                    offsetDefinitionService.getByPrimaryId(
738                        getPostingYear(), nonInvoiced.getChartOfAccountsCode(), 
739                        KFSConstants.FinancialDocumentTypeCodes.PAYMENT_APPLICATION, ArConstants.ACTUALS_BALANCE_TYPE_CODE);
740                debitOffsetDefinition.refreshReferenceObject("financialObject");
741                offsetDebitEntry.setFinancialObjectCode(debitOffsetDefinition.getFinancialObjectCode());
742                offsetDebitEntry.setFinancialObjectTypeCode(debitOffsetDefinition.getFinancialObject().getFinancialObjectTypeCode());
743                offsetDebitEntry.setFinancialBalanceTypeCode(ArConstants.ACTUALS_BALANCE_TYPE_CODE);
744                offsetDebitEntry.setFinancialDocumentTypeCode(KFSConstants.FinancialDocumentTypeCodes.PAYMENT_APPLICATION);
745                offsetDebitEntry.setTransactionLedgerEntryAmount(nonInvoiced.getFinancialDocumentLineAmount());
746                offsetDebitEntry.setSubAccountNumber(KFSConstants.getDashSubAccountNumber());
747                offsetDebitEntry.setFinancialSubObjectCode(KFSConstants.getDashFinancialSubObjectCode());
748                offsetDebitEntry.setProjectCode(KFSConstants.getDashProjectCode());
749                offsetDebitEntry.setTransactionLedgerEntrySequenceNumber(sequenceHelper.getSequenceCounter());
750                offsetDebitEntry.setTransactionLedgerEntryDescription(getDocumentHeader().getDocumentDescription());
751                generatedEntries.add(offsetDebitEntry);
752                sequenceHelper.increment();
753                
754                GeneralLedgerPendingEntry offsetCreditEntry = new GeneralLedgerPendingEntry();
755                offsetCreditEntry.setTransactionDebitCreditCode(KFSConstants.GL_CREDIT_CODE);
756                offsetCreditEntry.setUniversityFiscalYear(getPostingYear());
757                offsetCreditEntry.setChartOfAccountsCode(universityClearingAccount.getChartOfAccountsCode());
758                offsetCreditEntry.setAccountNumber(universityClearingAccount.getAccountNumber());
759                Integer fiscalYearForCreditOffsetDefinition = null == cashControlDocument ? currentFiscalYear : cashControlDocument.getUniversityFiscalYear();
760                OffsetDefinition creditOffsetDefinition = 
761                    offsetDefinitionService.getByPrimaryId(
762                            fiscalYearForCreditOffsetDefinition, processingChartCode, 
763                            KFSConstants.FinancialDocumentTypeCodes.PAYMENT_APPLICATION, ArConstants.ACTUALS_BALANCE_TYPE_CODE);
764                creditOffsetDefinition.refreshReferenceObject("financialObject");
765                offsetCreditEntry.setFinancialObjectCode(creditOffsetDefinition.getFinancialObjectCode());
766                offsetCreditEntry.setFinancialObjectTypeCode(creditOffsetDefinition.getFinancialObject().getFinancialObjectTypeCode());
767                offsetCreditEntry.setFinancialBalanceTypeCode(ArConstants.ACTUALS_BALANCE_TYPE_CODE);
768                offsetCreditEntry.setFinancialDocumentTypeCode(KFSConstants.FinancialDocumentTypeCodes.PAYMENT_APPLICATION);
769                offsetCreditEntry.setTransactionLedgerEntryAmount(nonInvoiced.getFinancialDocumentLineAmount());
770                offsetCreditEntry.setSubAccountNumber(KFSConstants.getDashSubAccountNumber());
771                offsetCreditEntry.setFinancialSubObjectCode(KFSConstants.getDashFinancialSubObjectCode());
772                offsetCreditEntry.setProjectCode(KFSConstants.getDashProjectCode());
773                offsetCreditEntry.setTransactionLedgerEntrySequenceNumber(sequenceHelper.getSequenceCounter());
774                offsetCreditEntry.setTransactionLedgerEntryDescription(getDocumentHeader().getDocumentDescription());
775                generatedEntries.add(offsetCreditEntry);
776                sequenceHelper.increment();
777            }
778            
779            // Generate GLPEs for applied payments
780            List<InvoicePaidApplied> appliedPayments = getInvoicePaidApplieds();
781            for(InvoicePaidApplied ipa : appliedPayments) {
782                
783                // Skip payments for 0 dollar amount
784                if(KualiDecimal.ZERO.equals(ipa.getInvoiceItemAppliedAmount())) {
785                    continue;
786                }
787                
788                ipa.refreshNonUpdateableReferences();
789                Account billingOrganizationAccount = ipa.getInvoiceDetail().getAccount();
790                ObjectCode invoiceObjectCode = getInvoiceReceivableObjectCode(ipa);
791                ObjectUtils.isNull(invoiceObjectCode); // Refresh 
792                ObjectCode accountsReceivableObjectCode = ipa.getAccountsReceivableObjectCode();
793                ObjectCode unappliedCashObjectCode = ipa.getSystemInformation().getUniversityClearingObject();
794                
795                GeneralLedgerPendingEntry actualDebitEntry = new GeneralLedgerPendingEntry();
796                actualDebitEntry.setUniversityFiscalYear(getPostingYear());
797                actualDebitEntry.setChartOfAccountsCode(universityClearingAccount.getChartOfAccountsCode());
798                actualDebitEntry.setAccountNumber(universityClearingAccount.getAccountNumber());
799                actualDebitEntry.setTransactionDebitCreditCode(KFSConstants.GL_DEBIT_CODE);
800                actualDebitEntry.setTransactionLedgerEntryAmount(ipa.getInvoiceItemAppliedAmount());
801                if (hasCashControlDocument()) {
802                    actualDebitEntry.setFinancialObjectCode(unappliedCashObjectCode.getFinancialObjectCode());
803                    actualDebitEntry.setFinancialObjectTypeCode(unappliedCashObjectCode.getFinancialObjectTypeCode());
804                    actualDebitEntry.setFinancialSubObjectCode(KFSConstants.getDashFinancialSubObjectCode());
805                }
806                else {
807                    actualDebitEntry.setFinancialObjectCode(unappliedObjectCode);
808                    actualDebitEntry.setFinancialObjectTypeCode(unappliedObjectTypeCode);
809                    if (StringUtils.isBlank(unappliedSubObjectCode)) {
810                        actualDebitEntry.setFinancialSubObjectCode(KFSConstants.getDashFinancialSubObjectCode());
811                    }
812                    else {
813                        actualDebitEntry.setFinancialSubObjectCode(unappliedSubObjectCode);
814                    }
815                }
816                if (StringUtils.isBlank(unappliedSubAccountNumber)) {
817                    actualDebitEntry.setSubAccountNumber(KFSConstants.getDashSubAccountNumber());
818                }
819                else {
820                    actualDebitEntry.setSubAccountNumber(unappliedSubAccountNumber);
821                }
822                actualDebitEntry.setProjectCode(KFSConstants.getDashProjectCode());
823                actualDebitEntry.setFinancialBalanceTypeCode(ArConstants.ACTUALS_BALANCE_TYPE_CODE);
824                actualDebitEntry.setFinancialDocumentTypeCode(KFSConstants.FinancialDocumentTypeCodes.PAYMENT_APPLICATION);
825                actualDebitEntry.setTransactionLedgerEntrySequenceNumber(sequenceHelper.getSequenceCounter());
826                actualDebitEntry.setTransactionLedgerEntryDescription(getDocumentHeader().getDocumentDescription());            
827                generatedEntries.add(actualDebitEntry);
828                sequenceHelper.increment();
829                
830                GeneralLedgerPendingEntry actualCreditEntry = new GeneralLedgerPendingEntry();
831                actualCreditEntry.setUniversityFiscalYear(getPostingYear());
832                actualCreditEntry.setChartOfAccountsCode(universityClearingAccount.getChartOfAccountsCode());
833                actualCreditEntry.setAccountNumber(universityClearingAccount.getAccountNumber());
834                actualCreditEntry.setTransactionDebitCreditCode(KFSConstants.GL_CREDIT_CODE);
835                actualCreditEntry.setTransactionLedgerEntryAmount(ipa.getInvoiceItemAppliedAmount());
836                actualCreditEntry.setFinancialObjectCode(invoiceObjectCode.getFinancialObjectCode());
837                actualCreditEntry.setFinancialObjectTypeCode(invoiceObjectCode.getFinancialObjectTypeCode());
838                actualCreditEntry.setFinancialBalanceTypeCode(ArConstants.ACTUALS_BALANCE_TYPE_CODE);
839                actualCreditEntry.setFinancialDocumentTypeCode(KFSConstants.FinancialDocumentTypeCodes.PAYMENT_APPLICATION);
840                actualCreditEntry.setSubAccountNumber(KFSConstants.getDashSubAccountNumber());
841                actualCreditEntry.setFinancialSubObjectCode(KFSConstants.getDashFinancialSubObjectCode());
842                actualCreditEntry.setProjectCode(KFSConstants.getDashProjectCode());                        
843                glpeService.populateOffsetGeneralLedgerPendingEntry(getPostingYear(), actualDebitEntry, sequenceHelper, actualCreditEntry);
844                actualCreditEntry.setTransactionLedgerEntrySequenceNumber(sequenceHelper.getSequenceCounter());
845                generatedEntries.add(actualCreditEntry);
846                sequenceHelper.increment();
847                
848                GeneralLedgerPendingEntry offsetDebitEntry = new GeneralLedgerPendingEntry();
849                offsetDebitEntry.setUniversityFiscalYear(getPostingYear());
850                offsetDebitEntry.setAccountNumber(billingOrganizationAccount.getAccountNumber());
851                offsetDebitEntry.setChartOfAccountsCode(billingOrganizationAccount.getChartOfAccountsCode());
852                offsetDebitEntry.setTransactionDebitCreditCode(KFSConstants.GL_CREDIT_CODE);
853                offsetDebitEntry.setTransactionLedgerEntryAmount(ipa.getInvoiceItemAppliedAmount());
854                offsetDebitEntry.setFinancialObjectCode(invoiceObjectCode.getFinancialObjectCode());
855                offsetDebitEntry.setFinancialObjectTypeCode(invoiceObjectCode.getFinancialObjectTypeCode());
856                offsetDebitEntry.setFinancialBalanceTypeCode(ArConstants.ACTUALS_BALANCE_TYPE_CODE);
857                offsetDebitEntry.setFinancialDocumentTypeCode(KFSConstants.FinancialDocumentTypeCodes.PAYMENT_APPLICATION);
858                if (StringUtils.isBlank(ipa.getInvoiceDetail().getSubAccountNumber())) {
859                    offsetDebitEntry.setSubAccountNumber(KFSConstants.getDashSubAccountNumber());
860                }
861                else {
862                    offsetDebitEntry.setSubAccountNumber(ipa.getInvoiceDetail().getSubAccountNumber());
863                }
864                if (StringUtils.isBlank(ipa.getInvoiceDetail().getFinancialSubObjectCode())) {
865                    offsetDebitEntry.setFinancialSubObjectCode(KFSConstants.getDashFinancialSubObjectCode());
866                }
867                else {
868                    offsetDebitEntry.setFinancialSubObjectCode(ipa.getInvoiceDetail().getFinancialSubObjectCode());
869                }
870                if (StringUtils.isBlank(ipa.getInvoiceDetail().getProjectCode())) {
871                    offsetDebitEntry.setProjectCode(KFSConstants.getDashProjectCode());
872                }
873                else {
874                    offsetDebitEntry.setProjectCode(ipa.getInvoiceDetail().getProjectCode());
875                }
876                offsetDebitEntry.setTransactionLedgerEntrySequenceNumber(sequenceHelper.getSequenceCounter());
877                offsetDebitEntry.setTransactionLedgerEntryDescription(getDocumentHeader().getDocumentDescription());            
878                generatedEntries.add(offsetDebitEntry);
879                sequenceHelper.increment();
880    
881                GeneralLedgerPendingEntry offsetCreditEntry = new GeneralLedgerPendingEntry();
882                offsetCreditEntry.setUniversityFiscalYear(getPostingYear());
883                offsetCreditEntry.setAccountNumber(billingOrganizationAccount.getAccountNumber());
884                offsetCreditEntry.setChartOfAccountsCode(billingOrganizationAccount.getChartOfAccountsCode());
885                offsetCreditEntry.setTransactionDebitCreditCode(KFSConstants.GL_DEBIT_CODE);
886                offsetCreditEntry.setTransactionLedgerEntryAmount(ipa.getInvoiceItemAppliedAmount());
887                offsetCreditEntry.setFinancialObjectCode(accountsReceivableObjectCode.getFinancialObjectCode());
888                offsetCreditEntry.setFinancialObjectTypeCode(accountsReceivableObjectCode.getFinancialObjectTypeCode());
889                offsetCreditEntry.setFinancialBalanceTypeCode(ArConstants.ACTUALS_BALANCE_TYPE_CODE);
890                offsetCreditEntry.setFinancialDocumentTypeCode(KFSConstants.FinancialDocumentTypeCodes.PAYMENT_APPLICATION);
891                offsetCreditEntry.setSubAccountNumber(KFSConstants.getDashSubAccountNumber());
892                offsetCreditEntry.setFinancialSubObjectCode(KFSConstants.getDashFinancialSubObjectCode());
893                offsetCreditEntry.setProjectCode(KFSConstants.getDashProjectCode());
894                offsetCreditEntry.refreshNonUpdateableReferences();
895                glpeService.populateOffsetGeneralLedgerPendingEntry(getPostingYear(), offsetDebitEntry, sequenceHelper, offsetCreditEntry);
896                generatedEntries.add(offsetCreditEntry);
897                sequenceHelper.increment();
898            }
899            
900            // Set the origination code for all entries.
901            for(GeneralLedgerPendingEntry entry : generatedEntries) {
902                entry.setFinancialSystemOriginationCode("01");
903            }
904            
905            return generatedEntries;
906        }
907        
908        /**
909         * @see org.kuali.kfs.sys.document.GeneralLedgerPendingEntrySource#generateDocumentGeneralLedgerPendingEntries(org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySequenceHelper)
910         */
911        public boolean generateDocumentGeneralLedgerPendingEntries(GeneralLedgerPendingEntrySequenceHelper sequenceHelper) {
912            try {
913                List<GeneralLedgerPendingEntry> entries = createPendingEntries(sequenceHelper);
914                for(GeneralLedgerPendingEntry entry : entries) {
915                    addPendingEntry(entry);
916                }
917            } catch(Throwable t) {
918                LOG.error("Exception encountered while generating pending entries.",t);
919                return false;
920            }
921            
922            return true;
923        }
924        
925        public boolean generateGeneralLedgerPendingEntries(GeneralLedgerPendingEntrySourceDetail glpeSourceDetail, GeneralLedgerPendingEntrySequenceHelper sequenceHelper) {
926            return true;
927        }
928    
929        public KualiDecimal getGeneralLedgerPendingEntryAmountForDetail(GeneralLedgerPendingEntrySourceDetail glpeSourceDetail) {
930            return null;
931        }
932    
933        public List<GeneralLedgerPendingEntrySourceDetail> getGeneralLedgerPendingEntrySourceDetails() {
934            return new ArrayList<GeneralLedgerPendingEntrySourceDetail>();
935        }
936    
937        public boolean isDebit(GeneralLedgerPendingEntrySourceDetail postable) {
938            return false;
939        }
940    
941        /**
942         * 
943         * This method is used ONLY for handleRouteStatus change and other 
944         * postProcessor related tasks (like getWorkflowEngineDocumentIdsToLock()) 
945         * and should not otherwise be used.  The reason this is its own method 
946         * is to make sure that handleRouteStatusChange and 
947         * getWorkflowEngineDocumentIdsToLock use the same method to retrieve 
948         * what invoices to update.
949         * @return
950         */
951        protected List<String> getInvoiceNumbersToUpdateOnFinal() {
952            List<String> docIds = new ArrayList<String>();
953            for(InvoicePaidApplied ipa : getInvoicePaidApplieds()) {
954                docIds.add(ipa.getFinancialDocumentReferenceInvoiceNumber());
955            }
956            return docIds;
957        }
958        
959        @Override
960        public List<Long> getWorkflowEngineDocumentIdsToLock() {
961            List<String> docIdStrings = getInvoiceNumbersToUpdateOnFinal();
962            if (docIdStrings == null || docIdStrings.isEmpty()) {
963                return null;
964            }
965            List<Long> docIds = new ArrayList<Long>();
966            for (int i = 0; i < docIdStrings.size(); i++) { // damn I miss ruby sometimes
967                docIds.add(new Long(docIdStrings.get(i)));
968            }
969            return docIds;
970        }
971    
972        @Override
973        public void doRouteStatusChange(DocumentRouteStatusChangeDTO statusChangeEvent) {
974            super.doRouteStatusChange(statusChangeEvent);
975            
976            if(getDocumentHeader().getWorkflowDocument().stateIsFinal()) {
977                DateTimeService dateTimeService = SpringContext.getBean(DateTimeService.class);
978                
979                //  get the now time to stamp invoices with
980                java.sql.Date today = new java.sql.Date(dateTimeService.getCurrentDate().getTime());
981                
982                List<String> invoiceDocNumbers = getInvoiceNumbersToUpdateOnFinal();
983                for(String invoiceDocumentNumber : invoiceDocNumbers) {
984                    CustomerInvoiceDocument invoice = null;
985                    
986                    //  attempt to retrieve the invoice doc
987                    try {
988                         invoice = (CustomerInvoiceDocument) getDocService().getByDocumentHeaderId(invoiceDocumentNumber);
989                    } catch(WorkflowException we) {
990                        LOG.error("Failed to load the Invoice document due to a WorkflowException.", we);
991                    }
992                    if (invoice == null) {
993                        throw new RuntimeException("DocumentService returned a Null CustomerInvoice Document for Doc# " + invoiceDocumentNumber + ".");
994                    }
995                    
996                    // KULAR-384 - close the invoice if its open and the openAmount is zero
997                    if (invoice.getOpenAmount().isZero() && invoice.isOpenInvoiceIndicator()) {
998                        invoice.setClosedDate(today);
999                        invoice.setOpenInvoiceIndicator(false);
1000                        getDocService().updateDocument(invoice);
1001                    }
1002                }
1003            }
1004        }
1005    
1006        @Override
1007        public List buildListOfDeletionAwareLists() {
1008            List deletionAwareLists = super.buildListOfDeletionAwareLists();
1009            if (invoicePaidApplieds != null) { deletionAwareLists.add(invoicePaidApplieds); }  
1010            if (nonInvoiceds != null) { deletionAwareLists.add(nonInvoiceds); }  
1011            if (nonInvoicedDistributions != null) { deletionAwareLists.add(nonInvoicedDistributions); }  
1012            if (nonAppliedDistributions != null) { deletionAwareLists.add(nonAppliedDistributions); }  
1013            return deletionAwareLists;
1014        }
1015    
1016        @Override
1017        public void prepareForSave(KualiDocumentEvent event) {
1018            super.prepareForSave(event);
1019            
1020            // set primary key for NonAppliedHolding if data entered
1021            if (ObjectUtils.isNotNull(this.nonAppliedHolding)) {
1022                if (ObjectUtils.isNull(this.nonAppliedHolding.getReferenceFinancialDocumentNumber())) {
1023                    this.nonAppliedHolding.setReferenceFinancialDocumentNumber(this.documentNumber);
1024                }
1025            }
1026            
1027            //  generate GLPEs only when routing or blanket approving
1028            if (event instanceof RouteDocumentEvent || event instanceof BlanketApproveDocumentEvent) {
1029                // if this document is not generated thru CashControl, 
1030                // create nonApplied and nonInvoiced Distributions
1031                if (!this.hasCashControlDetail()) {
1032                    createDistributions();
1033                }
1034    
1035                GeneralLedgerPendingEntryService glpeService = SpringContext.getBean(GeneralLedgerPendingEntryService.class); 
1036                if (!glpeService.generateGeneralLedgerPendingEntries(this)) {
1037                    logErrors();
1038                    throw new ValidationException("general ledger GLPE generation failed");
1039                }
1040            }
1041    
1042        }
1043    
1044        public PaymentApplicationDocumentService getPaymentApplicationDocumentService() {
1045            if(null == paymentApplicationDocumentService) {
1046                paymentApplicationDocumentService = SpringContext.getBean(PaymentApplicationDocumentService.class);
1047            }
1048            return paymentApplicationDocumentService;
1049        }
1050    
1051        protected FinancialSystemUserService getFsUserService() {
1052            if (fsUserService == null) {
1053                fsUserService = SpringContext.getBean(FinancialSystemUserService.class);
1054            }
1055            return fsUserService;
1056        }
1057        
1058        protected CustomerInvoiceDocumentService getInvoiceDocService() {
1059            if (invoiceDocService == null) {
1060                invoiceDocService = SpringContext.getBean(CustomerInvoiceDocumentService.class);
1061            }
1062            return invoiceDocService;
1063        }
1064        
1065        protected DocumentService getDocService() {
1066            if (docService == null) {
1067                docService = SpringContext.getBean(DocumentService.class);
1068            }
1069            return docService;
1070        }
1071        
1072        protected NonAppliedHoldingService getNonAppliedHoldingService() {
1073            if (nonAppliedHoldingService == null) {
1074                nonAppliedHoldingService = SpringContext.getBean(NonAppliedHoldingService.class);
1075            }
1076            return nonAppliedHoldingService;
1077        }
1078        
1079        protected BusinessObjectService getBoService() {
1080            if (boService == null) {
1081                boService = SpringContext.getBean(BusinessObjectService.class);
1082            }
1083            return boService;
1084        }
1085        
1086        public String getHiddenFieldForErrors() {
1087            return hiddenFieldForErrors;
1088        }
1089    
1090        public void setHiddenFieldForErrors(String hiddenFieldForErrors) {
1091            this.hiddenFieldForErrors = hiddenFieldForErrors;
1092        }
1093    
1094        /**
1095         * 
1096         * Retrieves the NonApplied Holdings that are the Controls for this 
1097         * PaymentApplication.  
1098         * 
1099         * Note that this is dangerous to use and should not be relied upon.
1100         * The data is never persisted to the database, so will always be 
1101         * null/empty when retrieved fresh.  It is only populated while the 
1102         * document is live from the website, or while its in flight in workflow, due 
1103         * to the fact that it has been serialized.
1104         * 
1105         * You should probably not be using this method unless you are sure you know 
1106         * what you are doing.
1107         * 
1108         * @return
1109         */
1110        public Collection<NonAppliedHolding> getNonAppliedHoldingsForCustomer() {
1111            return nonAppliedHoldingsForCustomer;
1112        }
1113    
1114        /**
1115         * 
1116         * Warning, this property is not ever persisted to the database, and is only 
1117         * used during workflow processing (since its been serialized) and during 
1118         * presentation of the document on the webapp.
1119         * 
1120         * You should probably not be using this method unless you are sure you know 
1121         * what you are doing.
1122         * 
1123         * @param nonApplieds
1124         */
1125        public void setNonAppliedHoldingsForCustomer(ArrayList<NonAppliedHolding> nonApplieds) {
1126            this.nonAppliedHoldingsForCustomer = nonApplieds;
1127        }
1128    
1129        /**
1130         * 
1131         * Collects and returns the combined distributions from NonInvoiced/NonAr and Unapplied.
1132         * 
1133         * This method is intended to be used only when the document has gone to final, to show what 
1134         * control documents were issued what funds.
1135         * 
1136         * The return value is a Map<String,KualiDecimal> where the key is the NonAppliedHolding's 
1137         * ReferenceFinancialDocumentNumber and the value is the Amount to be applied.
1138         *  
1139         * @return
1140         */
1141        public Map<String,KualiDecimal> getDistributionsFromControlDocuments() {
1142            if (!isFinal()) {
1143                throw new UnsupportedOperationException("This method should only be used once the document has been approved/gone to final.");
1144            }
1145    
1146            Map<String,KualiDecimal> distributions = new HashMap<String,KualiDecimal>();
1147            
1148            //  short circuit if no non-applied-distributions available
1149            if ((nonAppliedDistributions == null || nonAppliedDistributions.isEmpty()) && 
1150                    (nonInvoicedDistributions == null || nonInvoicedDistributions.isEmpty())) {
1151                return distributions;
1152            }
1153            
1154            //  get the list of payapp docnumbers from non-applied-distributions
1155            for (NonAppliedDistribution nonAppliedDistribution : nonAppliedDistributions) {
1156                String refDocNbr = nonAppliedDistribution.getReferenceFinancialDocumentNumber();
1157                if (distributions.containsKey(refDocNbr)) {
1158                    distributions.put(refDocNbr, (distributions.get(refDocNbr).add(nonAppliedDistribution.getFinancialDocumentLineAmount())));
1159                }
1160                else {
1161                    distributions.put(refDocNbr, nonAppliedDistribution.getFinancialDocumentLineAmount());
1162                }
1163            }
1164            
1165            //  get the list of payapp docnumbers from non-applied-distributions
1166            for (NonInvoicedDistribution nonInvoicedDistribution : nonInvoicedDistributions) {
1167                String refDocNbr = nonInvoicedDistribution.getReferenceFinancialDocumentNumber();
1168                if (distributions.containsKey(refDocNbr)) {
1169                    distributions.put(refDocNbr, (distributions.get(refDocNbr).add(nonInvoicedDistribution.getFinancialDocumentLineAmount())));
1170                }
1171                else {
1172                    distributions.put(refDocNbr, nonInvoicedDistribution.getFinancialDocumentLineAmount());
1173                }
1174            }
1175            
1176            return distributions;
1177        }
1178        
1179        /**
1180         *
1181         *  Walks through the nonAppliedHoldings passed in (the control docs) and allocates how the 
1182         *  funding should be allocated.
1183         *  
1184         *  This function is intended to be used when the document is still live, ie not for when its 
1185         *  been finalized. 
1186         *  
1187         *  The return value is a Map<String,KualiDecimal> where the key is the NonAppliedHolding's 
1188         *  ReferenceFinancialDocumentNumber and the value is the Amount to be applied.
1189         *  
1190         */
1191        public Map<String,KualiDecimal> allocateFundsFromUnappliedControls(List<NonAppliedHolding> nonAppliedHoldings, KualiDecimal amountToBeApplied) {
1192            if (nonAppliedHoldings == null) {
1193                throw new IllegalArgumentException("A null value for the parameter [nonAppliedHoldings] was passed in.");
1194            }
1195            if (amountToBeApplied == null) {
1196                throw new IllegalArgumentException("A null ovalue for the parameter [amountToBeApplied] was passed in.");
1197            }
1198            if (isFinal()) {
1199                throw new UnsupportedOperationException("This method should not be used when the document has been approved/gone to final.");
1200            }
1201            
1202            // special-case the situation where the amountToBeApplied is negative, then make all allocations zero
1203            if (amountToBeApplied.isNegative()) {
1204                Map<String,KualiDecimal> allocations = new HashMap<String,KualiDecimal>();
1205                for (NonAppliedHolding nonAppliedHolding : nonAppliedHoldings) {
1206                    allocations.put(nonAppliedHolding.getReferenceFinancialDocumentNumber(), KualiDecimal.ZERO);
1207                }
1208                return allocations;
1209            }
1210            
1211            Map<String,KualiDecimal> allocations = new HashMap<String,KualiDecimal>();
1212            KualiDecimal remainingAmount = new KualiDecimal(amountToBeApplied.toString()); //clone it
1213            
1214            //  due to the way the control list is generated, this will result in applying 
1215            // from the oldest to newest, which is the ordering desired.  If this ever changes, 
1216            // then the internal logic here should be to apply to the oldest doc first, and then 
1217            // move forward in time until you run out of money or docs
1218            for (NonAppliedHolding nonAppliedHolding : nonAppliedHoldings) {
1219                String refDocNumber = nonAppliedHolding.getReferenceFinancialDocumentNumber();
1220                
1221                //  this shouldnt ever happen, but lets sanity check it
1222                if (allocations.containsKey(nonAppliedHolding.getReferenceFinancialDocumentNumber())) {
1223                    throw new RuntimeException("The same NonAppliedHolding RefDocNumber came up twice, which should never happen.");
1224                }
1225                else {
1226                    allocations.put(refDocNumber, KualiDecimal.ZERO);
1227                }
1228                
1229                if (remainingAmount.isGreaterThan(KualiDecimal.ZERO)) {
1230                    if (nonAppliedHoldings.iterator().hasNext()) {
1231                        if (remainingAmount.isLessEqual(nonAppliedHolding.getAvailableUnappliedAmount())) {
1232                            allocations.put(refDocNumber, remainingAmount);
1233                            remainingAmount = remainingAmount.subtract(remainingAmount);
1234                        }
1235                        else {
1236                            allocations.put(refDocNumber, nonAppliedHolding.getAvailableUnappliedAmount());
1237                            remainingAmount = remainingAmount.subtract(nonAppliedHolding.getAvailableUnappliedAmount());
1238                        }
1239                    }
1240                }
1241            }
1242            return allocations;
1243        }
1244        
1245        // this method is only used by Unapplied PayApp. 
1246        // create nonApplied and nonInvoiced Distributions
1247        public void createDistributions() {
1248            
1249            // if there are non nonApplieds, then we have nothing to do
1250            if (nonAppliedHoldingsForCustomer == null || nonAppliedHoldingsForCustomer.isEmpty()) {
1251                return;
1252            }
1253            
1254            Collection<InvoicePaidApplied> invoicePaidAppliedsForCurrentDoc = this.getInvoicePaidApplieds();
1255            Collection<NonInvoiced> nonInvoicedsForCurrentDoc = this.getNonInvoiceds();
1256    
1257            for(NonAppliedHolding nonAppliedHoldings : this.getNonAppliedHoldingsForCustomer()) {
1258                
1259                // check if payment has been applied to Invoices
1260                // create Unapplied Distribution for each PaidApplied
1261                KualiDecimal remainingUnappliedForDistribution = nonAppliedHoldings.getAvailableUnappliedAmount();
1262                for(InvoicePaidApplied invoicePaidAppliedForCurrentDoc : invoicePaidAppliedsForCurrentDoc) {
1263                    KualiDecimal paidAppliedDistributionAmount = invoicePaidAppliedForCurrentDoc.getPaidAppiedDistributionAmount();
1264                    KualiDecimal remainingPaidAppliedForDistribution = invoicePaidAppliedForCurrentDoc.getInvoiceItemAppliedAmount().subtract(paidAppliedDistributionAmount);
1265                    if (remainingPaidAppliedForDistribution.equals(KualiDecimal.ZERO) ||
1266                        remainingUnappliedForDistribution.equals(KualiDecimal.ZERO)) {
1267                        continue;
1268                    }
1269                    
1270                        // set NonAppliedDistributions for the current document
1271                        NonAppliedDistribution nonAppliedDistribution = new NonAppliedDistribution();
1272                        nonAppliedDistribution.setDocumentNumber(invoicePaidAppliedForCurrentDoc.getDocumentNumber());
1273                        nonAppliedDistribution.setPaidAppliedItemNumber(invoicePaidAppliedForCurrentDoc.getPaidAppliedItemNumber());
1274                        nonAppliedDistribution.setReferenceFinancialDocumentNumber(nonAppliedHoldings.getReferenceFinancialDocumentNumber());
1275                        if (remainingPaidAppliedForDistribution.isLessEqual(remainingUnappliedForDistribution)) {
1276                            nonAppliedDistribution.setFinancialDocumentLineAmount(remainingPaidAppliedForDistribution);
1277                            remainingUnappliedForDistribution = remainingUnappliedForDistribution.subtract(remainingPaidAppliedForDistribution);
1278                            invoicePaidAppliedForCurrentDoc.setPaidAppiedDistributionAmount(paidAppliedDistributionAmount.add(remainingPaidAppliedForDistribution));
1279                        }
1280                        else {
1281                            nonAppliedDistribution.setFinancialDocumentLineAmount(remainingUnappliedForDistribution);
1282                            invoicePaidAppliedForCurrentDoc.setPaidAppiedDistributionAmount(paidAppliedDistributionAmount.add(remainingUnappliedForDistribution));
1283                            remainingUnappliedForDistribution = KualiDecimal.ZERO;
1284                        }
1285                        this.nonAppliedDistributions.add(nonAppliedDistribution);
1286                }
1287    
1288                // check if payment has been applied to NonAR
1289                // create NonAR distribution for each NonAR Applied row
1290                for(NonInvoiced nonInvoicedForCurrentDoc : nonInvoicedsForCurrentDoc) {
1291                    KualiDecimal nonInvoicedDistributionAmount = nonInvoicedForCurrentDoc.getNonInvoicedDistributionAmount();
1292                    KualiDecimal remainingNonInvoicedForDistribution = nonInvoicedForCurrentDoc.getFinancialDocumentLineAmount().subtract(nonInvoicedDistributionAmount);
1293                    if (remainingNonInvoicedForDistribution.equals(KualiDecimal.ZERO) ||
1294                        remainingUnappliedForDistribution.equals(KualiDecimal.ZERO)) {
1295                        continue;
1296                    }
1297                    
1298                    // set NonAppliedDistributions for the current document
1299                    NonInvoicedDistribution nonInvoicedDistribution = new NonInvoicedDistribution();
1300                    nonInvoicedDistribution.setDocumentNumber(nonInvoicedForCurrentDoc.getDocumentNumber());
1301                    nonInvoicedDistribution.setFinancialDocumentLineNumber(nonInvoicedForCurrentDoc.getFinancialDocumentLineNumber());
1302                    nonInvoicedDistribution.setReferenceFinancialDocumentNumber(nonAppliedHoldings.getReferenceFinancialDocumentNumber());
1303                    if (remainingNonInvoicedForDistribution.isLessEqual(remainingUnappliedForDistribution)) {
1304                        nonInvoicedDistribution.setFinancialDocumentLineAmount(remainingNonInvoicedForDistribution);
1305                        remainingUnappliedForDistribution = remainingUnappliedForDistribution.subtract(remainingNonInvoicedForDistribution);
1306                        nonInvoicedForCurrentDoc.setNonInvoicedDistributionAmount(nonInvoicedDistributionAmount.add(remainingNonInvoicedForDistribution));
1307                    }
1308                    else {
1309                        nonInvoicedDistribution.setFinancialDocumentLineAmount(remainingUnappliedForDistribution);
1310                        nonInvoicedForCurrentDoc.setNonInvoicedDistributionAmount(nonInvoicedDistributionAmount.add(remainingUnappliedForDistribution));
1311                        remainingUnappliedForDistribution = KualiDecimal.ZERO;
1312                    }
1313                    this.nonInvoicedDistributions.add(nonInvoicedDistribution);
1314                }
1315            }
1316        }
1317        
1318        /**
1319         * 
1320         * @see org.kuali.kfs.sys.document.FinancialSystemTransactionalDocumentBase#answerSplitNodeQuestion(java.lang.String)
1321         */
1322        @Override
1323        public boolean answerSplitNodeQuestion(String nodeName) throws UnsupportedOperationException {
1324            if (LAUNCHED_FROM_BATCH.equals(nodeName)) {
1325                return launchedFromBatch();
1326            }
1327            throw new UnsupportedOperationException("answerSplitNode('" + nodeName + "') was called but no handler for nodeName specified.");
1328        }
1329    
1330        // determines if the doc was launched by SYSTEM_USER, if so, then it was launched from batch
1331        protected boolean launchedFromBatch() {
1332            boolean result = false;
1333            Person initiator = SpringContext.getBean(PersonService.class).getPersonByPrincipalName(KFSConstants.SYSTEM_USER);
1334            result = initiator.getPrincipalId().equalsIgnoreCase(getDocumentHeader().getWorkflowDocument().getInitiatorPrincipalId());
1335            return result;
1336        }
1337     
1338        /** CUSTOM SEARCH HELPER METHODS **/
1339        
1340        /**
1341         * 
1342         * This method is defined to assist in the custom search implementation.
1343         * @return
1344         */
1345        public String getUnappliedCustomerNumber() {
1346            if(nonAppliedHolding==null) {
1347                return "";
1348            }
1349            return nonAppliedHolding.getCustomerNumber();
1350        }
1351        
1352        /**
1353         * 
1354         * This method is defined to assist in the custom search implementation.
1355         * @return
1356         */
1357        public String getUnappliedCustomerName() {
1358            if(nonAppliedHolding==null) {
1359                return "";
1360            }
1361            return nonAppliedHolding.getCustomer().getCustomerName();
1362        }
1363        
1364        /**
1365         * 
1366         * This method is defined to assist in the custom search implementation.
1367         * @return
1368         */
1369        public String getInvoiceAppliedCustomerNumber() {
1370            return getAccountsReceivableDocumentHeader().getCustomerNumber();
1371        }
1372        
1373        /**
1374         * 
1375         * This method is defined to assist in the custom search implementation.
1376         * @return
1377         */
1378        public String getInvoiceAppliedCustomerName() {
1379            return getAccountsReceivableDocumentHeader().getCustomer().getCustomerName();
1380        }
1381        
1382    }
1383