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