View Javadoc
1   /*
2    * Copyright 2007 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.ole.pdp.service.impl;
17  
18  import java.sql.Timestamp;
19  import java.util.ArrayList;
20  import java.util.Calendar;
21  import java.util.Date;
22  import java.util.HashMap;
23  import java.util.Iterator;
24  import java.util.List;
25  import java.util.Map;
26  
27  import org.apache.commons.lang.StringUtils;
28  import org.kuali.ole.pdp.PdpConstants;
29  import org.kuali.ole.pdp.PdpKeyConstants;
30  import org.kuali.ole.pdp.PdpParameterConstants;
31  import org.kuali.ole.pdp.PdpPropertyConstants;
32  import org.kuali.ole.pdp.batch.service.ExtractPaymentService;
33  import org.kuali.ole.pdp.businessobject.AchAccountNumber;
34  import org.kuali.ole.pdp.businessobject.CustomerBank;
35  import org.kuali.ole.pdp.businessobject.CustomerProfile;
36  import org.kuali.ole.pdp.businessobject.DisbursementNumberRange;
37  import org.kuali.ole.pdp.businessobject.DisbursementType;
38  import org.kuali.ole.pdp.businessobject.FormatProcess;
39  import org.kuali.ole.pdp.businessobject.FormatProcessSummary;
40  import org.kuali.ole.pdp.businessobject.FormatSelection;
41  import org.kuali.ole.pdp.businessobject.PayeeACHAccount;
42  import org.kuali.ole.pdp.businessobject.PaymentChangeCode;
43  import org.kuali.ole.pdp.businessobject.PaymentDetail;
44  import org.kuali.ole.pdp.businessobject.PaymentGroup;
45  import org.kuali.ole.pdp.businessobject.PaymentGroupHistory;
46  import org.kuali.ole.pdp.businessobject.PaymentProcess;
47  import org.kuali.ole.pdp.businessobject.PaymentStatus;
48  import org.kuali.ole.pdp.dataaccess.FormatPaymentDao;
49  import org.kuali.ole.pdp.dataaccess.PaymentDetailDao;
50  import org.kuali.ole.pdp.dataaccess.PaymentGroupDao;
51  import org.kuali.ole.pdp.dataaccess.ProcessDao;
52  import org.kuali.ole.pdp.service.AchService;
53  import org.kuali.ole.pdp.service.FormatService;
54  import org.kuali.ole.pdp.service.PaymentGroupService;
55  import org.kuali.ole.pdp.service.PendingTransactionService;
56  import org.kuali.ole.pdp.service.impl.exception.FormatException;
57  import org.kuali.ole.sys.DynamicCollectionComparator;
58  import org.kuali.ole.sys.OLEConstants;
59  import org.kuali.ole.sys.OLEPropertyConstants;
60  import org.kuali.ole.sys.batch.service.SchedulerService;
61  import org.kuali.ole.sys.businessobject.Bank;
62  import org.kuali.ole.sys.context.SpringContext;
63  import org.kuali.ole.sys.service.impl.OleParameterConstants;
64  import org.kuali.rice.core.api.datetime.DateTimeService;
65  import org.kuali.rice.core.api.util.type.KualiInteger;
66  import org.kuali.rice.coreservice.framework.parameter.ParameterService;
67  import org.kuali.rice.kim.api.identity.Person;
68  import org.kuali.rice.kim.api.identity.PersonService;
69  import org.kuali.rice.krad.service.BusinessObjectService;
70  import org.kuali.rice.krad.util.GlobalVariables;
71  import org.kuali.rice.krad.util.ObjectUtils;
72  import org.springframework.transaction.annotation.Transactional;
73  
74  @Transactional
75  public class FormatServiceImpl implements FormatService {
76      private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(FormatServiceImpl.class);
77  
78      protected PaymentDetailDao paymentDetailDao;
79      protected PaymentGroupDao paymentGroupDao;
80      protected ProcessDao processDao;
81      protected AchService achService;
82      protected PendingTransactionService glPendingTransactionService;
83      protected ParameterService parameterService;
84      protected FormatPaymentDao formatPaymentDao;
85      protected SchedulerService schedulerService;
86      protected BusinessObjectService businessObjectService;
87      protected PaymentGroupService paymentGroupService;
88      protected DateTimeService dateTimeService;
89      protected ExtractPaymentService extractPaymentService;
90      protected PersonService personService;
91  
92      /**
93       * Constructs a FormatServiceImpl.java.
94       */
95      public FormatServiceImpl() {
96          super();
97      }
98  
99      /**
100      * @see org.kuali.ole.pdp.service.FormatProcessService#getDataForFormat(org.kuali.rice.kim.api.identity.Person)
101      */
102     @Override
103     public FormatSelection getDataForFormat(Person user) {
104 
105         String campusCode = user.getCampusCode();
106         Date formatStartDate = getFormatProcessStartDate(campusCode);
107 
108         // create new FormatSelection object an set the campus code and the start date
109         FormatSelection formatSelection = new FormatSelection();
110         formatSelection.setCampus(campusCode);
111         formatSelection.setStartDate(formatStartDate);
112 
113         // if format process not started yet, populate the other data as well
114         if (formatStartDate == null) {
115             formatSelection.setCustomerList(getAllCustomerProfiles());
116             formatSelection.setRangeList(getAllDisbursementNumberRanges());
117         }
118 
119         return formatSelection;
120     }
121 
122     /**
123      * @see org.kuali.ole.pdp.service.FormatService#getFormatProcessStartDate(java.lang.String)
124      */
125     @Override
126     @SuppressWarnings("rawtypes")
127     public Date getFormatProcessStartDate(String campus) {
128         LOG.debug("getFormatProcessStartDate() started");
129 
130         Map primaryKeys = new HashMap();
131         primaryKeys.put(PdpPropertyConstants.PHYS_CAMPUS_PROCESS_CODE, campus);
132         FormatProcess formatProcess = this.businessObjectService.findByPrimaryKey(FormatProcess.class, primaryKeys);
133 
134         if (formatProcess != null) {
135             LOG.debug("getFormatProcessStartDate() found");
136             return new Date(formatProcess.getBeginFormat().getTime());
137         }
138         else {
139             LOG.debug("getFormatProcessStartDate() not found");
140             return null;
141         }
142     }
143 
144     /**
145      * @see org.kuali.ole.pdp.service.FormatService#startFormatProcess(org.kuali.rice.kim.api.identity.Person, java.lang.String,
146      *      java.util.List, java.util.Date, java.lang.String)
147      */
148     @Override
149     public FormatProcessSummary startFormatProcess(Person user, String campus, List<CustomerProfile> customers, Date paydate, String paymentTypes) {
150         LOG.debug("startFormatProcess() started");
151 
152         for (CustomerProfile element : customers) {
153             if (LOG.isDebugEnabled()) {
154                 LOG.debug("startFormatProcess() Customer: " + element);
155             }
156         }
157 
158         // Create the process
159         Date d = new Date();
160         PaymentProcess paymentProcess = new PaymentProcess();
161         paymentProcess.setCampusCode(campus);
162         paymentProcess.setProcessUser(user);
163         paymentProcess.setProcessTimestamp(new Timestamp(d.getTime()));
164 
165         this.businessObjectService.save(paymentProcess);
166 
167         // add an entry in the format process table (to lock the format process)
168         FormatProcess formatProcess = new FormatProcess();
169 
170         formatProcess.setPhysicalCampusProcessCode(campus);
171         formatProcess.setBeginFormat(dateTimeService.getCurrentTimestamp());
172         formatProcess.setPaymentProcIdentifier(paymentProcess.getId().intValue());
173 
174         this.businessObjectService.save(formatProcess);
175 
176 
177         Timestamp now = new Timestamp((new Date()).getTime());
178         java.sql.Date sqlDate = new java.sql.Date(paydate.getTime());
179         Calendar c = Calendar.getInstance();
180         c.setTime(sqlDate);
181         c.set(Calendar.HOUR, 11);
182         c.set(Calendar.MINUTE, 59);
183         c.set(Calendar.SECOND, 59);
184         c.set(Calendar.MILLISECOND, 59);
185         c.set(Calendar.AM_PM, Calendar.PM);
186         Timestamp paydateTs = new Timestamp(c.getTime().getTime());
187 
188         if (LOG.isDebugEnabled()) {
189             LOG.debug("startFormatProcess() last update = " + now);
190             LOG.debug("startFormatProcess() entered paydate = " + paydate);
191             LOG.debug("startFormatProcess() actual paydate = " + paydateTs);
192         }
193         PaymentStatus format = this.businessObjectService.findBySinglePrimaryKey(PaymentStatus.class, PdpConstants.PaymentStatusCodes.FORMAT);
194 
195         List customerIds = new ArrayList();
196         for (Iterator iter = customers.iterator(); iter.hasNext();) {
197             CustomerProfile element = (CustomerProfile) iter.next();
198             customerIds.add(element.getId());
199         }
200 
201         // Mark all of them ready for format
202         Iterator groupIterator = formatPaymentDao.markPaymentsForFormat(customerIds, paydateTs, paymentTypes);
203 
204         while (groupIterator.hasNext()) {
205             PaymentGroup paymentGroup = (PaymentGroup) groupIterator.next();
206             paymentGroup.setLastUpdate(paydateTs);// delete this one
207             paymentGroup.setPaymentStatus(format);
208             paymentGroup.setProcess(paymentProcess);
209             businessObjectService.save(paymentGroup);
210         }
211 
212 
213         // summarize them
214         FormatProcessSummary preFormatProcessSummary = new FormatProcessSummary();
215         Iterator<PaymentGroup> iterator = this.paymentGroupService.getByProcess(paymentProcess);
216 
217         while (iterator.hasNext()) {
218             PaymentGroup paymentGroup = iterator.next();
219             preFormatProcessSummary.add(paymentGroup);
220         }
221 
222         // if no payments found for format clear the format process
223         if (preFormatProcessSummary.getProcessSummaryList().size() == 0) {
224             LOG.debug("startFormatProcess() No payments to process.  Format process ending");
225             clearUnfinishedFormat(paymentProcess.getId().intValue());// ?? maybe call end format process
226         }
227 
228         return preFormatProcessSummary;
229     }
230 
231 
232     /**
233      * This method gets the maximum number of lines in a note.
234      *
235      * @return the maximum number of lines in a note
236      */
237     protected int getMaxNoteLines() {
238         String maxLines = parameterService.getParameterValueAsString(OleParameterConstants.PRE_DISBURSEMENT_ALL.class, PdpParameterConstants.MAX_NOTE_LINES);
239         if (StringUtils.isBlank(maxLines)) {
240             throw new RuntimeException("System parameter for max note lines is blank");
241         }
242 
243         return Integer.parseInt(maxLines);
244     }
245 
246     /**
247      * @see org.kuali.ole.pdp.service.FormatService#performFormat(java.lang.Integer)
248      */
249     @Override
250     public void performFormat(Integer processId) throws FormatException {
251         LOG.debug("performFormat() started");
252 
253         // get the PaymentProcess for the given id
254         @SuppressWarnings("rawtypes")
255         Map primaryKeys = new HashMap();
256         primaryKeys.put(PdpPropertyConstants.PaymentProcess.PAYMENT_PROCESS_ID, processId);
257         PaymentProcess paymentProcess = this.businessObjectService.findByPrimaryKey(PaymentProcess.class, primaryKeys);
258         if (paymentProcess == null) {
259             LOG.error("performFormat() Invalid proc ID " + processId);
260             throw new RuntimeException("Invalid proc ID");
261         }
262 
263         String processCampus = paymentProcess.getCampusCode();
264         FormatProcessSummary postFormatProcessSummary = new FormatProcessSummary();
265 
266         // step 1 get ACH or Check, Bank info, ACH info, sorting
267         Iterator<PaymentGroup> paymentGroupIterator = this.paymentGroupService.getByProcess(paymentProcess);
268         while (paymentGroupIterator.hasNext()) {
269             PaymentGroup paymentGroup = paymentGroupIterator.next();
270             if (LOG.isDebugEnabled()) {
271                 LOG.debug("performFormat() Step 1 Payment Group ID " + paymentGroup.getId());
272             }
273 
274             // process payment group data
275             boolean groupProcessed = processPaymentGroup(paymentGroup, paymentProcess);
276             if (!groupProcessed) {
277                 throw new FormatException("Error encountered during format");
278             }
279 
280             // save payment group
281             this.businessObjectService.save(paymentGroup);
282 
283             // Add to summary information
284             postFormatProcessSummary.add(paymentGroup);
285         }
286 
287         // step 2 assign disbursement numbers and combine checks into one if possible
288         boolean disbursementNumbersAssigned = assignDisbursementNumbersAndCombineChecks(paymentProcess, postFormatProcessSummary);
289         if (!disbursementNumbersAssigned) {
290             throw new FormatException("Error encountered during format");
291         }
292 
293         // step 3 save the summarizing info
294         LOG.debug("performFormat() Save summarizing information");
295         postFormatProcessSummary.save();
296 
297         // step 4 set formatted indicator to true and save in the db
298         paymentProcess.setFormattedIndicator(true);
299         businessObjectService.save(paymentProcess);
300 
301         // step 5 end the format process for this campus
302         LOG.debug("performFormat() End the format process for this campus");
303         endFormatProcess(processCampus);
304 
305         // step 6 tell the extract batch job to start
306         LOG.debug("performFormat() Start extract");
307         extractChecks();
308     }
309 
310     /**
311      * This method processes the payment group data.
312      *
313      * @param paymentGroup
314      * @param paymentProcess
315      */
316     protected boolean processPaymentGroup(PaymentGroup paymentGroup, PaymentProcess paymentProcess) {
317         boolean successful = true;
318 
319         paymentGroup.setSortValue(paymentGroupService.getSortGroupId(paymentGroup));
320         paymentGroup.setPhysCampusProcessCd(paymentProcess.getCampusCode());
321         paymentGroup.setProcess(paymentProcess);
322 
323         // If any one of the payment details in the group are negative, we always force a check
324         boolean noNegativeDetails = true;
325 
326         // If any one of the payment details in the group are negative, we always force a check
327         List<PaymentDetail> paymentDetailsList = paymentGroup.getPaymentDetails();
328         for (PaymentDetail paymentDetail : paymentDetailsList) {
329             if (paymentDetail.getNetPaymentAmount().doubleValue() < 0) {
330                 if (LOG.isDebugEnabled()) {
331                     LOG.debug("performFormat() Payment Group " + paymentGroup + " has payment detail net payment amount " + paymentDetail.getNetPaymentAmount());
332                     LOG.debug("performFormat() Forcing a Check for Group");
333                 }
334                 noNegativeDetails = false;
335                 break;
336             }
337         }
338 
339         // determine whether payment should be ACH or Check
340         CustomerProfile customer = paymentGroup.getBatch().getCustomerProfile();
341 
342         PayeeACHAccount payeeAchAccount = null;
343         boolean isCheck = true;
344         if (PdpConstants.PayeeIdTypeCodes.VENDOR_ID.equals(paymentGroup.getPayeeIdTypeCd()) || PdpConstants.PayeeIdTypeCodes.EMPLOYEE.equals(paymentGroup.getPayeeIdTypeCd()) || PdpConstants.PayeeIdTypeCodes.ENTITY.equals(paymentGroup.getPayeeIdTypeCd())) {
345             if (StringUtils.isNotBlank(paymentGroup.getPayeeId()) && !paymentGroup.getPymtAttachment() && !paymentGroup.getProcessImmediate() && !paymentGroup.getPymtSpecialHandling() && (customer.getAchTransactionType() != null) && noNegativeDetails) {
346                 LOG.debug("performFormat() Checking ACH");
347                 payeeAchAccount = achService.getAchInformation(paymentGroup.getPayeeIdTypeCd(), paymentGroup.getPayeeId(), customer.getAchTransactionType());
348                 isCheck = (payeeAchAccount == null);
349             }
350         }
351 
352         DisbursementType disbursementType = null;
353         if (isCheck) {
354             PaymentStatus paymentStatus = businessObjectService.findBySinglePrimaryKey(PaymentStatus.class, PdpConstants.PaymentStatusCodes.PENDING_CHECK);
355             paymentGroup.setPaymentStatus(paymentStatus);
356 
357             disbursementType = businessObjectService.findBySinglePrimaryKey(DisbursementType.class, PdpConstants.DisbursementTypeCodes.CHECK);
358             paymentGroup.setDisbursementType(disbursementType);
359         }
360         else {
361             PaymentStatus paymentStatus = businessObjectService.findBySinglePrimaryKey(PaymentStatus.class, PdpConstants.PaymentStatusCodes.PENDING_ACH);
362             paymentGroup.setPaymentStatus(paymentStatus);
363 
364             disbursementType = businessObjectService.findBySinglePrimaryKey(DisbursementType.class, PdpConstants.DisbursementTypeCodes.ACH);
365             paymentGroup.setDisbursementType(disbursementType);
366 
367             paymentGroup.setAchBankRoutingNbr(payeeAchAccount.getBankRoutingNumber());
368             paymentGroup.setAdviceEmailAddress(payeeAchAccount.getPayeeEmailAddress());
369             paymentGroup.setAchAccountType(payeeAchAccount.getBankAccountTypeCode());
370 
371             AchAccountNumber achAccountNumber = new AchAccountNumber();
372             achAccountNumber.setAchBankAccountNbr(payeeAchAccount.getBankAccountNumber());
373             achAccountNumber.setId(paymentGroup.getId());
374             paymentGroup.setAchAccountNumber(achAccountNumber);
375         }
376 
377         // set payment group bank
378         successful &= validateAndUpdatePaymentGroupBankCode(paymentGroup, disbursementType, customer);
379 
380         return successful;
381     }
382 
383     /**
384      * Verifies a valid bank is set on the payment group. A bank is valid if it is active and supports the given disbursement type. If the payment group already has an
385      * assigned bank it will be used unless it is not valid. If the payment group bank is not valid or was not given the bank specified on the customer profile to use
386      * for the given disbursement type is used. If this bank is inactive then its continuation bank is used. If not valid bank to use is found an error is added to the
387      * global message map.
388      *
389      * @param paymentGroup group to set bank on
390      * @param disbursementType type of disbursement for given payment group
391      * @param customer customer profile for payment group
392      * @return boolean true if a valid bank is set on the payment group, false otherwise
393      */
394     protected boolean validateAndUpdatePaymentGroupBankCode(PaymentGroup paymentGroup, DisbursementType disbursementType, CustomerProfile customer) {
395         boolean bankValid = true;
396 
397         String originalBankCode = paymentGroup.getBankCode();
398         if (ObjectUtils.isNull(paymentGroup.getBank()) || ((disbursementType.getCode().equals(PdpConstants.DisbursementTypeCodes.ACH) && !paymentGroup.getBank().isBankAchIndicator()) || (disbursementType.getCode().equals(PdpConstants.DisbursementTypeCodes.CHECK) && !paymentGroup.getBank().isBankCheckIndicator())) || !paymentGroup.getBank().isActive()) {
399             CustomerBank customerBank = customer.getCustomerBankByDisbursementType(disbursementType.getCode());
400             if (ObjectUtils.isNotNull(customerBank) && customerBank.isActive() && ObjectUtils.isNotNull(customerBank.getBank()) && customerBank.getBank().isActive()) {
401                 paymentGroup.setBankCode(customerBank.getBankCode());
402                 paymentGroup.setBank(customerBank.getBank());
403             }
404             else if (ObjectUtils.isNotNull(customerBank) && ObjectUtils.isNotNull(customerBank.getBank()) && ObjectUtils.isNotNull(customerBank.getBank().getContinuationBank()) && customerBank.getBank().getContinuationBank().isActive()) {
405                 paymentGroup.setBankCode(customerBank.getBank().getContinuationBank().getBankCode());
406                 paymentGroup.setBank(customerBank.getBank().getContinuationBank());
407             }
408         }
409 
410         if (ObjectUtils.isNull(paymentGroup.getBank())) {
411             LOG.error("performFormat() A bank is needed for " + disbursementType.getName() + " disbursement type for customer: " + customer);
412             GlobalVariables.getMessageMap().putError(OLEConstants.GLOBAL_ERRORS, PdpKeyConstants.Format.ErrorMessages.ERROR_FORMAT_BANK_MISSING, customer.getCustomerShortName());
413             bankValid = false;
414 
415             return bankValid;
416         }
417 
418         // create payment history record if bank was changed
419         if (StringUtils.isNotBlank(originalBankCode) && !paymentGroup.getBankCode().equals(originalBankCode)) {
420             PaymentGroupHistory paymentGroupHistory = new PaymentGroupHistory();
421 
422             PaymentChangeCode paymentChangeCode = businessObjectService.findBySinglePrimaryKey(PaymentChangeCode.class, PdpConstants.PaymentChangeCodes.BANK_CHNG_CD);
423             paymentGroupHistory.setPaymentChange(paymentChangeCode);
424             paymentGroupHistory.setOrigBankCode(originalBankCode);
425 
426             Bank originalBank = businessObjectService.findBySinglePrimaryKey(Bank.class, originalBankCode);
427             paymentGroupHistory.setBank(originalBank);
428             paymentGroupHistory.setOrigPaymentStatus(paymentGroup.getPaymentStatus());
429 
430             Person changeUser = getPersonService().getPersonByPrincipalName(OLEConstants.SYSTEM_USER);
431             paymentGroupHistory.setChangeUser(changeUser);
432             paymentGroupHistory.setPaymentGroup(paymentGroup);
433             paymentGroupHistory.setChangeTime(new Timestamp(new Date().getTime()));
434 
435             // save payment group history
436             businessObjectService.save(paymentGroupHistory);
437         }
438 
439         return bankValid;
440     }
441 
442     /**
443      * This method assigns disbursement numbers and tries to combine payment groups with disbursement type check if possible.
444      *
445      * @param paymentProcess
446      * @param postFormatProcessSummary
447      */
448     protected boolean assignDisbursementNumbersAndCombineChecks(PaymentProcess paymentProcess, FormatProcessSummary postFormatProcessSummary) {
449         boolean successful = true;
450 
451         // keep a map with paymentGroupKey and PaymentInfo (disbursementNumber, noteLines)
452         Map<String, PaymentInfo> combinedChecksMap = new HashMap<String, PaymentInfo>();
453 
454         Iterator<PaymentGroup> paymentGroupIterator = this.paymentGroupService.getByProcess(paymentProcess);
455         int maxNoteLines = getMaxNoteLines();
456 
457         while (paymentGroupIterator.hasNext()) {
458             PaymentGroup paymentGroup = paymentGroupIterator.next();
459             if (LOG.isDebugEnabled()) {
460                 LOG.debug("performFormat() Payment Group ID " + paymentGroup.getId());
461             }
462 
463             //Use the customer's profile's campus code to check for disbursement ranges
464             String campus = paymentGroup.getBatch().getCustomerProfile().getDefaultPhysicalCampusProcessingCode();
465             List<DisbursementNumberRange> disbursementRanges = paymentDetailDao.getDisbursementNumberRanges(campus);
466 
467             DisbursementNumberRange range = getRange(disbursementRanges, paymentGroup.getBank(), paymentGroup.getDisbursementType().getCode());
468 
469             if (range == null) {
470                 GlobalVariables.getMessageMap().putError(OLEConstants.GLOBAL_ERRORS, PdpKeyConstants.Format.ErrorMessages.ERROR_FORMAT_DISBURSEMENT_MISSING, campus, paymentGroup.getBank().getBankCode(), paymentGroup.getDisbursementType().getCode());
471                 successful = false;
472                 return successful;
473             }
474 
475             if (PdpConstants.DisbursementTypeCodes.CHECK.equals(paymentGroup.getDisbursementType().getCode())) {
476 
477                 if (paymentGroup.getPymtAttachment().booleanValue() || paymentGroup.getProcessImmediate().booleanValue() || paymentGroup.getPymtSpecialHandling().booleanValue() || (!paymentGroup.getCombineGroups())) {
478                     assignDisbursementNumber(campus, range, paymentGroup, postFormatProcessSummary);
479                 }
480                 else {
481                     String paymentGroupKey = paymentGroup.toStringKey();
482                     // check if there was another paymentGroup we can combine with
483                     if (combinedChecksMap.containsKey(paymentGroupKey)) {
484                         PaymentInfo paymentInfo = combinedChecksMap.get(paymentGroupKey);
485                         paymentInfo.noteLines = paymentInfo.noteLines.add(new KualiInteger(paymentGroup.getNoteLines()));
486 
487                         // if noteLines don't excede the maximum assign the same disbursementNumber
488                         if (paymentInfo.noteLines.intValue() <= maxNoteLines) {
489                             KualiInteger checkNumber = paymentInfo.disbursementNumber;
490                             paymentGroup.setDisbursementNbr(checkNumber);
491 
492                             // update payment info for new noteLines value
493                             combinedChecksMap.put(paymentGroupKey, paymentInfo);
494                         }
495                         // it noteLines more than maxNoteLines we remove the old entry and get a new disbursement number
496                         else {
497                             // remove old entry for this paymentGroupKey
498                             combinedChecksMap.remove(paymentGroupKey);
499 
500                             // get a new check number and the paymentGroup noteLines
501                             KualiInteger checkNumber = assignDisbursementNumber(campus, range, paymentGroup, postFormatProcessSummary);
502                             int noteLines = paymentGroup.getNoteLines();
503 
504                             // create new payment info with these two
505                             paymentInfo = new PaymentInfo(checkNumber, new KualiInteger(noteLines));
506 
507                             // add new entry in the map for this paymentGroupKey
508                             combinedChecksMap.put(paymentGroupKey, paymentInfo);
509 
510                         }
511                     }
512                     // if no entry in the map for this payment group we create a new one
513                     else {
514                         // get a new check number and the paymentGroup noteLines
515                         KualiInteger checkNumber = assignDisbursementNumber(campus, range, paymentGroup, postFormatProcessSummary);
516                         int noteLines = paymentGroup.getNoteLines();
517 
518                         // create new payment info with these two
519                         PaymentInfo paymentInfo = new PaymentInfo(checkNumber, new KualiInteger(noteLines));
520 
521                         // add new entry in the map for this paymentGroupKey
522                         combinedChecksMap.put(paymentGroupKey, paymentInfo);
523                     }
524                 }
525             }
526             else if (PdpConstants.DisbursementTypeCodes.ACH.equals(paymentGroup.getDisbursementType().getCode())) {
527                 assignDisbursementNumber(campus, range, paymentGroup, postFormatProcessSummary);
528             }
529             else {
530                 // if it isn't check or ach, we're in trouble
531                 LOG.error("assignDisbursementNumbers() Payment group " + paymentGroup.getId() + " must be CHCK or ACH.  It is: " + paymentGroup.getDisbursementType());
532                 throw new IllegalArgumentException("Payment group " + paymentGroup.getId() + " must be Check or ACH");
533             }
534 
535             this.businessObjectService.save(paymentGroup);
536 
537             // Generate a GL entry for CHCK & ACH
538             glPendingTransactionService.generatePaymentGeneralLedgerPendingEntry(paymentGroup);
539 
540             // Update all the ranges
541             LOG.debug("assignDisbursementNumbers() Save ranges");
542             for (DisbursementNumberRange element : disbursementRanges) {
543                 this.businessObjectService.save(element);
544             }
545         }
546 
547         return successful;
548     }
549 
550     /**
551      * This method gets a new disbursement number and sets it on the payment group and process summary.
552      *
553      * @param campus
554      * @param range
555      * @param paymentGroup
556      * @param postFormatProcessSummary
557      * @return
558      */
559     protected KualiInteger assignDisbursementNumber(String campus, DisbursementNumberRange range, PaymentGroup paymentGroup, FormatProcessSummary postFormatProcessSummary) {
560         KualiInteger disbursementNumber = new KualiInteger(1 + range.getLastAssignedDisbNbr().intValue());
561 
562         if (disbursementNumber.isGreaterThan(range.getEndDisbursementNbr())) {
563             GlobalVariables.getMessageMap().putError(OLEConstants.GLOBAL_ERRORS, PdpKeyConstants.Format.ErrorMessages.ERROR_FORMAT_DISBURSEMENT_EXHAUSTED, campus, paymentGroup.getBank().getBankCode(), paymentGroup.getDisbursementType().getCode());
564 
565             throw new FormatException("No more disbursement numbers for bank code " + paymentGroup.getBank().getBankCode() + " and disbursement type code " + paymentGroup.getDisbursementType().getCode());
566         }
567 
568         paymentGroup.setDisbursementNbr(disbursementNumber);
569         range.setLastAssignedDisbNbr(disbursementNumber);
570 
571         // Update the summary information
572         postFormatProcessSummary.setDisbursementNumber(paymentGroup, disbursementNumber.intValue());
573 
574         return disbursementNumber;
575     }
576 
577     /**
578      * runs the extract process.
579      */
580     protected void extractChecks() {
581         LOG.debug("extractChecks() started");
582 
583         extractPaymentService.extractChecks();
584     }
585 
586     /**
587      * @see org.kuali.ole.pdp.service.FormatService#clearUnfinishedFormat(java.lang.Integer)
588      */
589     @Override
590     @SuppressWarnings("rawtypes")
591     public void clearUnfinishedFormat(Integer processId) {
592         LOG.debug("clearUnfinishedFormat() started");
593 
594         Map primaryKeys = new HashMap();
595         primaryKeys.put(PdpPropertyConstants.PaymentProcess.PAYMENT_PROCESS_ID, processId);
596         PaymentProcess paymentProcess = this.businessObjectService.findByPrimaryKey(PaymentProcess.class, primaryKeys);
597         if (LOG.isDebugEnabled()) {
598             LOG.debug("clearUnfinishedFormat() Process: " + paymentProcess);
599         }
600 
601         Timestamp now = new Timestamp((new Date()).getTime());
602 
603         PaymentStatus openStatus = businessObjectService.findBySinglePrimaryKey(PaymentStatus.class, PdpConstants.PaymentStatusCodes.OPEN);
604 
605         Iterator groupIterator = formatPaymentDao.unmarkPaymentsForFormat(paymentProcess);
606 
607         while (groupIterator.hasNext()) {
608             PaymentGroup paymentGroup = (PaymentGroup) groupIterator.next();
609             paymentGroup.setLastUpdate(now);
610             paymentGroup.setPaymentStatus(openStatus);
611             businessObjectService.save(paymentGroup);
612         }
613 
614         endFormatProcess(paymentProcess.getCampusCode());
615     }
616 
617     /**
618      * @see org.kuali.ole.pdp.service.FormatService#resetFormatPayments(java.lang.Integer)
619      */
620     @Override
621     public void resetFormatPayments(Integer processId) {
622         LOG.debug("resetFormatPayments() started");
623         clearUnfinishedFormat(processId);
624     }
625 
626     /**
627      * @see org.kuali.ole.pdp.service.FormatService#endFormatProcess(java.lang.String)
628      */
629     @Override
630     @SuppressWarnings("rawtypes")
631     public void endFormatProcess(String campus) {
632         LOG.debug("endFormatProcess() starting");
633 
634         Map primaryKeys = new HashMap();
635         primaryKeys.put(PdpPropertyConstants.PHYS_CAMPUS_PROCESS_CODE, campus);
636 
637         this.businessObjectService.deleteMatching(FormatProcess.class, primaryKeys);
638     }
639 
640     /**
641      * @see org.kuali.ole.pdp.service.FormatService#getAllCustomerProfiles()
642      */
643     @Override
644     public List<CustomerProfile> getAllCustomerProfiles() {
645         if (LOG.isDebugEnabled()) {
646             LOG.debug("getAllCustomerProfiles() started");
647         }
648         Map<String, Object> criteria = new HashMap<String, Object>();
649         criteria.put(OLEPropertyConstants.ACTIVE, Boolean.TRUE);
650 
651         List<CustomerProfile> customerProfileList = (List<CustomerProfile>) getBusinessObjectService().findMatching(CustomerProfile.class, criteria);
652 
653         DynamicCollectionComparator.sort(customerProfileList, PdpPropertyConstants.CustomerProfile.CUSTOMER_PROFILE_CHART_CODE, PdpPropertyConstants.CustomerProfile.CUSTOMER_PROFILE_UNIT_CODE, PdpPropertyConstants.CustomerProfile.CUSTOMER_PROFILE_SUB_UNIT_CODE);
654 
655         return customerProfileList;
656     }
657 
658     /**
659      * @see org.kuali.ole.pdp.service.FormatService#getAllDisbursementNumberRanges()
660      */
661     @Override
662     public List<DisbursementNumberRange> getAllDisbursementNumberRanges() {
663         if (LOG.isDebugEnabled()) {
664             LOG.debug("getAllDisbursementNumberRanges() started");
665         }
666         Map<String, Object> criteria = new HashMap<String, Object>();
667         criteria.put(OLEPropertyConstants.ACTIVE, Boolean.TRUE);
668 
669         List<DisbursementNumberRange> disbursementNumberRangeList = (List<DisbursementNumberRange>) getBusinessObjectService().findMatching(DisbursementNumberRange.class, criteria);
670         DynamicCollectionComparator.sort(disbursementNumberRangeList, PdpPropertyConstants.DisbursementNumberRange.DISBURSEMENT_NUMBER_RANGE_PHYS_CAMPUS_PROC_CODE, PdpPropertyConstants.DisbursementNumberRange.DISBURSEMENT_NUMBER_RANGE_TYPE_CODE);
671 
672         return disbursementNumberRangeList;
673     }
674 
675     /**
676      * Given the List of disbursement number ranges for the processing campus, finds matches for the bank code and disbursement type
677      * code. If more than one match is found, the range with the latest start date (before or equal to today) will be returned.
678      *
679      * @param ranges List of disbursement ranges to search (already filtered to processing campus, active, and start date before or
680      *        equal to today)
681      * @param bank bank code to find range for
682      * @param disbursementTypeCode disbursement type code to find range for
683      * @return found <code>DisbursementNumberRange</code or null if one was not found
684      */
685     protected DisbursementNumberRange getRange(List<DisbursementNumberRange> ranges, Bank bank, String disbursementTypeCode) {
686         if (LOG.isDebugEnabled()) {
687             LOG.debug("getRange() Looking for bank = " + bank.getBankCode() + " and disbursement type " + disbursementTypeCode);
688         }
689 
690         List<DisbursementNumberRange> rangeMatches = new ArrayList<DisbursementNumberRange>();
691         for (DisbursementNumberRange range : ranges) {
692             if (range.getBank().getBankCode().equals(bank.getBankCode()) && range.getDisbursementTypeCode().equals(disbursementTypeCode)) {
693                 rangeMatches.add(range);
694             }
695         }
696 
697         // if more than one match we need to take the range with the latest start date
698         if (rangeMatches.size() > 0) {
699             DisbursementNumberRange maxStartDateRange = rangeMatches.get(0);
700             for (DisbursementNumberRange range : rangeMatches) {
701                 if (range.getDisbNbrRangeStartDt().compareTo(maxStartDateRange.getDisbNbrRangeStartDt()) > 0) {
702                     maxStartDateRange = range;
703                 }
704             }
705 
706             return maxStartDateRange;
707         }
708 
709         return null;
710     }
711 
712     /**
713      * This method sets the formatPaymentDao
714      *
715      * @param fpd
716      */
717     public void setFormatPaymentDao(FormatPaymentDao fpd) {
718         formatPaymentDao = fpd;
719     }
720 
721     /**
722      * This method sets the glPendingTransactionService
723      *
724      * @param gs
725      */
726     public void setGlPendingTransactionService(PendingTransactionService gs) {
727         glPendingTransactionService = gs;
728     }
729 
730     /**
731      * This method sets the achService
732      *
733      * @param as
734      */
735     public void setAchService(AchService as) {
736         achService = as;
737     }
738 
739     /**
740      * This method sets the processDao
741      *
742      * @param pd
743      */
744     public void setProcessDao(ProcessDao pd) {
745         processDao = pd;
746     }
747 
748     /**
749      * This method sets the paymentGroupDao
750      *
751      * @param pgd
752      */
753     public void setPaymentGroupDao(PaymentGroupDao pgd) {
754         paymentGroupDao = pgd;
755     }
756 
757     /**
758      * This method sets the paymentDetailDao
759      *
760      * @param pdd
761      */
762     public void setPaymentDetailDao(PaymentDetailDao pdd) {
763         paymentDetailDao = pdd;
764     }
765 
766     /**
767      * This method sets the schedulerService
768      *
769      * @param ss
770      */
771     public void setSchedulerService(SchedulerService ss) {
772         schedulerService = ss;
773     }
774 
775     /**
776      * This method sets the parameterService
777      *
778      * @param parameterService
779      */
780     public void setParameterService(ParameterService parameterService) {
781         this.parameterService = parameterService;
782     }
783 
784     /**
785      * Gets the businessObjectService attribute.
786      * @return Returns the businessObjectService.
787      */
788     public BusinessObjectService getBusinessObjectService() {
789         return businessObjectService;
790     }
791 
792     /**
793      * This method sets the businessObjectService
794      *
795      * @param bos
796      */
797     public void setBusinessObjectService(BusinessObjectService bos) {
798         this.businessObjectService = bos;
799     }
800 
801     /**
802      * This method sets the paymentGroupService
803      *
804      * @param paymentGroupService
805      */
806     public void setPaymentGroupService(PaymentGroupService paymentGroupService) {
807         this.paymentGroupService = paymentGroupService;
808     }
809 
810     /**
811      * This method sets the dateTimeService
812      *
813      * @param dateTimeService
814      */
815     public void setDateTimeService(DateTimeService dateTimeService) {
816         this.dateTimeService = dateTimeService;
817     }
818 
819     /**
820      * Gets the extractPaymentService attribute.
821      *
822      * @return Returns the extractPaymentService.
823      */
824     protected ExtractPaymentService getExtractPaymentService() {
825         return extractPaymentService;
826     }
827 
828     /**
829      * Sets the extractPaymentService attribute value.
830      *
831      * @param extractPaymentService The extractPaymentService to set.
832      */
833     public void setExtractPaymentService(ExtractPaymentService extractPaymentService) {
834         this.extractPaymentService = extractPaymentService;
835     }
836 
837     /**
838      * @return Returns the personService.
839      */
840     protected PersonService getPersonService() {
841         if(personService==null) {
842             personService = SpringContext.getBean(PersonService.class);
843         }
844         return personService;
845     }
846 
847     /**
848      * This class holds disbursement number and noteLines info for payment group disbursement number assignment and combine checks.
849      */
850     protected class PaymentInfo {
851         public KualiInteger disbursementNumber;
852         public KualiInteger noteLines;
853 
854         public PaymentInfo(KualiInteger disbursementNumber, KualiInteger noteLines) {
855             this.disbursementNumber = disbursementNumber;
856             this.noteLines = noteLines;
857         }
858     }
859 
860 }