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.pdp.service.impl;
017
018 import java.sql.Timestamp;
019 import java.util.ArrayList;
020 import java.util.Date;
021 import java.util.HashMap;
022 import java.util.Iterator;
023 import java.util.List;
024 import java.util.Map;
025
026 import org.apache.commons.lang.StringUtils;
027 import org.kuali.kfs.pdp.PdpConstants;
028 import org.kuali.kfs.pdp.PdpKeyConstants;
029 import org.kuali.kfs.pdp.PdpParameterConstants;
030 import org.kuali.kfs.pdp.PdpPropertyConstants;
031 import org.kuali.kfs.pdp.batch.service.ExtractPaymentService;
032 import org.kuali.kfs.pdp.businessobject.AchAccountNumber;
033 import org.kuali.kfs.pdp.businessobject.CustomerBank;
034 import org.kuali.kfs.pdp.businessobject.CustomerProfile;
035 import org.kuali.kfs.pdp.businessobject.DisbursementNumberRange;
036 import org.kuali.kfs.pdp.businessobject.DisbursementType;
037 import org.kuali.kfs.pdp.businessobject.FormatProcess;
038 import org.kuali.kfs.pdp.businessobject.FormatProcessSummary;
039 import org.kuali.kfs.pdp.businessobject.FormatSelection;
040 import org.kuali.kfs.pdp.businessobject.PayeeACHAccount;
041 import org.kuali.kfs.pdp.businessobject.PaymentChangeCode;
042 import org.kuali.kfs.pdp.businessobject.PaymentDetail;
043 import org.kuali.kfs.pdp.businessobject.PaymentGroup;
044 import org.kuali.kfs.pdp.businessobject.PaymentGroupHistory;
045 import org.kuali.kfs.pdp.businessobject.PaymentProcess;
046 import org.kuali.kfs.pdp.businessobject.PaymentStatus;
047 import org.kuali.kfs.pdp.dataaccess.FormatPaymentDao;
048 import org.kuali.kfs.pdp.dataaccess.PaymentDetailDao;
049 import org.kuali.kfs.pdp.dataaccess.PaymentGroupDao;
050 import org.kuali.kfs.pdp.dataaccess.ProcessDao;
051 import org.kuali.kfs.pdp.service.AchService;
052 import org.kuali.kfs.pdp.service.FormatService;
053 import org.kuali.kfs.pdp.service.PaymentGroupService;
054 import org.kuali.kfs.pdp.service.PendingTransactionService;
055 import org.kuali.kfs.pdp.service.impl.exception.FormatException;
056 import org.kuali.kfs.sys.DynamicCollectionComparator;
057 import org.kuali.kfs.sys.KFSConstants;
058 import org.kuali.kfs.sys.KFSPropertyConstants;
059 import org.kuali.kfs.sys.batch.service.SchedulerService;
060 import org.kuali.kfs.sys.businessobject.Bank;
061 import org.kuali.kfs.sys.context.SpringContext;
062 import org.kuali.kfs.sys.service.impl.KfsParameterConstants;
063 import org.kuali.rice.kim.bo.Person;
064 import org.kuali.rice.kim.service.PersonService;
065 import org.kuali.rice.kns.service.BusinessObjectService;
066 import org.kuali.rice.kns.service.DateTimeService;
067 import org.kuali.rice.kns.service.ParameterService;
068 import org.kuali.rice.kns.util.GlobalVariables;
069 import org.kuali.rice.kns.util.KualiInteger;
070 import org.kuali.rice.kns.util.ObjectUtils;
071 import org.springframework.transaction.annotation.Transactional;
072
073 @Transactional
074 public class FormatServiceImpl implements FormatService {
075 private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(FormatServiceImpl.class);
076
077 private PaymentDetailDao paymentDetailDao;
078 private PaymentGroupDao paymentGroupDao;
079 private ProcessDao processDao;
080 private AchService achService;
081 private PendingTransactionService glPendingTransactionService;
082 private ParameterService parameterService;
083 private FormatPaymentDao formatPaymentDao;
084 private SchedulerService schedulerService;
085 private BusinessObjectService businessObjectService;
086 private PaymentGroupService paymentGroupService;
087 private DateTimeService dateTimeService;
088 private ExtractPaymentService extractPaymentService;
089 private PersonService<Person> personService;
090
091 /**
092 * Constructs a FormatServiceImpl.java.
093 */
094 public FormatServiceImpl() {
095 super();
096 }
097
098 /**
099 * @see org.kuali.kfs.pdp.service.FormatProcessService#getDataForFormat(org.kuali.rice.kim.bo.Person)
100 */
101 public FormatSelection getDataForFormat(Person user) {
102
103 String campusCode = user.getCampusCode();
104 Date formatStartDate = getFormatProcessStartDate(campusCode);
105
106 // create new FormatSelection object an set the campus code and the start date
107 FormatSelection formatSelection = new FormatSelection();
108 formatSelection.setCampus(campusCode);
109 formatSelection.setStartDate(formatStartDate);
110
111 // if format process not started yet, populate the other data as well
112 if (formatStartDate == null) {
113 formatSelection.setCustomerList(getAllCustomerProfiles());
114 formatSelection.setRangeList(getAllDisbursementNumberRanges());
115 }
116
117 return formatSelection;
118 }
119
120 /**
121 * @see org.kuali.kfs.pdp.service.FormatService#getFormatProcessStartDate(java.lang.String)
122 */
123 @SuppressWarnings("rawtypes")
124 public Date getFormatProcessStartDate(String campus) {
125 LOG.debug("getFormatProcessStartDate() started");
126
127 Map primaryKeys = new HashMap();
128 primaryKeys.put(PdpPropertyConstants.PHYS_CAMPUS_PROCESS_CODE, campus);
129 FormatProcess formatProcess = (FormatProcess) this.businessObjectService.findByPrimaryKey(FormatProcess.class, primaryKeys);
130
131 if (formatProcess != null) {
132 LOG.debug("getFormatProcessStartDate() found");
133 return new Date(formatProcess.getBeginFormat().getTime());
134 }
135 else {
136 LOG.debug("getFormatProcessStartDate() not found");
137 return null;
138 }
139 }
140
141 /**
142 * @see org.kuali.kfs.pdp.service.FormatService#startFormatProcess(org.kuali.rice.kim.bo.Person, java.lang.String,
143 * java.util.List, java.util.Date, java.lang.String)
144 */
145 public FormatProcessSummary startFormatProcess(Person user, String campus, List<CustomerProfile> customers, Date paydate, String paymentTypes) {
146 LOG.debug("startFormatProcess() started");
147
148 for (CustomerProfile element : customers) {
149 LOG.debug("startFormatProcess() Customer: " + element);
150 }
151
152 // Create the process
153 Date d = new Date();
154 PaymentProcess paymentProcess = new PaymentProcess();
155 paymentProcess.setCampusCode(campus);
156 paymentProcess.setProcessUser(user);
157 paymentProcess.setProcessTimestamp(new Timestamp(d.getTime()));
158
159 this.businessObjectService.save(paymentProcess);
160
161 // add an entry in the format process table (to lock the format process)
162 FormatProcess formatProcess = new FormatProcess();
163
164 formatProcess.setPhysicalCampusProcessCode(campus);
165 formatProcess.setBeginFormat(dateTimeService.getCurrentTimestamp());
166 formatProcess.setPaymentProcIdentifier(paymentProcess.getId().intValue());
167
168 this.businessObjectService.save(formatProcess);
169
170 // Mark all of them ready for format
171 formatPaymentDao.markPaymentsForFormat(paymentProcess, customers, paydate, paymentTypes);
172
173 // summarize them
174 FormatProcessSummary preFormatProcessSummary = new FormatProcessSummary();
175 Iterator<PaymentGroup> iterator = this.paymentGroupService.getByProcess(paymentProcess);
176
177 while (iterator.hasNext()) {
178 PaymentGroup paymentGroup = iterator.next();
179 preFormatProcessSummary.add(paymentGroup);
180 }
181
182 // if no payments found for format clear the format process
183 if (preFormatProcessSummary.getProcessSummaryList().size() == 0) {
184 LOG.debug("startFormatProcess() No payments to process. Format process ending");
185 clearUnfinishedFormat(paymentProcess.getId().intValue());// ?? maybe call end format process
186 }
187
188 return preFormatProcessSummary;
189 }
190
191
192 /**
193 * This method gets the maximum number of lines in a note.
194 *
195 * @return the maximum number of lines in a note
196 */
197 protected int getMaxNoteLines() {
198 String maxLines = parameterService.getParameterValue(KfsParameterConstants.PRE_DISBURSEMENT_ALL.class, PdpParameterConstants.MAX_NOTE_LINES);
199 if (StringUtils.isBlank(maxLines)) {
200 throw new RuntimeException("System parameter for max note lines is blank");
201 }
202
203 return Integer.parseInt(maxLines);
204 }
205
206 /**
207 * @see org.kuali.kfs.pdp.service.FormatService#performFormat(java.lang.Integer)
208 */
209 public void performFormat(Integer processId) throws FormatException {
210 LOG.debug("performFormat() started");
211
212 // get the PaymentProcess for the given id
213 @SuppressWarnings("rawtypes")
214 Map primaryKeys = new HashMap();
215 primaryKeys.put(PdpPropertyConstants.PaymentProcess.PAYMENT_PROCESS_ID, processId);
216 PaymentProcess paymentProcess = (PaymentProcess) this.businessObjectService.findByPrimaryKey(PaymentProcess.class, primaryKeys);
217 if (paymentProcess == null) {
218 LOG.error("performFormat() Invalid proc ID " + processId);
219 throw new RuntimeException("Invalid proc ID");
220 }
221
222 String processCampus = paymentProcess.getCampusCode();
223 FormatProcessSummary postFormatProcessSummary = new FormatProcessSummary();
224
225 // step 1 get ACH or Check, Bank info, ACH info, sorting
226 Iterator<PaymentGroup> paymentGroupIterator = this.paymentGroupService.getByProcess(paymentProcess);
227 while (paymentGroupIterator.hasNext()) {
228 PaymentGroup paymentGroup = paymentGroupIterator.next();
229 LOG.debug("performFormat() Step 1 Payment Group ID " + paymentGroup.getId());
230
231 // process payment group data
232 boolean groupProcessed = processPaymentGroup(paymentGroup, paymentProcess);
233 if (!groupProcessed) {
234 throw new FormatException("Error encountered during format");
235 }
236
237 // save payment group
238 this.businessObjectService.save(paymentGroup);
239
240 // Add to summary information
241 postFormatProcessSummary.add(paymentGroup);
242 }
243
244 // step 2 assign disbursement numbers and combine checks into one if possible
245 boolean disbursementNumbersAssigned = assignDisbursementNumbersAndCombineChecks(paymentProcess, postFormatProcessSummary);
246 if (!disbursementNumbersAssigned) {
247 throw new FormatException("Error encountered during format");
248 }
249
250 // step 3 save the summarizing info
251 LOG.debug("performFormat() Save summarizing information");
252 postFormatProcessSummary.save();
253
254 // step 4 set formatted indicator to true and save in the db
255 paymentProcess.setFormattedIndicator(true);
256 businessObjectService.save(paymentProcess);
257
258 // step 5 end the format process for this campus
259 LOG.debug("performFormat() End the format process for this campus");
260 endFormatProcess(processCampus);
261
262 // step 6 tell the extract batch job to start
263 LOG.debug("performFormat() Start extract");
264 extractChecks();
265 }
266
267 /**
268 * This method processes the payment group data.
269 *
270 * @param paymentGroup
271 * @param paymentProcess
272 */
273 protected boolean processPaymentGroup(PaymentGroup paymentGroup, PaymentProcess paymentProcess) {
274 boolean successful = true;
275
276 paymentGroup.setSortValue(paymentGroupService.getSortGroupId(paymentGroup));
277 paymentGroup.setPhysCampusProcessCd(paymentProcess.getCampusCode());
278 paymentGroup.setProcess(paymentProcess);
279
280 // If any one of the payment details in the group are negative, we always force a check
281 boolean noNegativeDetails = true;
282
283 // If any one of the payment details in the group are negative, we always force a check
284 List<PaymentDetail> paymentDetailsList = paymentGroup.getPaymentDetails();
285 for (PaymentDetail paymentDetail : paymentDetailsList) {
286 if (paymentDetail.getNetPaymentAmount().doubleValue() < 0) {
287 LOG.debug("performFormat() Payment Group " + paymentGroup + " has payment detail net payment amount " + paymentDetail.getNetPaymentAmount());
288 LOG.debug("performFormat() Forcing a Check for Group");
289 noNegativeDetails = false;
290 break;
291 }
292 }
293
294 // determine whether payment should be ACH or Check
295 CustomerProfile customer = paymentGroup.getBatch().getCustomerProfile();
296
297 PayeeACHAccount payeeAchAccount = null;
298 boolean isCheck = true;
299 if (PdpConstants.PayeeIdTypeCodes.VENDOR_ID.equals(paymentGroup.getPayeeIdTypeCd()) || PdpConstants.PayeeIdTypeCodes.EMPLOYEE.equals(paymentGroup.getPayeeIdTypeCd()) || PdpConstants.PayeeIdTypeCodes.ENTITY.equals(paymentGroup.getPayeeIdTypeCd())) {
300 if (StringUtils.isNotBlank(paymentGroup.getPayeeId()) && !paymentGroup.getPymtAttachment() && !paymentGroup.getProcessImmediate() && !paymentGroup.getPymtSpecialHandling() && (customer.getAchTransactionType() != null) && noNegativeDetails) {
301 LOG.debug("performFormat() Checking ACH");
302 payeeAchAccount = achService.getAchInformation(paymentGroup.getPayeeIdTypeCd(), paymentGroup.getPayeeId(), customer.getAchTransactionType());
303 isCheck = (payeeAchAccount == null);
304 }
305 }
306
307 DisbursementType disbursementType = null;
308 if (isCheck) {
309 PaymentStatus paymentStatus = (PaymentStatus) businessObjectService.findBySinglePrimaryKey(PaymentStatus.class, PdpConstants.PaymentStatusCodes.PENDING_CHECK);
310 paymentGroup.setPaymentStatus(paymentStatus);
311
312 disbursementType = (DisbursementType) businessObjectService.findBySinglePrimaryKey(DisbursementType.class, PdpConstants.DisbursementTypeCodes.CHECK);
313 paymentGroup.setDisbursementType(disbursementType);
314 }
315 else {
316 PaymentStatus paymentStatus = (PaymentStatus) businessObjectService.findBySinglePrimaryKey(PaymentStatus.class, PdpConstants.PaymentStatusCodes.PENDING_ACH);
317 paymentGroup.setPaymentStatus(paymentStatus);
318
319 disbursementType = (DisbursementType) businessObjectService.findBySinglePrimaryKey(DisbursementType.class, PdpConstants.DisbursementTypeCodes.ACH);
320 paymentGroup.setDisbursementType(disbursementType);
321
322 paymentGroup.setAchBankRoutingNbr(payeeAchAccount.getBankRoutingNumber());
323 paymentGroup.setAdviceEmailAddress(payeeAchAccount.getPayeeEmailAddress());
324 paymentGroup.setAchAccountType(payeeAchAccount.getBankAccountTypeCode());
325
326 AchAccountNumber achAccountNumber = new AchAccountNumber();
327 achAccountNumber.setAchBankAccountNbr(payeeAchAccount.getBankAccountNumber());
328 achAccountNumber.setId(paymentGroup.getId());
329 paymentGroup.setAchAccountNumber(achAccountNumber);
330 }
331
332 // set payment group bank
333 successful &= validateAndUpdatePaymentGroupBankCode(paymentGroup, disbursementType, customer);
334
335 return successful;
336 }
337
338 /**
339 * 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
340 * 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
341 * 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
342 * global message map.
343 *
344 * @param paymentGroup group to set bank on
345 * @param disbursementType type of disbursement for given payment group
346 * @param customer customer profile for payment group
347 * @return boolean true if a valid bank is set on the payment group, false otherwise
348 */
349 protected boolean validateAndUpdatePaymentGroupBankCode(PaymentGroup paymentGroup, DisbursementType disbursementType, CustomerProfile customer) {
350 boolean bankValid = true;
351
352 String originalBankCode = paymentGroup.getBankCode();
353 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()) {
354 CustomerBank customerBank = customer.getCustomerBankByDisbursementType(disbursementType.getCode());
355 if (ObjectUtils.isNotNull(customerBank) && customerBank.isActive() && ObjectUtils.isNotNull(customerBank.getBank()) && customerBank.getBank().isActive()) {
356 paymentGroup.setBankCode(customerBank.getBankCode());
357 paymentGroup.setBank(customerBank.getBank());
358 }
359 else if (ObjectUtils.isNotNull(customerBank) && ObjectUtils.isNotNull(customerBank.getBank()) && ObjectUtils.isNotNull(customerBank.getBank().getContinuationBank()) && customerBank.getBank().getContinuationBank().isActive()) {
360 paymentGroup.setBankCode(customerBank.getBank().getContinuationBank().getBankCode());
361 paymentGroup.setBank(customerBank.getBank().getContinuationBank());
362 }
363 }
364
365 if (ObjectUtils.isNull(paymentGroup.getBank())) {
366 LOG.error("performFormat() A bank is needed for " + disbursementType.getName() + " disbursement type for customer: " + customer);
367 GlobalVariables.getMessageMap().putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.Format.ErrorMessages.ERROR_FORMAT_BANK_MISSING, customer.getCustomerShortName());
368 bankValid = false;
369
370 return bankValid;
371 }
372
373 // create payment history record if bank was changed
374 if (StringUtils.isNotBlank(originalBankCode) && !paymentGroup.getBankCode().equals(originalBankCode)) {
375 PaymentGroupHistory paymentGroupHistory = new PaymentGroupHistory();
376
377 PaymentChangeCode paymentChangeCode = (PaymentChangeCode) businessObjectService.findBySinglePrimaryKey(PaymentChangeCode.class, PdpConstants.PaymentChangeCodes.BANK_CHNG_CD);
378 paymentGroupHistory.setPaymentChange(paymentChangeCode);
379 paymentGroupHistory.setOrigBankCode(originalBankCode);
380
381 Bank originalBank = (Bank) businessObjectService.findBySinglePrimaryKey(Bank.class, originalBankCode);
382 paymentGroupHistory.setBank(originalBank);
383 paymentGroupHistory.setOrigPaymentStatus(paymentGroup.getPaymentStatus());
384
385 Person changeUser = getPersonService().getPerson(KFSConstants.SYSTEM_USER);
386 paymentGroupHistory.setChangeUser(changeUser);
387 paymentGroupHistory.setPaymentGroup(paymentGroup);
388 paymentGroupHistory.setChangeTime(new Timestamp(new Date().getTime()));
389
390 // save payment group history
391 businessObjectService.save(paymentGroupHistory);
392 }
393
394 return bankValid;
395 }
396
397 /**
398 * This method assigns disbursement numbers and tries to combine payment groups with disbursement type check if possible.
399 *
400 * @param paymentProcess
401 * @param postFormatProcessSummary
402 */
403 protected boolean assignDisbursementNumbersAndCombineChecks(PaymentProcess paymentProcess, FormatProcessSummary postFormatProcessSummary) {
404 boolean successful = true;
405
406 // keep a map with paymentGroupKey and PaymentInfo (disbursementNumber, noteLines)
407 Map<String, PaymentInfo> combinedChecksMap = new HashMap<String, PaymentInfo>();
408
409 Iterator<PaymentGroup> paymentGroupIterator = this.paymentGroupService.getByProcess(paymentProcess);
410 int maxNoteLines = getMaxNoteLines();
411
412 while (paymentGroupIterator.hasNext()) {
413 PaymentGroup paymentGroup = paymentGroupIterator.next();
414 LOG.debug("performFormat() Payment Group ID " + paymentGroup.getId());
415
416 //Use the customer's profile's campus code to check for disbursement ranges
417 String campus = paymentGroup.getBatch().getCustomerProfile().getDefaultPhysicalCampusProcessingCode();
418 List<DisbursementNumberRange> disbursementRanges = paymentDetailDao.getDisbursementNumberRanges(campus);
419
420 DisbursementNumberRange range = getRange(disbursementRanges, paymentGroup.getBank(), paymentGroup.getDisbursementType().getCode());
421
422 if (range == null) {
423 GlobalVariables.getMessageMap().putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.Format.ErrorMessages.ERROR_FORMAT_DISBURSEMENT_MISSING, campus, paymentGroup.getBank().getBankCode(), paymentGroup.getDisbursementType().getCode());
424 successful = false;
425 return successful;
426 }
427
428 if (PdpConstants.DisbursementTypeCodes.CHECK.equals(paymentGroup.getDisbursementType().getCode())) {
429
430 if (paymentGroup.getPymtAttachment().booleanValue() || paymentGroup.getProcessImmediate().booleanValue() || paymentGroup.getPymtSpecialHandling().booleanValue() || (!paymentGroup.getCombineGroups())) {
431 assignDisbursementNumber(campus, range, paymentGroup, postFormatProcessSummary);
432 }
433 else {
434 String paymentGroupKey = paymentGroup.toStringKey();
435 // check if there was another paymentGroup we can combine with
436 if (combinedChecksMap.containsKey(paymentGroupKey)) {
437 PaymentInfo paymentInfo = combinedChecksMap.get(paymentGroupKey);
438 paymentInfo.noteLines = paymentInfo.noteLines.add(new KualiInteger(paymentGroup.getNoteLines()));
439
440 // if noteLines don't excede the maximum assign the same disbursementNumber
441 if (paymentInfo.noteLines.intValue() <= maxNoteLines) {
442 KualiInteger checkNumber = paymentInfo.disbursementNumber;
443 paymentGroup.setDisbursementNbr(checkNumber);
444
445 // update payment info for new noteLines value
446 combinedChecksMap.put(paymentGroupKey, paymentInfo);
447 }
448 // it noteLines more than maxNoteLines we remove the old entry and get a new disbursement number
449 else {
450 // remove old entry for this paymentGroupKey
451 combinedChecksMap.remove(paymentGroupKey);
452
453 // get a new check number and the paymentGroup noteLines
454 KualiInteger checkNumber = assignDisbursementNumber(campus, range, paymentGroup, postFormatProcessSummary);
455 int noteLines = paymentGroup.getNoteLines();
456
457 // create new payment info with these two
458 paymentInfo = new PaymentInfo(checkNumber, new KualiInteger(noteLines));
459
460 // add new entry in the map for this paymentGroupKey
461 combinedChecksMap.put(paymentGroupKey, paymentInfo);
462
463 }
464 }
465 // if no entry in the map for this payment group we create a new one
466 else {
467 // get a new check number and the paymentGroup noteLines
468 KualiInteger checkNumber = assignDisbursementNumber(campus, range, paymentGroup, postFormatProcessSummary);
469 int noteLines = paymentGroup.getNoteLines();
470
471 // create new payment info with these two
472 PaymentInfo paymentInfo = new PaymentInfo(checkNumber, new KualiInteger(noteLines));
473
474 // add new entry in the map for this paymentGroupKey
475 combinedChecksMap.put(paymentGroupKey, paymentInfo);
476 }
477 }
478 }
479 else if (PdpConstants.DisbursementTypeCodes.ACH.equals(paymentGroup.getDisbursementType().getCode())) {
480 assignDisbursementNumber(campus, range, paymentGroup, postFormatProcessSummary);
481 }
482 else {
483 // if it isn't check or ach, we're in trouble
484 LOG.error("assignDisbursementNumbers() Payment group " + paymentGroup.getId() + " must be CHCK or ACH. It is: " + paymentGroup.getDisbursementType());
485 throw new IllegalArgumentException("Payment group " + paymentGroup.getId() + " must be Check or ACH");
486 }
487
488 this.businessObjectService.save(paymentGroup);
489
490 // Generate a GL entry for CHCK & ACH
491 glPendingTransactionService.generatePaymentGeneralLedgerPendingEntry(paymentGroup);
492
493 // Update all the ranges
494 LOG.debug("assignDisbursementNumbers() Save ranges");
495 for (DisbursementNumberRange element : disbursementRanges) {
496 this.businessObjectService.save(element);
497 }
498 }
499
500 return successful;
501 }
502
503 /**
504 * This method gets a new disbursement number and sets it on the payment group and process summary.
505 *
506 * @param campus
507 * @param range
508 * @param paymentGroup
509 * @param postFormatProcessSummary
510 * @return
511 */
512 protected KualiInteger assignDisbursementNumber(String campus, DisbursementNumberRange range, PaymentGroup paymentGroup, FormatProcessSummary postFormatProcessSummary) {
513 KualiInteger disbursementNumber = new KualiInteger(1 + range.getLastAssignedDisbNbr().intValue());
514
515 if (disbursementNumber.isGreaterThan(range.getEndDisbursementNbr())) {
516 GlobalVariables.getMessageMap().putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.Format.ErrorMessages.ERROR_FORMAT_DISBURSEMENT_EXHAUSTED, campus, paymentGroup.getBank().getBankCode(), paymentGroup.getDisbursementType().getCode());
517
518 throw new FormatException("No more disbursement numbers for bank code " + paymentGroup.getBank().getBankCode() + " and disbursement type code " + paymentGroup.getDisbursementType().getCode());
519 }
520
521 paymentGroup.setDisbursementNbr(disbursementNumber);
522 range.setLastAssignedDisbNbr(disbursementNumber);
523
524 // Update the summary information
525 postFormatProcessSummary.setDisbursementNumber(paymentGroup, disbursementNumber.intValue());
526
527 return disbursementNumber;
528 }
529
530 /**
531 * runs the extract process.
532 */
533 protected void extractChecks() {
534 LOG.debug("extractChecks() started");
535
536 extractPaymentService.extractChecks();
537 }
538
539 /**
540 * @see org.kuali.kfs.pdp.service.FormatService#clearUnfinishedFormat(java.lang.Integer)
541 */
542 @SuppressWarnings("rawtypes")
543 public void clearUnfinishedFormat(Integer processId) {
544 LOG.debug("clearUnfinishedFormat() started");
545
546 Map primaryKeys = new HashMap();
547 primaryKeys.put(PdpPropertyConstants.PaymentProcess.PAYMENT_PROCESS_ID, processId);
548 PaymentProcess paymentProcess = (PaymentProcess) this.businessObjectService.findByPrimaryKey(PaymentProcess.class, primaryKeys);
549 LOG.debug("clearUnfinishedFormat() Process: " + paymentProcess);
550
551 formatPaymentDao.unmarkPaymentsForFormat(paymentProcess);
552
553 endFormatProcess(paymentProcess.getCampusCode());
554 }
555
556 /**
557 * @see org.kuali.kfs.pdp.service.FormatService#resetFormatPayments(java.lang.Integer)
558 */
559 public void resetFormatPayments(Integer processId) {
560 LOG.debug("resetFormatPayments() started");
561 clearUnfinishedFormat(processId);
562 }
563
564 /**
565 * @see org.kuali.kfs.pdp.service.FormatService#endFormatProcess(java.lang.String)
566 */
567 @SuppressWarnings("rawtypes")
568 public void endFormatProcess(String campus) {
569 LOG.debug("endFormatProcess() starting");
570
571 Map primaryKeys = new HashMap();
572 primaryKeys.put(PdpPropertyConstants.PHYS_CAMPUS_PROCESS_CODE, campus);
573
574 this.businessObjectService.deleteMatching(FormatProcess.class, primaryKeys);
575 }
576
577 /**
578 * @see org.kuali.kfs.pdp.service.FormatService#getAllCustomerProfiles()
579 */
580 public List<CustomerProfile> getAllCustomerProfiles() {
581 if (LOG.isDebugEnabled()) {
582 LOG.debug("getAllCustomerProfiles() started");
583 }
584 Map<String, Object> criteria = new HashMap<String, Object>();
585 criteria.put(KFSPropertyConstants.ACTIVE, Boolean.TRUE);
586
587 List<CustomerProfile> customerProfileList = (List<CustomerProfile>) getBusinessObjectService().findMatching(CustomerProfile.class, criteria);
588
589 DynamicCollectionComparator.sort(customerProfileList, PdpPropertyConstants.CustomerProfile.CUSTOMER_PROFILE_CHART_CODE, PdpPropertyConstants.CustomerProfile.CUSTOMER_PROFILE_UNIT_CODE, PdpPropertyConstants.CustomerProfile.CUSTOMER_PROFILE_SUB_UNIT_CODE);
590
591 return customerProfileList;
592 }
593
594 /**
595 * @see org.kuali.kfs.pdp.service.FormatService#getAllDisbursementNumberRanges()
596 */
597 public List<DisbursementNumberRange> getAllDisbursementNumberRanges() {
598 if (LOG.isDebugEnabled()) {
599 LOG.debug("getAllDisbursementNumberRanges() started");
600 }
601 Map<String, Object> criteria = new HashMap<String, Object>();
602 criteria.put(KFSPropertyConstants.ACTIVE, Boolean.TRUE);
603
604 List<DisbursementNumberRange> disbursementNumberRangeList = (List<DisbursementNumberRange>) getBusinessObjectService().findMatching(DisbursementNumberRange.class, criteria);
605 DynamicCollectionComparator.sort(disbursementNumberRangeList, PdpPropertyConstants.DisbursementNumberRange.DISBURSEMENT_NUMBER_RANGE_PHYS_CAMPUS_PROC_CODE, PdpPropertyConstants.DisbursementNumberRange.DISBURSEMENT_NUMBER_RANGE_TYPE_CODE);
606
607 return disbursementNumberRangeList;
608 }
609
610 /**
611 * Given the List of disbursement number ranges for the processing campus, finds matches for the bank code and disbursement type
612 * code. If more than one match is found, the range with the latest start date (before or equal to today) will be returned.
613 *
614 * @param ranges List of disbursement ranges to search (already filtered to processing campus, active, and start date before or
615 * equal to today)
616 * @param bank bank code to find range for
617 * @param disbursementTypeCode disbursement type code to find range for
618 * @return found <code>DisbursementNumberRange</code or null if one was not found
619 */
620 protected DisbursementNumberRange getRange(List<DisbursementNumberRange> ranges, Bank bank, String disbursementTypeCode) {
621 LOG.debug("getRange() Looking for bank = " + bank.getBankCode() + " and disbursement type " + disbursementTypeCode);
622
623 List<DisbursementNumberRange> rangeMatches = new ArrayList<DisbursementNumberRange>();
624 for (DisbursementNumberRange range : ranges) {
625 if (range.getBank().getBankCode().equals(bank.getBankCode()) && range.getDisbursementTypeCode().equals(disbursementTypeCode)) {
626 rangeMatches.add(range);
627 }
628 }
629
630 // if more than one match we need to take the range with the latest start date
631 if (rangeMatches.size() > 0) {
632 DisbursementNumberRange maxStartDateRange = rangeMatches.get(0);
633 for (DisbursementNumberRange range : rangeMatches) {
634 if (range.getDisbNbrRangeStartDt().compareTo(maxStartDateRange.getDisbNbrRangeStartDt()) > 0) {
635 maxStartDateRange = range;
636 }
637 }
638
639 return maxStartDateRange;
640 }
641
642 return null;
643 }
644
645 /**
646 * This method sets the formatPaymentDao
647 *
648 * @param fpd
649 */
650 public void setFormatPaymentDao(FormatPaymentDao fpd) {
651 formatPaymentDao = fpd;
652 }
653
654 /**
655 * This method sets the glPendingTransactionService
656 *
657 * @param gs
658 */
659 public void setGlPendingTransactionService(PendingTransactionService gs) {
660 glPendingTransactionService = gs;
661 }
662
663 /**
664 * This method sets the achService
665 *
666 * @param as
667 */
668 public void setAchService(AchService as) {
669 achService = as;
670 }
671
672 /**
673 * This method sets the processDao
674 *
675 * @param pd
676 */
677 public void setProcessDao(ProcessDao pd) {
678 processDao = pd;
679 }
680
681 /**
682 * This method sets the paymentGroupDao
683 *
684 * @param pgd
685 */
686 public void setPaymentGroupDao(PaymentGroupDao pgd) {
687 paymentGroupDao = pgd;
688 }
689
690 /**
691 * This method sets the paymentDetailDao
692 *
693 * @param pdd
694 */
695 public void setPaymentDetailDao(PaymentDetailDao pdd) {
696 paymentDetailDao = pdd;
697 }
698
699 /**
700 * This method sets the schedulerService
701 *
702 * @param ss
703 */
704 public void setSchedulerService(SchedulerService ss) {
705 schedulerService = ss;
706 }
707
708 /**
709 * This method sets the parameterService
710 *
711 * @param parameterService
712 */
713 public void setParameterService(ParameterService parameterService) {
714 this.parameterService = parameterService;
715 }
716
717 /**
718 * Gets the businessObjectService attribute.
719 * @return Returns the businessObjectService.
720 */
721 public BusinessObjectService getBusinessObjectService() {
722 return businessObjectService;
723 }
724
725 /**
726 * This method sets the businessObjectService
727 *
728 * @param bos
729 */
730 public void setBusinessObjectService(BusinessObjectService bos) {
731 this.businessObjectService = bos;
732 }
733
734 /**
735 * This method sets the paymentGroupService
736 *
737 * @param paymentGroupService
738 */
739 public void setPaymentGroupService(PaymentGroupService paymentGroupService) {
740 this.paymentGroupService = paymentGroupService;
741 }
742
743 /**
744 * This method sets the dateTimeService
745 *
746 * @param dateTimeService
747 */
748 public void setDateTimeService(DateTimeService dateTimeService) {
749 this.dateTimeService = dateTimeService;
750 }
751
752 /**
753 * Gets the extractPaymentService attribute.
754 *
755 * @return Returns the extractPaymentService.
756 */
757 protected ExtractPaymentService getExtractPaymentService() {
758 return extractPaymentService;
759 }
760
761 /**
762 * Sets the extractPaymentService attribute value.
763 *
764 * @param extractPaymentService The extractPaymentService to set.
765 */
766 public void setExtractPaymentService(ExtractPaymentService extractPaymentService) {
767 this.extractPaymentService = extractPaymentService;
768 }
769
770 /**
771 * @return Returns the personService.
772 */
773 protected PersonService<Person> getPersonService() {
774 if(personService==null) {
775 personService = SpringContext.getBean(PersonService.class);
776 }
777 return personService;
778 }
779
780 /**
781 * This class holds disbursement number and noteLines info for payment group disbursement number assignment and combine checks.
782 */
783 protected class PaymentInfo {
784 public KualiInteger disbursementNumber;
785 public KualiInteger noteLines;
786
787 public PaymentInfo(KualiInteger disbursementNumber, KualiInteger noteLines) {
788 this.disbursementNumber = disbursementNumber;
789 this.noteLines = noteLines;
790 }
791 }
792
793 }