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 }