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