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.text.MessageFormat; 020 import java.text.ParseException; 021 import java.util.ArrayList; 022 import java.util.Calendar; 023 import java.util.List; 024 025 import org.apache.commons.lang.StringUtils; 026 import org.kuali.kfs.coa.businessobject.Account; 027 import org.kuali.kfs.coa.businessobject.ObjectCode; 028 import org.kuali.kfs.coa.businessobject.ProjectCode; 029 import org.kuali.kfs.coa.businessobject.SubAccount; 030 import org.kuali.kfs.coa.businessobject.SubObjectCode; 031 import org.kuali.kfs.coa.service.AccountService; 032 import org.kuali.kfs.coa.service.ObjectCodeService; 033 import org.kuali.kfs.coa.service.ProjectCodeService; 034 import org.kuali.kfs.coa.service.SubAccountService; 035 import org.kuali.kfs.coa.service.SubObjectCodeService; 036 import org.kuali.kfs.pdp.PdpConstants; 037 import org.kuali.kfs.pdp.PdpKeyConstants; 038 import org.kuali.kfs.pdp.PdpParameterConstants; 039 import org.kuali.kfs.pdp.PdpPropertyConstants; 040 import org.kuali.kfs.pdp.businessobject.AccountingChangeCode; 041 import org.kuali.kfs.pdp.businessobject.CustomerProfile; 042 import org.kuali.kfs.pdp.businessobject.PayeeType; 043 import org.kuali.kfs.pdp.businessobject.PaymentAccountDetail; 044 import org.kuali.kfs.pdp.businessobject.PaymentAccountHistory; 045 import org.kuali.kfs.pdp.businessobject.PaymentDetail; 046 import org.kuali.kfs.pdp.businessobject.PaymentFileLoad; 047 import org.kuali.kfs.pdp.businessobject.PaymentGroup; 048 import org.kuali.kfs.pdp.businessobject.PaymentStatus; 049 import org.kuali.kfs.pdp.dataaccess.PaymentFileLoadDao; 050 import org.kuali.kfs.pdp.service.CustomerProfileService; 051 import org.kuali.kfs.pdp.service.PaymentFileValidationService; 052 import org.kuali.kfs.sys.KFSConstants; 053 import org.kuali.kfs.sys.businessobject.Bank; 054 import org.kuali.kfs.sys.businessobject.OriginationCode; 055 import org.kuali.kfs.sys.context.SpringContext; 056 import org.kuali.kfs.sys.service.BankService; 057 import org.kuali.kfs.sys.service.OriginationCodeService; 058 import org.kuali.kfs.sys.service.impl.KfsParameterConstants; 059 import org.kuali.rice.kew.exception.WorkflowException; 060 import org.kuali.rice.kns.bo.KualiCodeBase; 061 import org.kuali.rice.kns.service.BusinessObjectService; 062 import org.kuali.rice.kns.service.DateTimeService; 063 import org.kuali.rice.kns.service.KualiConfigurationService; 064 import org.kuali.rice.kns.service.ParameterService; 065 import org.kuali.rice.kns.util.KualiDecimal; 066 import org.kuali.rice.kns.util.MessageMap; 067 import org.kuali.rice.kns.workflow.service.KualiWorkflowInfo; 068 import org.springframework.transaction.annotation.Transactional; 069 070 /** 071 * @see org.kuali.kfs.pdp.batch.service.PaymentFileValidationService 072 */ 073 @Transactional 074 public class PaymentFileValidationServiceImpl implements PaymentFileValidationService { 075 private CustomerProfileService customerProfileService; 076 private PaymentFileLoadDao paymentFileLoadDao; 077 private ParameterService parameterService; 078 private KualiConfigurationService kualiConfigurationService; 079 private DateTimeService dateTimeService; 080 private AccountService accountService; 081 private SubAccountService subAccountService; 082 private ObjectCodeService objectCodeService; 083 private SubObjectCodeService subObjectCodeService; 084 private ProjectCodeService projectCodeService; 085 private BankService bankService; 086 private OriginationCodeService originationCodeService; 087 private KualiWorkflowInfo workflowInfoService; 088 private BusinessObjectService businessObjectService; 089 090 /** 091 * @see org.kuali.kfs.pdp.batch.service.PaymentFileValidationService#doHardEdits(org.kuali.kfs.pdp.businessobject.PaymentFile, 092 * org.kuali.rice.kns.util.ErrorMap) 093 */ 094 public void doHardEdits(PaymentFileLoad paymentFile, MessageMap errorMap) { 095 processHeaderValidation(paymentFile, errorMap); 096 097 if (errorMap.hasNoErrors()) { 098 processGroupValidation(paymentFile, errorMap); 099 } 100 101 if (errorMap.hasNoErrors()) { 102 processTrailerValidation(paymentFile, errorMap); 103 } 104 } 105 106 /** 107 * Validates payment file header fields <li>Checks customer exists in customer profile table and is active</li> 108 * 109 * @param paymentFile payment file object 110 * @param errorMap map in which errors will be added to 111 */ 112 protected void processHeaderValidation(PaymentFileLoad paymentFile, MessageMap errorMap) { 113 CustomerProfile customer = customerProfileService.get(paymentFile.getChart(), paymentFile.getUnit(), paymentFile.getSubUnit()); 114 if (customer == null) { 115 errorMap.putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.ERROR_PAYMENT_LOAD_INVALID_CUSTOMER, paymentFile.getChart(), paymentFile.getUnit(), paymentFile.getSubUnit()); 116 } 117 else { 118 if (!customer.isActive()) { 119 errorMap.putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.ERROR_PAYMENT_LOAD_INACTIVE_CUSTOMER, paymentFile.getChart(), paymentFile.getUnit(), paymentFile.getSubUnit()); 120 } 121 else { 122 paymentFile.setCustomer(customer); 123 } 124 } 125 } 126 127 /** 128 * Validates payment file trailer fields <li>Reconciles actual to expected payment count and totals</li> <li>Verifies the batch 129 * is not a duplicate</li> 130 * 131 * @param paymentFile payment file object 132 * @param errorMap map in which errors will be added to 133 */ 134 protected void processTrailerValidation(PaymentFileLoad paymentFile, MessageMap errorMap) { 135 // compare trailer payment count to actual count loaded 136 if (paymentFile.getActualPaymentCount() != paymentFile.getPaymentCount()) { 137 errorMap.putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.ERROR_PAYMENT_LOAD_PAYMENT_COUNT_MISMATCH, Integer.toString(paymentFile.getPaymentCount()), Integer.toString(paymentFile.getActualPaymentCount())); 138 } 139 140 // compare trailer total amount with actual total amount 141 if (paymentFile.getCalculatedPaymentTotalAmount().compareTo(paymentFile.getPaymentTotalAmount()) != 0) { 142 errorMap.putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.ERROR_PAYMENT_LOAD_PAYMENT_TOTAL_MISMATCH, paymentFile.getPaymentTotalAmount().toString(), paymentFile.getCalculatedPaymentTotalAmount().toString()); 143 } 144 145 // Check to see if this is a duplicate batch 146 Timestamp now = new Timestamp(paymentFile.getCreationDate().getTime()); 147 148 if (paymentFileLoadDao.isDuplicateBatch(paymentFile.getCustomer(), paymentFile.getPaymentCount(), paymentFile.getPaymentTotalAmount().bigDecimalValue(), now)) { 149 errorMap.putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.ERROR_PAYMENT_LOAD_DUPLICATE_BATCH); 150 } 151 } 152 153 /** 154 * Validates payment file groups <li>Checks number of note lines needed is not above the configured maximum allowed</li> <li> 155 * Verifies group total is not negative</li> <li>Verifies detail accounting total equals net payment amount</li> 156 * 157 * @param paymentFile payment file object 158 * @param errorMap map in which errors will be added to 159 */ 160 protected void processGroupValidation(PaymentFileLoad paymentFile, MessageMap errorMap) { 161 int groupCount = 0; 162 for (PaymentGroup paymentGroup : paymentFile.getPaymentGroups()) { 163 groupCount++; 164 165 int noteLineCount = 0; 166 int detailCount = 0; 167 168 // verify payee id and owner code if customer requires them to be filled in 169 if (paymentFile.getCustomer().getPayeeIdRequired() && StringUtils.isBlank(paymentGroup.getPayeeId())) { 170 errorMap.putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.ERROR_PAYMENT_LOAD_PAYEE_ID_REQUIRED, Integer.toString(groupCount)); 171 } 172 173 if (paymentFile.getCustomer().getOwnershipCodeRequired() && StringUtils.isBlank(paymentGroup.getPayeeOwnerCd())) { 174 errorMap.putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.ERROR_PAYMENT_LOAD_PAYEE_OWNER_CODE, Integer.toString(groupCount)); 175 } 176 177 // validate payee id type 178 if (StringUtils.isNotBlank(paymentGroup.getPayeeIdTypeCd())) { 179 PayeeType payeeType = (PayeeType) businessObjectService.findBySinglePrimaryKey(PayeeType.class, paymentGroup.getPayeeIdTypeCd()); 180 if (payeeType == null) { 181 errorMap.putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.ERROR_PAYMENT_LOAD_INVALID_PAYEE_ID_TYPE, Integer.toString(groupCount), paymentGroup.getPayeeIdTypeCd()); 182 } 183 } 184 185 // validate bank 186 String bankCode = paymentGroup.getBankCode(); 187 if (StringUtils.isNotBlank(bankCode)) { 188 Bank bank = bankService.getByPrimaryId(bankCode); 189 if (bank == null) { 190 errorMap.putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.ERROR_PAYMENT_LOAD_INVALID_BANK_CODE, Integer.toString(groupCount), bankCode); 191 } 192 else if (!bank.isActive()) { 193 errorMap.putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.ERROR_PAYMENT_LOAD_INACTIVE_BANK_CODE, Integer.toString(groupCount), bankCode); 194 } 195 } 196 197 KualiDecimal groupTotal = KualiDecimal.ZERO; 198 for (PaymentDetail paymentDetail : paymentGroup.getPaymentDetails()) { 199 detailCount++; 200 201 noteLineCount++; // Add a line to print the invoice number 202 noteLineCount = noteLineCount + paymentDetail.getNotes().size(); 203 204 if ((paymentDetail.getNetPaymentAmount() == null) && (!paymentDetail.isDetailAmountProvided())) { 205 paymentDetail.setNetPaymentAmount(paymentDetail.getAccountTotal()); 206 } 207 else if ((paymentDetail.getNetPaymentAmount() == null) && (paymentDetail.isDetailAmountProvided())) { 208 paymentDetail.setNetPaymentAmount(paymentDetail.getCalculatedPaymentAmount()); 209 } 210 211 // compare net to accounting segments 212 if (paymentDetail.getAccountTotal().compareTo(paymentDetail.getNetPaymentAmount()) != 0) { 213 errorMap.putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.ERROR_PAYMENT_LOAD_DETAIL_TOTAL_MISMATCH, Integer.toString(groupCount), Integer.toString(detailCount), paymentDetail.getAccountTotal().toString(), paymentDetail.getNetPaymentAmount().toString()); 214 } 215 216 // validate origin code if given 217 if (StringUtils.isNotBlank(paymentDetail.getFinancialSystemOriginCode())) { 218 OriginationCode originationCode = originationCodeService.getByPrimaryKey(paymentDetail.getFinancialSystemOriginCode()); 219 if (originationCode == null) { 220 errorMap.putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.ERROR_PAYMENT_LOAD_INVALID_ORIGIN_CODE, Integer.toString(groupCount), Integer.toString(detailCount), paymentDetail.getFinancialSystemOriginCode()); 221 } 222 } 223 224 // validate doc type if given 225 if (StringUtils.isNotBlank(paymentDetail.getFinancialDocumentTypeCode())) { 226 try { 227 if (!workflowInfoService.isCurrentActiveDocumentType(paymentDetail.getFinancialDocumentTypeCode())) { 228 errorMap.putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.ERROR_PAYMENT_LOAD_INVALID_DOC_TYPE, Integer.toString(groupCount), Integer.toString(detailCount), paymentDetail.getFinancialDocumentTypeCode()); 229 } 230 } 231 catch (WorkflowException e) { 232 throw new RuntimeException("error trying to validate document type: " + e.getMessage(), e); 233 } 234 } 235 236 groupTotal = groupTotal.add(paymentDetail.getNetPaymentAmount()); 237 } 238 239 // verify total for group is not negative 240 if (groupTotal.doubleValue() < 0) { 241 errorMap.putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.ERROR_PAYMENT_LOAD_NEGATIVE_GROUP_TOTAL, Integer.toString(groupCount)); 242 } 243 244 // check that the number of detail items and note lines will fit on a check stub 245 if (noteLineCount > getMaxNoteLines()) { 246 errorMap.putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.ERROR_PAYMENT_LOAD_MAX_NOTE_LINES, Integer.toString(groupCount), Integer.toString(noteLineCount), Integer.toString(getMaxNoteLines())); 247 } 248 } 249 } 250 251 /** 252 * @see org.kuali.kfs.pdp.service.PaymentFileValidationService#doSoftEdits(org.kuali.kfs.pdp.businessobject.PaymentFile) 253 */ 254 public List<String> doSoftEdits(PaymentFileLoad paymentFile) { 255 List<String> warnings = new ArrayList<String>(); 256 257 CustomerProfile customer = customerProfileService.get(paymentFile.getChart(), paymentFile.getUnit(), paymentFile.getSubUnit()); 258 259 // check payment amount does not exceed the configured threshold amount of this customer 260 if (paymentFile.getPaymentTotalAmount().compareTo(customer.getFileThresholdAmount()) > 0) { 261 addWarningMessage(warnings, PdpKeyConstants.MESSAGE_PAYMENT_LOAD_FILE_THRESHOLD, paymentFile.getPaymentTotalAmount().toString(), customer.getFileThresholdAmount().toString()); 262 paymentFile.setFileThreshold(true); 263 } 264 265 processGroupSoftEdits(paymentFile, customer, warnings); 266 267 return warnings; 268 } 269 270 /** 271 * Set defaults for group fields and do tax checks. 272 * 273 * @param paymentFile payment file object 274 * @param customer payment customer 275 * @param warnings <code>List</code> list of accumulated warning messages 276 */ 277 public void processGroupSoftEdits(PaymentFileLoad paymentFile, CustomerProfile customer, List<String> warnings) { 278 PaymentStatus openStatus = (PaymentStatus) businessObjectService.findBySinglePrimaryKey(PaymentStatus.class, PdpConstants.PaymentStatusCodes.OPEN); 279 280 for (PaymentGroup paymentGroup : paymentFile.getPaymentGroups()) { 281 paymentGroup.setBatchId(paymentFile.getBatchId()); 282 paymentGroup.setPaymentStatusCode(openStatus.getCode()); 283 paymentGroup.setPaymentStatus(openStatus); 284 paymentGroup.setPayeeName(paymentGroup.getPayeeName().toUpperCase()); 285 286 // Set defaults for missing information 287 defaultGroupIndicators(paymentGroup); 288 289 // Tax Group Requirements for automatic Holding 290 checkForTaxEmailRequired(paymentFile, paymentGroup, customer); 291 292 // do edits on detail lines 293 for (PaymentDetail paymentDetail : paymentGroup.getPaymentDetails()) { 294 paymentDetail.setPaymentGroupId(paymentGroup.getId()); 295 296 processDetailSoftEdits(paymentFile, customer, paymentDetail, warnings); 297 } 298 299 } 300 } 301 302 /** 303 * Set default fields on detail line and check amount against customer threshold. 304 * 305 * @param paymentFile payment file object 306 * @param customer payment customer 307 * @param paymentDetail <code>PaymentDetail</code> object to process 308 * @param warnings <code>List</code> list of accumulated warning messages 309 */ 310 protected void processDetailSoftEdits(PaymentFileLoad paymentFile, CustomerProfile customer, PaymentDetail paymentDetail, List<String> warnings) { 311 updateDetailAmounts(paymentDetail); 312 313 // Check net payment amount 314 KualiDecimal testAmount = paymentDetail.getNetPaymentAmount(); 315 if (testAmount.compareTo(customer.getPaymentThresholdAmount()) > 0) { 316 addWarningMessage(warnings, PdpKeyConstants.MESSAGE_PAYMENT_LOAD_DETAIL_THRESHOLD, testAmount.toString(), customer.getPaymentThresholdAmount().toString()); 317 paymentFile.setDetailThreshold(true); 318 paymentFile.getThresholdPaymentDetails().add(paymentDetail); 319 } 320 321 // set invoice date if it doesn't exist 322 if (paymentDetail.getInvoiceDate() == null) { 323 paymentDetail.setInvoiceDate(dateTimeService.getCurrentSqlDate()); 324 } 325 326 if (paymentDetail.getPrimaryCancelledPayment() == null) { 327 paymentDetail.setPrimaryCancelledPayment(Boolean.FALSE); 328 } 329 330 // do accounting edits 331 for (PaymentAccountDetail paymentAccountDetail : paymentDetail.getAccountDetail()) { 332 paymentAccountDetail.setPaymentDetailId(paymentDetail.getId()); 333 334 processAccountSoftEdits(paymentFile, customer, paymentAccountDetail, warnings); 335 } 336 } 337 338 /** 339 * Set default fields on account line and perform account field existence checks 340 * 341 * @param paymentFile payment file object 342 * @param customer payment customer 343 * @param paymentAccountDetail <code>PaymentAccountDetail</code> object to process 344 * @param warnings <code>List</code> list of accumulated warning messages 345 */ 346 protected void processAccountSoftEdits(PaymentFileLoad paymentFile, CustomerProfile customer, PaymentAccountDetail paymentAccountDetail, List<String> warnings) { 347 List<PaymentAccountHistory> changeRecords = paymentAccountDetail.getAccountHistory(); 348 349 // uppercase chart 350 paymentAccountDetail.setFinChartCode(paymentAccountDetail.getFinChartCode().toUpperCase()); 351 352 // only do accounting edits if required by customer 353 if (customer.getAccountingEditRequired()) { 354 // check account number 355 Account account = accountService.getByPrimaryId(paymentAccountDetail.getFinChartCode(), paymentAccountDetail.getAccountNbr()); 356 if (account == null) { 357 addWarningMessage(warnings, PdpKeyConstants.MESSAGE_PAYMENT_LOAD_INVALID_ACCOUNT, paymentAccountDetail.getFinChartCode(), paymentAccountDetail.getAccountNbr()); 358 359 KualiCodeBase objChangeCd = (KualiCodeBase) businessObjectService.findBySinglePrimaryKey(AccountingChangeCode.class, PdpConstants.AccountChangeCodes.INVALID_ACCOUNT); 360 replaceAccountingString(objChangeCd, changeRecords, customer, paymentAccountDetail); 361 } 362 else { 363 // check sub account code 364 if (StringUtils.isNotBlank(paymentAccountDetail.getSubAccountNbr())) { 365 SubAccount subAccount = subAccountService.getByPrimaryId(paymentAccountDetail.getFinChartCode(), paymentAccountDetail.getAccountNbr(), paymentAccountDetail.getSubAccountNbr()); 366 if (subAccount == null) { 367 addWarningMessage(warnings, PdpKeyConstants.MESSAGE_PAYMENT_LOAD_INVALID_SUB_ACCOUNT, paymentAccountDetail.getFinChartCode(), paymentAccountDetail.getAccountNbr(), paymentAccountDetail.getSubAccountNbr()); 368 369 KualiCodeBase objChangeCd = (KualiCodeBase) businessObjectService.findBySinglePrimaryKey(AccountingChangeCode.class, PdpConstants.AccountChangeCodes.INVALID_SUB_ACCOUNT); 370 changeRecords.add(newAccountHistory(PdpPropertyConstants.SUB_ACCOUNT_DB_COLUMN_NAME, KFSConstants.getDashSubAccountNumber(), paymentAccountDetail.getSubAccountNbr(), objChangeCd)); 371 372 paymentAccountDetail.setSubAccountNbr(KFSConstants.getDashSubAccountNumber()); 373 } 374 } 375 376 // check object code 377 ObjectCode objectCode = objectCodeService.getByPrimaryIdForCurrentYear(paymentAccountDetail.getFinChartCode(), paymentAccountDetail.getFinObjectCode()); 378 if (objectCode == null) { 379 addWarningMessage(warnings, PdpKeyConstants.MESSAGE_PAYMENT_LOAD_INVALID_OBJECT, paymentAccountDetail.getFinChartCode(), paymentAccountDetail.getFinObjectCode()); 380 381 KualiCodeBase objChangeCd = (KualiCodeBase) businessObjectService.findBySinglePrimaryKey(AccountingChangeCode.class, PdpConstants.AccountChangeCodes.INVALID_OBJECT); 382 replaceAccountingString(objChangeCd, changeRecords, customer, paymentAccountDetail); 383 } 384 385 // check sub object code 386 else if (StringUtils.isNotBlank(paymentAccountDetail.getFinSubObjectCode())) { 387 SubObjectCode subObjectCode = subObjectCodeService.getByPrimaryIdForCurrentYear(paymentAccountDetail.getFinChartCode(), paymentAccountDetail.getAccountNbr(), paymentAccountDetail.getFinObjectCode(), paymentAccountDetail.getFinSubObjectCode()); 388 if (subObjectCode == null) { 389 addWarningMessage(warnings, PdpKeyConstants.MESSAGE_PAYMENT_LOAD_INVALID_SUB_OBJECT, paymentAccountDetail.getFinChartCode(), paymentAccountDetail.getAccountNbr(), paymentAccountDetail.getFinObjectCode(), paymentAccountDetail.getFinSubObjectCode()); 390 391 KualiCodeBase objChangeCd = (KualiCodeBase) businessObjectService.findBySinglePrimaryKey(AccountingChangeCode.class, PdpConstants.AccountChangeCodes.INVALID_SUB_OBJECT); 392 changeRecords.add(newAccountHistory(PdpPropertyConstants.SUB_OBJECT_DB_COLUMN_NAME, KFSConstants.getDashFinancialSubObjectCode(), paymentAccountDetail.getFinSubObjectCode(), objChangeCd)); 393 394 paymentAccountDetail.setFinSubObjectCode(KFSConstants.getDashFinancialSubObjectCode()); 395 } 396 } 397 } 398 399 // check project code 400 if (StringUtils.isNotBlank(paymentAccountDetail.getProjectCode())) { 401 ProjectCode projectCode = projectCodeService.getByPrimaryId(paymentAccountDetail.getProjectCode()); 402 if (projectCode == null) { 403 addWarningMessage(warnings, PdpKeyConstants.MESSAGE_PAYMENT_LOAD_INVALID_PROJECT, paymentAccountDetail.getProjectCode()); 404 405 KualiCodeBase objChangeCd = (KualiCodeBase) businessObjectService.findBySinglePrimaryKey(AccountingChangeCode.class, PdpConstants.AccountChangeCodes.INVALID_PROJECT); 406 changeRecords.add(newAccountHistory(PdpPropertyConstants.PROJECT_DB_COLUMN_NAME, KFSConstants.getDashProjectCode(), paymentAccountDetail.getProjectCode(), objChangeCd)); 407 paymentAccountDetail.setProjectCode(KFSConstants.getDashProjectCode()); 408 } 409 } 410 } 411 412 // change nulls into ---'s for the fields that need it 413 if (StringUtils.isBlank(paymentAccountDetail.getFinSubObjectCode())) { 414 paymentAccountDetail.setFinSubObjectCode(KFSConstants.getDashFinancialSubObjectCode()); 415 } 416 417 if (StringUtils.isBlank(paymentAccountDetail.getSubAccountNbr())) { 418 paymentAccountDetail.setSubAccountNbr(KFSConstants.getDashSubAccountNumber()); 419 } 420 421 if (StringUtils.isBlank(paymentAccountDetail.getProjectCode())) { 422 paymentAccountDetail.setProjectCode(KFSConstants.getDashProjectCode()); 423 } 424 425 } 426 427 /** 428 * Replaces the entire accounting string with defaults from the customer profile. 429 * 430 * @param objChangeCd code indicating reason for change 431 * @param changeRecords <code>List</code> of <code>PaymentAccountHistory</code> records 432 * @param customer profile of payment customer 433 * @param paymentAccountDetail account detail record 434 */ 435 protected void replaceAccountingString(KualiCodeBase objChangeCd, List<PaymentAccountHistory> changeRecords, CustomerProfile customer, PaymentAccountDetail paymentAccountDetail) { 436 changeRecords.add(newAccountHistory(PdpPropertyConstants.CHART_DB_COLUMN_NAME, customer.getDefaultChartCode(), paymentAccountDetail.getFinChartCode(), objChangeCd)); 437 changeRecords.add(newAccountHistory(PdpPropertyConstants.ACCOUNT_DB_COLUMN_NAME, customer.getDefaultAccountNumber(), paymentAccountDetail.getAccountNbr(), objChangeCd)); 438 changeRecords.add(newAccountHistory(PdpPropertyConstants.SUB_ACCOUNT_DB_COLUMN_NAME, customer.getDefaultSubAccountNumber(), paymentAccountDetail.getSubAccountNbr(), objChangeCd)); 439 changeRecords.add(newAccountHistory(PdpPropertyConstants.OBJECT_DB_COLUMN_NAME, customer.getDefaultObjectCode(), paymentAccountDetail.getFinObjectCode(), objChangeCd)); 440 changeRecords.add(newAccountHistory(PdpPropertyConstants.SUB_OBJECT_DB_COLUMN_NAME, customer.getDefaultSubObjectCode(), paymentAccountDetail.getFinSubObjectCode(), objChangeCd)); 441 442 paymentAccountDetail.setFinChartCode(customer.getDefaultChartCode()); 443 paymentAccountDetail.setAccountNbr(customer.getDefaultAccountNumber()); 444 if (StringUtils.isNotBlank(customer.getDefaultSubAccountNumber())) { 445 paymentAccountDetail.setSubAccountNbr(customer.getDefaultSubAccountNumber()); 446 } 447 else { 448 paymentAccountDetail.setSubAccountNbr(KFSConstants.getDashSubAccountNumber()); 449 } 450 paymentAccountDetail.setFinObjectCode(customer.getDefaultObjectCode()); 451 if (StringUtils.isNotBlank(customer.getDefaultSubAccountNumber())) { 452 paymentAccountDetail.setFinSubObjectCode(customer.getDefaultSubObjectCode()); 453 } 454 else { 455 paymentAccountDetail.setFinSubObjectCode(KFSConstants.getDashFinancialSubObjectCode()); 456 } 457 } 458 459 /** 460 * Helper method to construct a new payment account history record 461 * 462 * @param attName name of field that has changed 463 * @param newValue new value for the field 464 * @param oldValue field value that was changed 465 * @param changeCode code indicating reason for change 466 * @return <code>PaymentAccountHistory</code> 467 */ 468 protected PaymentAccountHistory newAccountHistory(String attName, String newValue, String oldValue, KualiCodeBase changeCode) { 469 PaymentAccountHistory paymentAccountHistory = new PaymentAccountHistory(); 470 471 paymentAccountHistory.setAcctAttributeName(attName); 472 paymentAccountHistory.setAcctAttributeNewValue(newValue); 473 paymentAccountHistory.setAcctAttributeOrigValue(oldValue); 474 paymentAccountHistory.setAcctChangeDate(dateTimeService.getCurrentTimestamp()); 475 paymentAccountHistory.setAccountingChange((AccountingChangeCode) changeCode); 476 477 return paymentAccountHistory; 478 } 479 480 /** 481 * Sets null amount fields to 0 482 * 483 * @param paymentDetail <code>PaymentDetail</code> to update 484 */ 485 protected void updateDetailAmounts(PaymentDetail paymentDetail) { 486 KualiDecimal zero = KualiDecimal.ZERO; 487 488 if (paymentDetail.getInvTotDiscountAmount() == null) { 489 paymentDetail.setInvTotDiscountAmount(zero); 490 } 491 492 if (paymentDetail.getInvTotShipAmount() == null) { 493 paymentDetail.setInvTotShipAmount(zero); 494 } 495 496 if (paymentDetail.getInvTotOtherDebitAmount() == null) { 497 paymentDetail.setInvTotOtherDebitAmount(zero); 498 } 499 500 if (paymentDetail.getInvTotOtherCreditAmount() == null) { 501 paymentDetail.setInvTotOtherCreditAmount(zero); 502 } 503 504 // update the total payment amount with the amount from the accounts if null 505 if (paymentDetail.getNetPaymentAmount() == null) { 506 paymentDetail.setNetPaymentAmount(paymentDetail.getAccountTotal()); 507 } 508 509 if (paymentDetail.getOrigInvoiceAmount() == null) { 510 KualiDecimal amt = paymentDetail.getNetPaymentAmount(); 511 amt = amt.add(paymentDetail.getInvTotDiscountAmount()); 512 amt = amt.subtract(paymentDetail.getInvTotShipAmount()); 513 amt = amt.subtract(paymentDetail.getInvTotOtherDebitAmount()); 514 amt = amt.add(paymentDetail.getInvTotOtherCreditAmount()); 515 paymentDetail.setOrigInvoiceAmount(amt); 516 } 517 } 518 519 /** 520 * Sets null indicators to false 521 * 522 * @param paymentGroup <code>PaymentGroup</code> to update 523 */ 524 protected void defaultGroupIndicators(PaymentGroup paymentGroup) { 525 // combineGroups column does not accept null values, so it will never be null 526 /* 527 * if (paymentGroup.getCombineGroups() == null) { paymentGroup.setCombineGroups(Boolean.TRUE); } 528 */ 529 530 if (paymentGroup.getCampusAddress() == null) { 531 paymentGroup.setCampusAddress(Boolean.FALSE); 532 } 533 534 if (paymentGroup.getPymtAttachment() == null) { 535 paymentGroup.setPymtAttachment(Boolean.FALSE); 536 } 537 538 if (paymentGroup.getPymtSpecialHandling() == null) { 539 paymentGroup.setPymtSpecialHandling(Boolean.FALSE); 540 } 541 542 if (paymentGroup.getProcessImmediate() == null) { 543 paymentGroup.setProcessImmediate(Boolean.FALSE); 544 } 545 546 if (paymentGroup.getEmployeeIndicator() == null) { 547 paymentGroup.setEmployeeIndicator(Boolean.FALSE); 548 } 549 550 if (paymentGroup.getNraPayment() == null) { 551 paymentGroup.setNraPayment(Boolean.FALSE); 552 } 553 554 if (paymentGroup.getTaxablePayment() == null) { 555 paymentGroup.setTaxablePayment(Boolean.FALSE); 556 } 557 } 558 559 /** 560 * Checks whether payment status should be set to held and a tax email sent indicating so 561 * 562 * @param paymentFile payment file object 563 * @param paymentGroup <code>PaymentGroup</code> being checked 564 * @param customer payment customer 565 */ 566 protected void checkForTaxEmailRequired(PaymentFileLoad paymentFile, PaymentGroup paymentGroup, CustomerProfile customer) { 567 PaymentStatus heldForNRAEmployee = (PaymentStatus) businessObjectService.findBySinglePrimaryKey(PaymentStatus.class, PdpConstants.PaymentStatusCodes.HELD_TAX_NRA_EMPL_CD); 568 PaymentStatus heldForEmployee = (PaymentStatus) businessObjectService.findBySinglePrimaryKey(PaymentStatus.class, PdpConstants.PaymentStatusCodes.HELD_TAX_EMPLOYEE_CD); 569 PaymentStatus heldForNRA = (PaymentStatus) businessObjectService.findBySinglePrimaryKey(PaymentStatus.class, PdpConstants.PaymentStatusCodes.HELD_TAX_NRA_CD); 570 571 if (customer.getNraReview() && customer.getEmployeeCheck() && paymentGroup.getEmployeeIndicator().booleanValue() && paymentGroup.getNraPayment().booleanValue()) { 572 paymentGroup.setPaymentStatus(heldForNRAEmployee); 573 paymentFile.setTaxEmailRequired(true); 574 } 575 576 else if (customer.getEmployeeCheck() && paymentGroup.getEmployeeIndicator().booleanValue()) { 577 paymentGroup.setPaymentStatus(heldForEmployee); 578 paymentFile.setTaxEmailRequired(true); 579 } 580 581 else if (customer.getNraReview() && paymentGroup.getNraPayment().booleanValue()) { 582 paymentGroup.setPaymentStatus(heldForNRA); 583 paymentFile.setTaxEmailRequired(true); 584 } 585 } 586 587 /** 588 * Checks the payment date is not more than 30 days past or 30 days coming 589 * 590 * @param paymentGroup <code>PaymentGroup</code> being checked 591 * @param warnings <code>List</code> list of accumulated warning messages 592 */ 593 protected void checkGroupPaymentDate(PaymentGroup paymentGroup, List<String> warnings) { 594 Timestamp now = dateTimeService.getCurrentTimestamp(); 595 596 Calendar nowPlus30 = Calendar.getInstance(); 597 nowPlus30.setTime(now); 598 nowPlus30.add(Calendar.DATE, 30); 599 600 Calendar nowMinus30 = Calendar.getInstance(); 601 nowMinus30.setTime(now); 602 nowMinus30.add(Calendar.DATE, -30); 603 604 if (paymentGroup.getPaymentDate() != null) { 605 Calendar payDate = Calendar.getInstance(); 606 payDate.setTime(paymentGroup.getPaymentDate()); 607 608 if (payDate.before(nowMinus30)) { 609 addWarningMessage(warnings, PdpKeyConstants.MESSAGE_PAYMENT_LOAD_PAYDATE_OVER_30_DAYS_PAST, dateTimeService.toDateString(paymentGroup.getPaymentDate())); 610 } 611 612 if (payDate.after(nowPlus30)) { 613 addWarningMessage(warnings, PdpKeyConstants.MESSAGE_PAYMENT_LOAD_PAYDATE_OVER_30_DAYS_OUT, dateTimeService.toDateString(paymentGroup.getPaymentDate())); 614 } 615 } 616 else { 617 try { 618 paymentGroup.setPaymentDate(dateTimeService.convertToSqlDate(now)); 619 } 620 catch (ParseException e) { 621 throw new RuntimeException("Unable to parse current timestamp into sql date " + e.getMessage()); 622 } 623 } 624 } 625 626 /** 627 * @return system parameter value giving the maximum number of notes allowed. 628 */ 629 protected int getMaxNoteLines() { 630 String maxLines = parameterService.getParameterValue(KfsParameterConstants.PRE_DISBURSEMENT_ALL.class, PdpParameterConstants.MAX_NOTE_LINES); 631 if (StringUtils.isBlank(maxLines)) { 632 throw new RuntimeException("System parameter for max note lines is blank"); 633 } 634 635 return Integer.parseInt(maxLines); 636 } 637 638 /** 639 * Helper method for subsituting message parameters and adding the message to the warning list. 640 * 641 * @param warnings <code>List</code> of messages to add to 642 * @param messageKey resource key for message 643 * @param arguments message substitute parameters 644 */ 645 protected void addWarningMessage(List<String> warnings, String messageKey, String... arguments) { 646 String message = kualiConfigurationService.getPropertyString(messageKey); 647 warnings.add(MessageFormat.format(message, (Object[]) arguments)); 648 } 649 650 /** 651 * Sets the customerProfileService attribute value. 652 * 653 * @param customerProfileService The customerProfileService to set. 654 */ 655 public void setCustomerProfileService(CustomerProfileService customerProfileService) { 656 this.customerProfileService = customerProfileService; 657 } 658 659 /** 660 * Sets the paymentFileLoadDao attribute value. 661 * 662 * @param paymentFileLoadDao The paymentFileLoadDao to set. 663 */ 664 public void setPaymentFileLoadDao(PaymentFileLoadDao paymentFileLoadDao) { 665 this.paymentFileLoadDao = paymentFileLoadDao; 666 } 667 668 /** 669 * Sets the parameterService attribute value. 670 * 671 * @param parameterService The parameterService to set. 672 */ 673 public void setParameterService(ParameterService parameterService) { 674 this.parameterService = parameterService; 675 } 676 677 /** 678 * Sets the dateTimeService attribute value. 679 * 680 * @param dateTimeService The dateTimeService to set. 681 */ 682 public void setDateTimeService(DateTimeService dateTimeService) { 683 this.dateTimeService = dateTimeService; 684 } 685 686 /** 687 * Sets the accountService attribute value. 688 * 689 * @param accountService The accountService to set. 690 */ 691 public void setAccountService(AccountService accountService) { 692 this.accountService = accountService; 693 } 694 695 /** 696 * Sets the subAccountService attribute value. 697 * 698 * @param subAccountService The subAccountService to set. 699 */ 700 public void setSubAccountService(SubAccountService subAccountService) { 701 this.subAccountService = subAccountService; 702 } 703 704 /** 705 * Sets the objectCodeService attribute value. 706 * 707 * @param objectCodeService The objectCodeService to set. 708 */ 709 public void setObjectCodeService(ObjectCodeService objectCodeService) { 710 this.objectCodeService = objectCodeService; 711 } 712 713 /** 714 * Sets the subObjectCodeService attribute value. 715 * 716 * @param subObjectCodeService The subObjectCodeService to set. 717 */ 718 public void setSubObjectCodeService(SubObjectCodeService subObjectCodeService) { 719 this.subObjectCodeService = subObjectCodeService; 720 } 721 722 /** 723 * Sets the projectCodeService attribute value. 724 * 725 * @param projectCodeService The projectCodeService to set. 726 */ 727 public void setProjectCodeService(ProjectCodeService projectCodeService) { 728 this.projectCodeService = projectCodeService; 729 } 730 731 /** 732 * Sets the kualiConfigurationService attribute value. 733 * 734 * @param kualiConfigurationService The kualiConfigurationService to set. 735 */ 736 public void setKualiConfigurationService(KualiConfigurationService kualiConfigurationService) { 737 this.kualiConfigurationService = kualiConfigurationService; 738 } 739 740 /** 741 * Sets the bankService attribute value. 742 * 743 * @param bankService The bankService to set. 744 */ 745 public void setBankService(BankService bankService) { 746 this.bankService = bankService; 747 } 748 749 /** 750 * Sets the originationCodeService attribute value. 751 * 752 * @param originationCodeService The originationCodeService to set. 753 */ 754 public void setOriginationCodeService(OriginationCodeService originationCodeService) { 755 this.originationCodeService = originationCodeService; 756 } 757 758 /** 759 * Gets the workflowInfoService attribute. 760 * 761 * @return Returns the workflowInfoService. 762 */ 763 protected KualiWorkflowInfo getWorkflowInfoService() { 764 return workflowInfoService; 765 } 766 767 /** 768 * Sets the workflowInfoService attribute value. 769 * 770 * @param workflowInfoService The workflowInfoService to set. 771 */ 772 public void setWorkflowInfoService(KualiWorkflowInfo workflowInfoService) { 773 this.workflowInfoService = workflowInfoService; 774 } 775 776 /** 777 * Gets the businessObjectService attribute. 778 * 779 * @return Returns the businessObjectService. 780 */ 781 protected BusinessObjectService getBusinessObjectService() { 782 return businessObjectService; 783 } 784 785 /** 786 * Sets the businessObjectService attribute value. 787 * 788 * @param businessObjectService The businessObjectService to set. 789 */ 790 public void setBusinessObjectService(BusinessObjectService businessObjectService) { 791 this.businessObjectService = businessObjectService; 792 } 793 794 }