001: /*
002: * Copyright 2007 The Kuali Foundation.
003: *
004: * Licensed under the Educational Community License, Version 1.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/ecl1.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.module.financial.service.impl;
017:
018: import java.math.BigDecimal;
019: import java.sql.Timestamp;
020: import java.util.ArrayList;
021: import java.util.Collection;
022: import java.util.Date;
023: import java.util.HashSet;
024: import java.util.Iterator;
025: import java.util.LinkedHashMap;
026: import java.util.List;
027: import java.util.Map;
028: import java.util.Set;
029:
030: import org.apache.commons.lang.StringUtils;
031: import org.kuali.core.bo.user.UniversalUser;
032: import org.kuali.core.exceptions.UserNotFoundException;
033: import org.kuali.core.service.BusinessObjectService;
034: import org.kuali.core.service.DateTimeService;
035: import org.kuali.core.service.DocumentService;
036: import org.kuali.core.service.UniversalUserService;
037: import org.kuali.core.util.GeneralLedgerPendingEntrySequenceHelper;
038: import org.kuali.core.util.KualiDecimal;
039: import org.kuali.core.util.ObjectUtils;
040: import org.kuali.kfs.KFSConstants;
041: import org.kuali.kfs.KFSPropertyConstants;
042: import org.kuali.kfs.bo.GeneralLedgerPendingEntry;
043: import org.kuali.kfs.bo.SourceAccountingLine;
044: import org.kuali.kfs.context.SpringContext;
045: import org.kuali.kfs.rule.event.AccountingDocumentSaveWithNoLedgerEntryGenerationEvent;
046: import org.kuali.kfs.service.GeneralLedgerPendingEntryService;
047: import org.kuali.kfs.service.ParameterService;
048: import org.kuali.kfs.service.impl.ParameterConstants;
049: import org.kuali.module.financial.bo.DisbursementVoucherNonEmployeeExpense;
050: import org.kuali.module.financial.bo.DisbursementVoucherNonEmployeeTravel;
051: import org.kuali.module.financial.bo.DisbursementVoucherPayeeDetail;
052: import org.kuali.module.financial.bo.DisbursementVoucherPreConferenceDetail;
053: import org.kuali.module.financial.bo.DisbursementVoucherPreConferenceRegistrant;
054: import org.kuali.module.financial.bo.Payee;
055: import org.kuali.module.financial.dao.DisbursementVoucherDao;
056: import org.kuali.module.financial.document.DisbursementVoucherDocument;
057: import org.kuali.module.financial.rules.DisbursementVoucherRuleConstants;
058: import org.kuali.module.financial.service.DisbursementVoucherExtractService;
059: import org.kuali.module.pdp.PdpConstants;
060: import org.kuali.module.pdp.bo.Batch;
061: import org.kuali.module.pdp.bo.CustomerProfile;
062: import org.kuali.module.pdp.bo.PaymentAccountDetail;
063: import org.kuali.module.pdp.bo.PaymentDetail;
064: import org.kuali.module.pdp.bo.PaymentGroup;
065: import org.kuali.module.pdp.bo.PaymentNoteText;
066: import org.kuali.module.pdp.bo.PaymentStatus;
067: import org.kuali.module.pdp.bo.PdpUser;
068: import org.kuali.module.pdp.service.CustomerProfileService;
069: import org.kuali.module.pdp.service.PaymentFileService;
070: import org.kuali.module.pdp.service.PaymentGroupService;
071: import org.kuali.module.pdp.service.ReferenceService;
072: import org.springframework.transaction.annotation.Transactional;
073:
074: import edu.iu.uis.eden.exception.WorkflowException;
075:
076: /**
077: *
078: * This is the default implementation of the DisbursementVoucherExtractService interface.
079: */
080: @Transactional
081: public class DisbursementVoucherExtractServiceImpl implements
082: DisbursementVoucherExtractService {
083: private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger
084: .getLogger(DisbursementVoucherExtractServiceImpl.class);
085:
086: private static final String CAMPUS_BY_PAYMENT_REASON_PARAM = "CAMPUS_BY_PAYMENT_REASON";
087:
088: private ParameterService parameterService;
089: private DisbursementVoucherDao disbursementVoucherDao;
090: private DateTimeService dateTimeService;
091: private UniversalUserService universalUserService;
092: private CustomerProfileService customerProfileService;
093: private PaymentFileService paymentFileService;
094: private PaymentGroupService paymentGroupService;
095: private ReferenceService referenceService;
096: private int maxNoteLines;
097:
098: // This should only be set to true when testing this system. Setting this to true will run the code but
099: // won't set the doc status to extracted
100: boolean testMode = false;
101:
102: /**
103: * This method extracts all payments from a disbursement voucher with a status code of "A" and uploads them as a
104: * batch for processing.
105: *
106: * @return Always returns true if the method completes.
107: *
108: * @see org.kuali.module.financial.service.DisbursementVoucherExtractService#extractPayments()
109: */
110: public boolean extractPayments() {
111: LOG.debug("extractPayments() started");
112:
113: Date processRunDate = dateTimeService.getCurrentDate();
114:
115: String noteLines = parameterService.getParameterValue(
116: ParameterConstants.PRE_DISBURSEMENT_ALL.class,
117: PdpConstants.ApplicationParameterKeys.MAX_NOTE_LINES);
118:
119: try {
120: maxNoteLines = Integer.parseInt(noteLines);
121: } catch (NumberFormatException nfe) {
122: throw new IllegalArgumentException(
123: "Invalid Max Notes Lines parameter");
124: }
125:
126: String userId = parameterService
127: .getParameterValue(
128: DisbursementVoucherDocument.class,
129: DisbursementVoucherRuleConstants.DvPdpExtractGroup.DV_PDP_USER_ID);
130: UniversalUser uuser;
131: try {
132: uuser = universalUserService
133: .getUniversalUserByAuthenticationUserId(userId);
134: } catch (UserNotFoundException e) {
135: LOG
136: .debug("extractPayments() Unable to find user "
137: + userId);
138: throw new IllegalArgumentException("Unable to find user "
139: + userId);
140: }
141:
142: // Get a list of campuses that have documents with an A status.
143: Set<String> campusList = getCampusListByDocumentStatusCode("A");
144:
145: // Process each campus one at a time
146: for (String campusCode : campusList) {
147: extractPaymentsForCampus(campusCode, uuser, processRunDate);
148: }
149:
150: return true;
151: }
152:
153: /**
154: *
155: * This method extracts all outstanding payments from all the disbursement vouchers in approved status for a given campus
156: * and adds these payments to a batch file that is uploaded for processing.
157: *
158: * @param campusCode The id code of the campus the payments will be retrieved for.
159: * @param user The user object used when creating the batch file to upload with outstanding payments.
160: * @param processRunDate This is the date that the batch file is created, often this value will be today's date.
161: */
162: private void extractPaymentsForCampus(String campusCode,
163: UniversalUser user, Date processRunDate) {
164: LOG.debug("extractPaymentsForCampus() started for campus: "
165: + campusCode);
166:
167: Batch batch = createBatch(campusCode, user, processRunDate);
168: Integer count = 0;
169: BigDecimal totalAmount = new BigDecimal("0");
170:
171: Collection<DisbursementVoucherDocument> dvd = getListByDocumentStatusCodeCampus(
172: DisbursementVoucherRuleConstants.DocumentStatusCodes.APPROVED,
173: campusCode);
174: for (DisbursementVoucherDocument document : dvd) {
175: addPayment(document, batch, processRunDate);
176: count++;
177: totalAmount = totalAmount.add(document
178: .getDisbVchrCheckTotalAmount().bigDecimalValue());
179: }
180:
181: batch.setPaymentCount(count);
182: batch.setPaymentTotalAmount(totalAmount);
183: paymentFileService.saveBatch(batch);
184: paymentFileService.sendLoadEmail(batch);
185: }
186:
187: /**
188: *
189: * This method creates a payment group from the disbursement voucher and batch provided and persists that group to the database.
190: * @param document The document used to build a payment group detail.
191: * @param batch The batch file used to build a payment group and detail.
192: * @param processRunDate The date the batch file is to post.
193: */
194: private void addPayment(DisbursementVoucherDocument document,
195: Batch batch, Date processRunDate) {
196: LOG.debug("addPayment() started");
197:
198: PaymentGroup pg = buildPaymentGroup(document, batch);
199: PaymentDetail pd = buildPaymentDetail(document, batch,
200: processRunDate);
201:
202: pd.setPaymentGroup(pg);
203: pg.addPaymentDetails(pd);
204: paymentGroupService.save(pg);
205:
206: if (!testMode) {
207: try {
208: document
209: .getDocumentHeader()
210: .setFinancialDocumentStatusCode(
211: DisbursementVoucherRuleConstants.DocumentStatusCodes.EXTRACTED);
212: document.setExtractDate(new java.sql.Date(
213: processRunDate.getTime()));
214: SpringContext
215: .getBean(DocumentService.class)
216: .saveDocument(
217: document,
218: AccountingDocumentSaveWithNoLedgerEntryGenerationEvent.class);
219: } catch (WorkflowException we) {
220: LOG
221: .error("Could not save disbursement voucher document #"
222: + document.getDocumentNumber()
223: + ": "
224: + we);
225: throw new RuntimeException(we);
226: }
227: }
228: }
229:
230: /**
231: *
232: * This method creates a PaymentGroup from the disbursement voucher and batch provided. The values provided by the
233: * disbursement voucher are used to assign appropriate attributes to the payment group, including address and payee detail
234: * information.
235: *
236: * The information added to the payment group includes tax encoding to identify if taxes should be taken out of the
237: * payment. The tax rules vary depending on the type of individual or entity being paid
238: *
239: * @param document The document to be used for retrieving the information about the payee being paid.
240: * @param batch The batch that the payment group will be associated with.
241: * @return A PaymentGroup object fully populated with all the values necessary to make a payment.
242: */
243: private PaymentGroup buildPaymentGroup(
244: DisbursementVoucherDocument document, Batch batch) {
245: LOG.debug("buildPaymentGroup() started");
246:
247: PaymentGroup pg = new PaymentGroup();
248: pg.setBatch(batch);
249: pg.setCombineGroups(Boolean.TRUE);
250: pg.setCampusAddress(Boolean.FALSE);
251:
252: DisbursementVoucherPayeeDetail pd = document.getDvPayeeDetail();
253: String rc = pd.getDisbVchrPaymentReasonCode();
254:
255: if (pd.isVendor()) {
256: // TODO Write this when Vendor support is added
257:
258: // These are taxable
259: // ((VH.vndr_ownr_cd IN ('NP', 'FC')
260: // OR (VH.vndr_ownr_cd = 'CP' AND VH.vndr_ownr_ctgry_cd IS NULL))
261: // AND PD.dv_pmt_reas_cd IN ('H', 'J'))
262: // OR
263: // ((VH.vndr_ownr_cd = 'CP' AND VH.vndr_ownr_ctgry_cd = 'H')
264: // AND PD.dv_pmt_reas_cd IN ('C', 'E', 'H', 'L', 'J'))
265: // OR
266: // ((VH.vndr_ownr_cd IN ('NR', 'ID', 'PT')
267: // OR (VH.vndr_ownr_cd = 'CP' AND VH.vndr_ownr_ctgry_cd = 'H'))
268: // AND PD.dv_pmt_reas_cd IN ('A', 'C', 'E', 'H', 'R', 'T', 'X', 'Y', 'L', 'J'))
269: // OR
270: // ((VH.vndr_ownr_cd = 'CP' AND VH.vndr_ownr_ctgry_cd = 'L')
271: // AND PD.dv_pmt_reas_cd IN ('A', 'E', 'H', 'R', 'T', 'X', 'L', 'J')))
272: pg.setTaxablePayment(Boolean.FALSE);
273: } else if (pd.isEmployee()) {
274: pg.setEmployeeIndicator(Boolean.TRUE);
275: pg
276: .setPayeeIdTypeCd(PdpConstants.PayeeIdTypeCodes.EMPLOYEE_ID);
277:
278: // All payments are taxable except research participant, rental & royalties
279: pg
280: .setTaxablePayment((!DisbursementVoucherRuleConstants.PaymentReasonCodes.RESEARCH_PARTICIPANT
281: .equals(rc))
282: && (!DisbursementVoucherRuleConstants.PaymentReasonCodes.RENTAL_PAYMENT
283: .equals(rc))
284: && (!DisbursementVoucherRuleConstants.PaymentReasonCodes.ROYALTIES
285: .equals(rc)));
286: } else if (pd.isPayee()) {
287: pg.setEmployeeIndicator(Boolean.FALSE);
288: pg.setPayeeIdTypeCd(PdpConstants.PayeeIdTypeCodes.PAYEE_ID);
289:
290: Payee payee = disbursementVoucherDao.getPayee(pd
291: .getDisbVchrPayeeIdNumber());
292: String potc = payee.getPayeeOwnershipTypCd();
293:
294: // These determine if it is taxable
295: pg.setTaxablePayment(Boolean.FALSE);
296: if ("C".equals(potc) && "HJ".indexOf(rc) >= 0) {
297: pg.setTaxablePayment(Boolean.TRUE);
298: }
299: if ("M".equals(potc) && "CEHLJ".indexOf(rc) >= 0) {
300: pg.setTaxablePayment(Boolean.TRUE);
301: }
302: if ("IPSH".indexOf(potc) >= 0
303: && "ACEHRTXYLJ".indexOf(rc) >= 0) {
304: pg.setTaxablePayment(Boolean.TRUE);
305: }
306: if ("L".equals(potc) && "AEHRTXLJ".indexOf(rc) >= 0) {
307: pg.setTaxablePayment(Boolean.TRUE);
308: }
309: }
310:
311: pg.setCity(pd.getDisbVchrPayeeCityName());
312: pg.setCountry(pd.getDisbVchrPayeeCountryCode());
313: pg.setLine1Address(pd.getDisbVchrPayeeLine1Addr());
314: pg.setLine2Address(pd.getDisbVchrPayeeLine2Addr());
315: pg.setPayeeName(pd.getDisbVchrPayeePersonName());
316: pg.setPayeeId(pd.getDisbVchrPayeeIdNumber());
317: pg.setState(pd.getDisbVchrPayeeStateCode());
318: pg.setZipCd(pd.getDisbVchrPayeeZipCode());
319:
320: pg.setPaymentDate(batch.getFileProcessTimestamp());
321:
322: // It doesn't look like the DV has a way to do immediate processes
323: pg.setProcessImmediate(Boolean.FALSE);
324: pg.setPymtAttachment(document.isDisbVchrAttachmentCode());
325: pg.setPymtSpecialHandling(document
326: .isDisbVchrSpecialHandlingCode());
327: pg.setNraPayment(pd.isDisbVchrAlienPaymentCode());
328:
329: PaymentStatus open = (PaymentStatus) referenceService.getCode(
330: "PaymentStatus", PdpConstants.PaymentStatusCodes.OPEN);
331: pg.setPaymentStatus(open);
332:
333: return pg;
334: }
335:
336: /**
337: *
338: * This method builds a payment detail object from the disbursement voucher document provided and links that detail file
339: * to the batch and process run date given.
340: *
341: * @param document The disbursement voucher document to retrieve payment information from to populate the PaymentDetail.
342: * @param batch The batch file associated with the payment.
343: * @param processRunDate The date of the payment detail invoice.
344: * @return A fully populated PaymentDetail instance.
345: */
346: private PaymentDetail buildPaymentDetail(
347: DisbursementVoucherDocument document, Batch batch,
348: Date processRunDate) {
349: LOG.debug("buildPaymentDetail() started");
350:
351: PaymentDetail pd = new PaymentDetail();
352: if (StringUtils.isNotEmpty(document.getDocumentHeader()
353: .getOrganizationDocumentNumber())) {
354: pd.setOrganizationDocNbr(document.getDocumentHeader()
355: .getOrganizationDocumentNumber());
356: }
357: pd.setCustPaymentDocNbr(document.getDocumentNumber());
358: pd.setInvoiceDate(new Timestamp(processRunDate.getTime()));
359: pd.setOrigInvoiceAmount(document.getDisbVchrCheckTotalAmount()
360: .bigDecimalValue());
361: pd.setInvTotDiscountAmount(new BigDecimal("0"));
362: pd.setInvTotOtherCreditAmount(new BigDecimal("0"));
363: pd.setInvTotOtherDebitAmount(new BigDecimal("0"));
364: pd.setInvTotShipAmount(new BigDecimal("0"));
365: pd.setNetPaymentAmount(document.getDisbVchrCheckTotalAmount()
366: .bigDecimalValue());
367: pd.setPrimaryCancelledPayment(Boolean.FALSE);
368: pd
369: .setFinancialDocumentTypeCode(DisbursementVoucherRuleConstants.DOCUMENT_TYPE_CHECKACH);
370:
371: // Handle accounts
372: for (Iterator iter = document.getSourceAccountingLines()
373: .iterator(); iter.hasNext();) {
374: SourceAccountingLine sal = (SourceAccountingLine) iter
375: .next();
376:
377: PaymentAccountDetail pad = new PaymentAccountDetail();
378: pad.setFinChartCode(sal.getChartOfAccountsCode());
379: pad.setAccountNbr(sal.getAccountNumber());
380: if (StringUtils.isNotEmpty(sal.getSubAccountNumber())) {
381: pad.setSubAccountNbr(sal.getSubAccountNumber());
382: } else {
383: pad.setSubAccountNbr("-----");
384: }
385: pad.setFinObjectCode(sal.getFinancialObjectCode());
386: if (StringUtils.isNotEmpty(sal.getFinancialSubObjectCode())) {
387: pad
388: .setFinSubObjectCode(sal
389: .getFinancialSubObjectCode());
390: } else {
391: pad.setFinSubObjectCode("---");
392: }
393: if (StringUtils
394: .isNotEmpty(sal.getOrganizationReferenceId())) {
395: pad.setOrgReferenceId(sal.getOrganizationReferenceId());
396: }
397: if (StringUtils.isNotEmpty(sal.getProjectCode())) {
398: pad.setProjectCode(sal.getProjectCode());
399: } else {
400: pad.setProjectCode("----------");
401: }
402: pad.setAccountNetAmount(sal.getAmount().bigDecimalValue());
403: pd.addAccountDetail(pad);
404: }
405:
406: // Handle notes
407: DisbursementVoucherPayeeDetail dvpd = document
408: .getDvPayeeDetail();
409:
410: int line = 0;
411: PaymentNoteText pnt = new PaymentNoteText();
412: pnt.setCustomerNoteLineNbr(line++);
413: pnt.setCustomerNoteText("Info: "
414: + document.getDisbVchrContactPersonName() + " "
415: + document.getDisbVchrContactPhoneNumber());
416: pd.addNote(pnt);
417:
418: String dvRemitPersonName = null;
419: String dvRemitLine1Address = null;
420: String dvRemitLine2Address = null;
421: String dvRemitCity = null;
422: String dvRemitState = null;
423: String dvRemitZip = null;
424:
425: if (dvpd.isPayee()) {
426: Payee payee = disbursementVoucherDao.getPayee(dvpd
427: .getDisbVchrPayeeIdNumber());
428: dvRemitPersonName = payee.getPayeePersonName();
429: dvRemitLine1Address = payee.getPayeeLine1Addr();
430: dvRemitLine2Address = payee.getPayeeLine2Addr();
431: dvRemitCity = payee.getPayeeCityName();
432: dvRemitState = payee.getPayeeStateCode();
433: dvRemitZip = payee.getPayeeZipCode();
434: }
435:
436: if (StringUtils.isNotEmpty(dvRemitPersonName)) {
437: pnt = new PaymentNoteText();
438: pnt.setCustomerNoteLineNbr(line++);
439: pnt.setCustomerNoteText("Send Check To: "
440: + dvRemitPersonName);
441: pd.addNote(pnt);
442: }
443: if (StringUtils.isNotEmpty(dvRemitLine1Address)) {
444: pnt = new PaymentNoteText();
445: pnt.setCustomerNoteLineNbr(line++);
446: pnt.setCustomerNoteText(dvRemitLine1Address);
447: pd.addNote(pnt);
448: }
449: if (StringUtils.isNotEmpty(dvRemitLine2Address)) {
450: pnt = new PaymentNoteText();
451: pnt.setCustomerNoteLineNbr(line++);
452: pnt.setCustomerNoteText(dvRemitLine2Address);
453: pd.addNote(pnt);
454: }
455: if (StringUtils.isNotEmpty(dvRemitCity)) {
456: pnt = new PaymentNoteText();
457: pnt.setCustomerNoteLineNbr(line++);
458: pnt.setCustomerNoteText(dvRemitCity + ", " + dvRemitState
459: + " " + dvRemitZip);
460: pd.addNote(pnt);
461: }
462: if (document.isDisbVchrAttachmentCode()) {
463: pnt = new PaymentNoteText();
464: pnt.setCustomerNoteLineNbr(line++);
465: pnt.setCustomerNoteText("Attachment Included");
466: pd.addNote(pnt);
467: }
468:
469: String paymentReasonCode = dvpd.getDisbVchrPaymentReasonCode();
470: if (DisbursementVoucherRuleConstants.PaymentReasonCodes.TRAVEL_NONEMPLOYEE
471: .equals(paymentReasonCode)
472: || DisbursementVoucherRuleConstants.PaymentReasonCodes.TRAVEL_HONORARIUM
473: .equals(paymentReasonCode)) {
474: DisbursementVoucherNonEmployeeTravel dvnet = document
475: .getDvNonEmployeeTravel();
476: pnt = new PaymentNoteText();
477: pnt.setCustomerNoteLineNbr(line++);
478: pnt.setCustomerNoteText("Reimbursement associated with "
479: + dvnet.getDisbVchrServicePerformedDesc());
480: pd.addNote(pnt);
481:
482: pnt = new PaymentNoteText();
483: pnt.setCustomerNoteLineNbr(line++);
484: pnt
485: .setCustomerNoteText("The total per diem amount for your daily expenses is "
486: + dvnet.getDisbVchrPerdiemCalculatedAmt());
487: pd.addNote(pnt);
488:
489: if (dvnet.getDisbVchrPersonalCarAmount() != null
490: && dvnet.getDisbVchrPersonalCarAmount().compareTo(
491: KualiDecimal.ZERO) != 0) {
492: pnt = new PaymentNoteText();
493: pnt.setCustomerNoteLineNbr(line++);
494: pnt
495: .setCustomerNoteText("The total dollar amount for your vehicle mileage is "
496: + dvnet.getDisbVchrPersonalCarAmount());
497: pd.addNote(pnt);
498:
499: for (Iterator iter = dvnet.getDvNonEmployeeExpenses()
500: .iterator(); iter.hasNext();) {
501: DisbursementVoucherNonEmployeeExpense exp = (DisbursementVoucherNonEmployeeExpense) iter
502: .next();
503:
504: if (line < (maxNoteLines - 8)) {
505: pnt = new PaymentNoteText();
506: pnt.setCustomerNoteLineNbr(line++);
507: pnt.setCustomerNoteText(exp
508: .getDisbVchrExpenseCompanyName()
509: + " " + exp.getDisbVchrExpenseAmount());
510: pd.addNote(pnt);
511: }
512: }
513: }
514: } else if (DisbursementVoucherRuleConstants.PaymentReasonCodes.TRAVEL_PREPAID
515: .equals(paymentReasonCode)) {
516: pnt = new PaymentNoteText();
517: pnt.setCustomerNoteLineNbr(line++);
518: pnt
519: .setCustomerNoteText("Payment is for the following indviuals/charges:");
520: pd.addNote(pnt);
521:
522: DisbursementVoucherPreConferenceDetail dvpcd = document
523: .getDvPreConferenceDetail();
524:
525: for (Iterator iter = dvpcd.getDvPreConferenceRegistrants()
526: .iterator(); iter.hasNext();) {
527: DisbursementVoucherPreConferenceRegistrant dvpcr = (DisbursementVoucherPreConferenceRegistrant) iter
528: .next();
529:
530: if (line < (maxNoteLines - 8)) {
531: pnt = new PaymentNoteText();
532: pnt.setCustomerNoteLineNbr(line++);
533: pnt.setCustomerNoteText(dvpcr
534: .getDvConferenceRegistrantName()
535: + " " + dvpcr.getDisbVchrExpenseAmount());
536: pd.addNote(pnt);
537: }
538: }
539: }
540:
541: String text = document.getDisbVchrCheckStubText();
542: if (text.length() > 0) {
543: String[] lines = text.split("\\n");
544: for (int i = 0; i < lines.length; i++) {
545: if (line < (maxNoteLines - 3)) {
546: pnt = new PaymentNoteText();
547: pnt.setCustomerNoteLineNbr(line++);
548: if (lines[i].length() > 90) {
549: pnt.setCustomerNoteText(lines[i].substring(0,
550: 90));
551: } else {
552: pnt.setCustomerNoteText(lines[i]);
553: }
554: pd.addNote(pnt);
555: }
556: }
557: }
558: return pd;
559: }
560:
561: /**
562: *
563: * This method creates a Batch instance and populates it with the information provided.
564: *
565: * @param campusCode The campus code used to retrieve a customer profile to be set on the batch.
566: * @param user The user who submitted the batch.
567: * @param processRunDate The date the batch was submitted and the date the customer profile was generated.
568: * @return A fully populated batch instance.
569: */
570: private Batch createBatch(String campusCode, UniversalUser user,
571: Date processRunDate) {
572: String orgCode = parameterService
573: .getParameterValue(
574: DisbursementVoucherDocument.class,
575: DisbursementVoucherRuleConstants.DvPdpExtractGroup.DV_PDP_ORG_CODE);
576: String subUnitCode = parameterService
577: .getParameterValue(
578: DisbursementVoucherDocument.class,
579: DisbursementVoucherRuleConstants.DvPdpExtractGroup.DV_PDP_SBUNT_CODE);
580: CustomerProfile customer = customerProfileService.get(
581: campusCode, orgCode, subUnitCode);
582: if (customer == null) {
583: throw new IllegalArgumentException(
584: "Unable to find customer profile for " + campusCode
585: + "/" + orgCode + "/" + subUnitCode);
586: }
587:
588: // Create the group for this campus
589: Batch batch = new Batch();
590: batch.setCustomerProfile(customer);
591: batch.setCustomerFileCreateTimestamp(new Timestamp(
592: processRunDate.getTime()));
593: batch.setFileProcessTimestamp(new Timestamp(processRunDate
594: .getTime()));
595: batch.setPaymentFileName("extr_fr_disb_voucher");
596: batch.setSubmiterUser(new PdpUser(user));
597:
598: // Set these for now, we will update them later
599: batch.setPaymentCount(0);
600: batch.setPaymentTotalAmount(new BigDecimal("0"));
601: paymentFileService.saveBatch(batch);
602:
603: return batch;
604: }
605:
606: /**
607: *
608: * This method retrieves a collection of campus instances representing all the campuses which currently have
609: * disbursement vouchers with the status code provided.
610: *
611: * @param statusCode The status code to retrieve disbursement vouchers by.
612: * @return A collection of campus codes of all the campuses with disbursement vouchers in the status given.
613: */
614: private Set<String> getCampusListByDocumentStatusCode(
615: String statusCode) {
616: LOG.debug("getCampusListByDocumentStatusCode() started");
617:
618: Set<String> campusSet = new HashSet<String>();
619:
620: Collection docs = disbursementVoucherDao
621: .getDocumentsByHeaderStatus(statusCode);
622: for (Iterator iter = docs.iterator(); iter.hasNext();) {
623: DisbursementVoucherDocument element = (DisbursementVoucherDocument) iter
624: .next();
625:
626: String dvdCampusCode = element.getCampusCode();
627: DisbursementVoucherPayeeDetail dvpd = element
628: .getDvPayeeDetail();
629: if (dvpd != null) {
630: List<String> campusCodes = parameterService
631: .getParameterValues(
632: DisbursementVoucherDocument.class,
633: CAMPUS_BY_PAYMENT_REASON_PARAM, dvpd
634: .getDisbVchrPaymentReasonCode());
635: if (campusCodes.size() > 0
636: && StringUtils.isNotBlank(campusCodes.get(0))) {
637: dvdCampusCode = campusCodes.get(0);
638: }
639: campusSet.add(dvdCampusCode);
640: }
641: }
642: return campusSet;
643: }
644:
645: /**
646: *
647: * This method retrieves a list of disbursement voucher documents that are in the status provided for the campus code given.
648: *
649: * @param statusCode The status of the disbursement vouchers to be retrieved.
650: * @param campusCode The campus code that the disbursement vouchers will be associated with.
651: * @return A collection of disbursement voucher objects that meet the search criteria given.
652: */
653: private Collection<DisbursementVoucherDocument> getListByDocumentStatusCodeCampus(
654: String statusCode, String campusCode) {
655: LOG.debug("getListByDocumentStatusCodeCampus() started");
656:
657: Collection<DisbursementVoucherDocument> list = new ArrayList<DisbursementVoucherDocument>();
658:
659: try {
660: Collection docs = SpringContext.getBean(
661: DocumentService.class)
662: .findByDocumentHeaderStatusCode(
663: DisbursementVoucherDocument.class,
664: statusCode);
665: for (Iterator iter = docs.iterator(); iter.hasNext();) {
666: DisbursementVoucherDocument element = (DisbursementVoucherDocument) iter
667: .next();
668:
669: String dvdCampusCode = element.getCampusCode();
670:
671: DisbursementVoucherPayeeDetail dvpd = element
672: .getDvPayeeDetail();
673: if (dvpd != null) {
674: List<String> campusCodes = parameterService
675: .getParameterValues(
676: DisbursementVoucherDocument.class,
677: CAMPUS_BY_PAYMENT_REASON_PARAM,
678: dvpd.getDisbVchrPaymentReasonCode());
679: if (campusCodes.size() > 0
680: && StringUtils.isNotBlank(campusCodes
681: .get(0))) {
682: dvdCampusCode = campusCodes.get(0);
683: }
684: }
685:
686: if (dvdCampusCode.equals(campusCode)) {
687: list.add(element);
688: }
689: }
690: } catch (WorkflowException we) {
691: LOG
692: .error("Could not load Disbursement Voucher Documents with status code = "
693: + statusCode + ": " + we);
694: throw new RuntimeException(we);
695: }
696: return list;
697: }
698:
699: /**
700: * This cancels the disbursement voucher
701: * @param dv the disbursement voucher document to cancel
702: * @param processDate the date of the cancelation
703: * @see org.kuali.module.financial.service.DisbursementVoucherExtractService#cancelExtractedDisbursementVoucher(org.kuali.module.financial.document.DisbursementVoucherDocument)
704: */
705: public void cancelExtractedDisbursementVoucher(
706: DisbursementVoucherDocument dv, java.sql.Date processDate) {
707: if (dv.getCancelDate() == null) {
708: try {
709: BusinessObjectService boService = SpringContext
710: .getBean(BusinessObjectService.class);
711: // set the canceled date
712: dv.setCancelDate(processDate);
713: dv
714: .refreshReferenceObject("generalLedgerPendingEntries");
715: if (ObjectUtils.isNull(dv
716: .getGeneralLedgerPendingEntries())
717: || dv.getGeneralLedgerPendingEntries().size() == 0) {
718: // generate all the pending entries for the document
719: SpringContext.getBean(
720: GeneralLedgerPendingEntryService.class)
721: .generateGeneralLedgerPendingEntries(dv);
722: // for each pending entry, opposite-ify it and reattach it to the document
723: GeneralLedgerPendingEntrySequenceHelper glpeSeqHelper = new GeneralLedgerPendingEntrySequenceHelper();
724: for (GeneralLedgerPendingEntry glpe : dv
725: .getGeneralLedgerPendingEntries()) {
726: oppositifyEntry(glpe, boService, glpeSeqHelper);
727: }
728: } else {
729: List<GeneralLedgerPendingEntry> newGLPEs = new ArrayList<GeneralLedgerPendingEntry>();
730: GeneralLedgerPendingEntrySequenceHelper glpeSeqHelper = new GeneralLedgerPendingEntrySequenceHelper(
731: dv.getGeneralLedgerPendingEntries().size() + 1);
732: for (GeneralLedgerPendingEntry glpe : dv
733: .getGeneralLedgerPendingEntries()) {
734: glpe.refresh();
735: if (glpe
736: .getFinancialDocumentApprovedCode()
737: .equals(
738: KFSConstants.PENDING_ENTRY_APPROVED_STATUS_CODE.PROCESSED)) {
739: // damn! it got processed! well, make a copy, oppositify, and save
740: GeneralLedgerPendingEntry undoer = new GeneralLedgerPendingEntry(
741: glpe);
742: oppositifyEntry(undoer, boService,
743: glpeSeqHelper);
744: newGLPEs.add(undoer);
745: } else {
746: // just delete the GLPE before anything happens to it
747: boService.delete(glpe);
748: }
749: }
750: dv.setGeneralLedgerPendingEntries(newGLPEs);
751: }
752: // set the financial document status to canceled
753: dv.getDocumentHeader().setFinancialDocumentStatusCode(
754: KFSConstants.DocumentStatusCodes.CANCELLED);
755: // save the document
756: SpringContext
757: .getBean(DocumentService.class)
758: .saveDocument(
759: dv,
760: AccountingDocumentSaveWithNoLedgerEntryGenerationEvent.class);
761: } catch (WorkflowException we) {
762: LOG
763: .error("encountered workflow exception while attempting to save Disbursement Voucher: "
764: + dv.getDocumentNumber() + " " + we);
765: throw new RuntimeException(we);
766: }
767: }
768: }
769:
770: /**
771: * Updates the given general ledger pending entry so that it will have the opposite effect of what it was created to do; this, in effect,
772: * undoes the entries that were already posted for this document
773: * @param glpe the general ledger pending entry to undo
774: */
775: private void oppositifyEntry(GeneralLedgerPendingEntry glpe,
776: BusinessObjectService boService,
777: GeneralLedgerPendingEntrySequenceHelper glpeSeqHelper) {
778: if (glpe.getTransactionDebitCreditCode().equals(
779: KFSConstants.GL_CREDIT_CODE)) {
780: glpe
781: .setTransactionDebitCreditCode(KFSConstants.GL_DEBIT_CODE);
782: } else if (glpe.getTransactionDebitCreditCode().equals(
783: KFSConstants.GL_DEBIT_CODE)) {
784: glpe
785: .setTransactionDebitCreditCode(KFSConstants.GL_CREDIT_CODE);
786: }
787: glpe.setTransactionLedgerEntrySequenceNumber(glpeSeqHelper
788: .getSequenceCounter());
789: glpeSeqHelper.increment();
790: glpe
791: .setFinancialDocumentApprovedCode(KFSConstants.PENDING_ENTRY_APPROVED_STATUS_CODE.APPROVED);
792: boService.save(glpe);
793: }
794:
795: /**
796: * This updates the disbursement voucher so that when it is re-extracted, information about it will be accurate
797: * @param dv the disbursement voucher document to reset
798: * @param processDate the date of the reseting
799: * @see org.kuali.module.financial.service.DisbursementVoucherExtractService#resetExtractedDisbursementVoucher(org.kuali.module.financial.document.DisbursementVoucherDocument)
800: */
801: public void resetExtractedDisbursementVoucher(
802: DisbursementVoucherDocument dv, java.sql.Date processDate) {
803: try {
804: // 1. reset the extracted date
805: dv.setExtractDate(null);
806: dv.setPaidDate(null);
807: // 2. save the doc
808: SpringContext
809: .getBean(DocumentService.class)
810: .saveDocument(
811: dv,
812: AccountingDocumentSaveWithNoLedgerEntryGenerationEvent.class);
813: } catch (WorkflowException we) {
814: LOG
815: .error("encountered workflow exception while attempting to save Disbursement Voucher: "
816: + dv.getDocumentNumber() + " " + we);
817: throw new RuntimeException(we);
818: }
819: }
820:
821: /**
822: * Looks up the document using document service, and deals with any nasty WorkflowException or ClassCastExceptions that pop up
823: * @param documentNumber the number of the document to look up
824: * @return the dv doc if found, or null otherwise
825: * @see org.kuali.module.financial.service.DisbursementVoucherExtractService#getDocumentById(java.lang.String)
826: */
827: public DisbursementVoucherDocument getDocumentById(
828: String documentNumber) {
829: DisbursementVoucherDocument dv = null;
830: try {
831: dv = (DisbursementVoucherDocument) SpringContext.getBean(
832: DocumentService.class).getByDocumentHeaderId(
833: documentNumber);
834: } catch (WorkflowException we) {
835: LOG
836: .error("encountered workflow exception while attempting to retrieve Disbursement Voucher: "
837: + dv.getDocumentNumber() + " " + we);
838: throw new RuntimeException(we);
839: }
840: return dv;
841: }
842:
843: /**
844: * Marks the disbursement voucher as paid by setting its paid date
845: * @param dv the dv document to mark as paid
846: * @param processDate the date when the dv was paid
847: * @see org.kuali.module.financial.service.DisbursementVoucherExtractService#markDisbursementVoucherAsPaid(org.kuali.module.financial.document.DisbursementVoucherDocument)
848: */
849: public void markDisbursementVoucherAsPaid(
850: DisbursementVoucherDocument dv, java.sql.Date processDate) {
851: try {
852: dv.setPaidDate(processDate);
853: SpringContext
854: .getBean(DocumentService.class)
855: .saveDocument(
856: dv,
857: AccountingDocumentSaveWithNoLedgerEntryGenerationEvent.class);
858: } catch (WorkflowException we) {
859: LOG
860: .error("encountered workflow exception while attempting to save Disbursement Voucher: "
861: + dv.getDocumentNumber() + " " + we);
862: throw new RuntimeException(we);
863: }
864: }
865:
866: /**
867: *
868: * This method sets the disbursementVoucherDao instance.
869: * @param disbursementVoucherDao The DisbursementVoucherDao to be set.
870: */
871: public void setDisbursementVoucherDao(
872: DisbursementVoucherDao disbursementVoucherDao) {
873: this .disbursementVoucherDao = disbursementVoucherDao;
874: }
875:
876: /**
877: *
878: * This method sets the ParameterService instance.
879: * @param parameterService The ParameterService to be set.
880: */
881: public void setParameterService(ParameterService parameterService) {
882: this .parameterService = parameterService;
883: }
884:
885: /**
886: *
887: * This method sets the dateTimeService instance.
888: * @param dateTimeService The DateTimeService to be set.
889: */
890: public void setDateTimeService(DateTimeService dateTimeService) {
891: this .dateTimeService = dateTimeService;
892: }
893:
894: /**
895: *
896: * This method sets the universalUserService instance.
897: * @param universalUserService The UniversalUserService to be set.
898: */
899: public void setUniversalUserService(
900: UniversalUserService universalUserService) {
901: this .universalUserService = universalUserService;
902: }
903:
904: /**
905: *
906: * This method sets the customerProfileService instance.
907: * @param customerProfileService The CustomerProfileService to be set.
908: */
909: public void setCustomerProfileService(
910: CustomerProfileService customerProfileService) {
911: this .customerProfileService = customerProfileService;
912: }
913:
914: /**
915: *
916: * This method sets the paymentFileService instance.
917: * @param paymentFileService The PaymentFileService to be set.
918: */
919: public void setPaymentFileService(
920: PaymentFileService paymentFileService) {
921: this .paymentFileService = paymentFileService;
922: }
923:
924: /**
925: *
926: * This method sets the paymentGroupService instance.
927: * @param paymentGroupService The PaymentGroupService to be set.
928: */
929: public void setPaymentGroupService(
930: PaymentGroupService paymentGroupService) {
931: this .paymentGroupService = paymentGroupService;
932: }
933:
934: /**
935: *
936: * This method sets the referenceService instance.
937: * @param rs The ReferenceService to be set.
938: */
939: public void setReferenceService(ReferenceService rs) {
940: this.referenceService = rs;
941: }
942: }
|