use of org.kuali.kfs.pdp.businessobject.PaymentDetail in project cu-kfs by CU-CommunityApps.
the class CuProcessPdpCancelPaidServiceImpl method processPdpPaids.
/**
* Overridden to process each payment detail in its own transaction.
* This implementation forces the current service to call a proxied version of itself,
* in order for Spring to handle the transactions properly on the per-payment handler method.
*
* @see org.kuali.kfs.pdp.batch.service.impl.ProcessPdpCancelPaidServiceImpl#processPdpPaids()
*/
@Override
public void processPdpPaids() {
LOG.debug("processPdpPaids() started");
CuProcessPdpCancelPaidService proxiedProcessPdpCancelPaidService = getProxiedProcessPdpCancelPaidService();
Date processDate = dateTimeService.getCurrentSqlDate();
List<ExtractionUnit> extractionUnits = getExtractionUnits();
Iterator<PaymentDetail> details = paymentDetailService.getUnprocessedPaidDetails(extractionUnits);
while (details.hasNext()) {
PaymentDetail paymentDetail = details.next();
proxiedProcessPdpCancelPaidService.processPdpPaid(paymentDetail, processDate);
}
}
use of org.kuali.kfs.pdp.businessobject.PaymentDetail in project cu-kfs by CU-CommunityApps.
the class CuPaymentFileValidationServiceImpl method processGroupValidation.
@Override
protected void processGroupValidation(PaymentFileLoad paymentFile, MessageMap errorMap) {
int groupCount = 0;
for (PaymentGroup paymentGroup : paymentFile.getPaymentGroups()) {
groupCount++;
int noteLineCount = 0;
int detailCount = 0;
// We've encountered Payment Files that have address lines exceeding the column size in DB table;
// so adding extra validation on payment group BO, especially the max length, based on DD definitions.
// Check that PaymentGroup String properties don't exceed maximum allowed length
checkPaymentGroupPropertyMaxLength(paymentGroup, errorMap);
// verify payee id and owner code if customer requires them to be filled in
if (paymentFile.getCustomer().getPayeeIdRequired() && StringUtils.isBlank(paymentGroup.getPayeeId())) {
LOG.debug("processGroupValidation, No payee");
errorMap.putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.ERROR_PAYMENT_LOAD_PAYEE_ID_REQUIRED, Integer.toString(groupCount));
}
if (paymentFile.getCustomer().getOwnershipCodeRequired() && StringUtils.isBlank(paymentGroup.getPayeeOwnerCd())) {
LOG.debug("processGroupValidation, no ownership code");
errorMap.putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.ERROR_PAYMENT_LOAD_PAYEE_OWNER_CODE, Integer.toString(groupCount));
}
// validate payee id type
if (StringUtils.isNotBlank(paymentGroup.getPayeeIdTypeCd())) {
PayeeType payeeType = businessObjectService.findBySinglePrimaryKey(PayeeType.class, paymentGroup.getPayeeIdTypeCd());
if (payeeType == null) {
LOG.debug("processGroupValidation, no payee type");
errorMap.putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.ERROR_PAYMENT_LOAD_INVALID_PAYEE_ID_TYPE, Integer.toString(groupCount), paymentGroup.getPayeeIdTypeCd());
}
}
// validate vendor id and customer institution number
if (paymentGroup.getPayeeId().split("-").length > 1) {
try {
paymentGroup.validateVendorIdAndCustomerInstitutionIdentifier();
} catch (RuntimeException e1) {
LOG.error("processGroupValidation, there was an error validating customer institution information", e1);
errorMap.putError(KFSConstants.GLOBAL_ERRORS, KFSKeyConstants.ERROR_BATCH_UPLOAD_PARSING, new String[] { e1.getMessage() });
}
} else {
LOG.debug("processGroupValidation, found a non vendor number payee ID: " + paymentGroup.getPayeeId());
if (cuPdpEmployeeService.shouldPayeeBeProcessedAsEmployeeForThisCustomer(paymentFile)) {
Person employee = findPerson(paymentGroup.getPayeeId());
if (ObjectUtils.isNull(employee)) {
LOG.error("processGroupValidation, unable to get a person from the employee id");
errorMap.putError(KFSConstants.GLOBAL_ERRORS, CUPdpKeyConstants.ERROR_PDP_PAYMENTLOAD_INVALID_EMPLOYEE_ID, paymentGroup.getPayeeId());
}
}
}
// validate bank
String bankCode = paymentGroup.getBankCode();
if (StringUtils.isNotBlank(bankCode)) {
Bank bank = bankService.getByPrimaryId(bankCode);
if (bank == null) {
LOG.debug("processGroupValidation, no bank");
errorMap.putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.ERROR_PAYMENT_LOAD_INVALID_BANK_CODE, Integer.toString(groupCount), bankCode);
} else if (!bank.isActive()) {
LOG.debug("processGroupValidation, bank isn't active");
errorMap.putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.ERROR_PAYMENT_LOAD_INACTIVE_BANK_CODE, Integer.toString(groupCount), bankCode);
}
}
KualiDecimal groupTotal = KualiDecimal.ZERO;
for (PaymentDetail paymentDetail : paymentGroup.getPaymentDetails()) {
detailCount++;
// CU Customization: Check max lengths on Payment Detail properties.
checkPaymentDetailPropertyMaxLength(paymentDetail, errorMap);
// Add a line to print the invoice number
noteLineCount++;
noteLineCount = noteLineCount + paymentDetail.getNotes().size();
if ((paymentDetail.getNetPaymentAmount() == null) && (!paymentDetail.isDetailAmountProvided())) {
paymentDetail.setNetPaymentAmount(paymentDetail.getAccountTotal());
} else if ((paymentDetail.getNetPaymentAmount() == null) && (paymentDetail.isDetailAmountProvided())) {
paymentDetail.setNetPaymentAmount(paymentDetail.getCalculatedPaymentAmount());
}
// compare net to accounting segments
if (paymentDetail.getAccountTotal().compareTo(paymentDetail.getNetPaymentAmount()) != 0) {
LOG.debug("processGroupValidation, account total (" + paymentDetail.getAccountTotal() + ") not equal to net amount total (" + paymentDetail.getNetPaymentAmount() + ")");
errorMap.putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.ERROR_PAYMENT_LOAD_DETAIL_TOTAL_MISMATCH, Integer.toString(groupCount), Integer.toString(detailCount), paymentDetail.getAccountTotal().toString(), paymentDetail.getNetPaymentAmount().toString());
}
// validate origin code if given
if (StringUtils.isNotBlank(paymentDetail.getFinancialSystemOriginCode())) {
OriginationCode originationCode = originationCodeService.getByPrimaryKey(paymentDetail.getFinancialSystemOriginCode());
if (originationCode == null) {
LOG.debug("processGroupValidation, origination code is null");
errorMap.putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.ERROR_PAYMENT_LOAD_INVALID_ORIGIN_CODE, Integer.toString(groupCount), Integer.toString(detailCount), paymentDetail.getFinancialSystemOriginCode());
}
}
// validate doc type if given
if (StringUtils.isNotBlank(paymentDetail.getFinancialDocumentTypeCode())) {
if (!documentTypeService.isActiveByName(paymentDetail.getFinancialDocumentTypeCode())) {
LOG.debug("processGroupValidation, " + paymentDetail.getFinancialDocumentTypeCode() + " is not active.");
errorMap.putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.ERROR_PAYMENT_LOAD_INVALID_DOC_TYPE, Integer.toString(groupCount), Integer.toString(detailCount), paymentDetail.getFinancialDocumentTypeCode());
}
}
groupTotal = groupTotal.add(paymentDetail.getNetPaymentAmount());
}
// verify total for group is not negative
if (groupTotal.doubleValue() < 0) {
LOG.debug("processGroupValidation, group total less than zero");
errorMap.putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.ERROR_PAYMENT_LOAD_NEGATIVE_GROUP_TOTAL, Integer.toString(groupCount));
}
// check that the number of detail items and note lines will fit on a check stub
if (noteLineCount > getMaxNoteLines()) {
LOG.debug("processGroupValidation, too many notes");
errorMap.putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.ERROR_PAYMENT_LOAD_MAX_NOTE_LINES, Integer.toString(groupCount), Integer.toString(noteLineCount), Integer.toString(getMaxNoteLines()));
}
if (LOG.isDebugEnabled()) {
LOG.debug("After processGroupValidation: " + printErrorMap(errorMap));
}
}
}
use of org.kuali.kfs.pdp.businessobject.PaymentDetail in project cu-kfs by CU-CommunityApps.
the class CuPaymentMaintenanceServiceImpl method cancelDisbursement.
@Override
public boolean cancelDisbursement(Integer paymentGroupId, Integer paymentDetailId, String note, Person user) {
LOG.debug("cancelDisbursement() started");
if (!pdpAuthorizationService.hasCancelPaymentPermission(user.getPrincipalId())) {
LOG.warn("cancelDisbursement() User " + user.getPrincipalId() + " does not have rights to cancel " + "payments. This should not happen unless user is URL spoofing.");
throw new RuntimeException("cancelDisbursement() User " + user.getPrincipalId() + " does not have rights to cancel payments. This should not happen unless user is URL spoofing.");
}
PaymentGroup paymentGroup = this.paymentGroupService.get(paymentGroupId);
if (paymentGroup == null) {
LOG.debug("cancelDisbursement() Disbursement not found; throw exception.");
GlobalVariables.getMessageMap().putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.PaymentDetail.ErrorMessages.ERROR_DISBURSEMENT_NOT_FOUND);
return false;
}
// get the target PaymentGroup info
PaymentDetail targetPd = getPaymentDetail(paymentDetailId);
KualiInteger targetGroupId = targetPd.getPaymentGroupId();
PaymentGroup targetPg = getPaymentGroup(targetGroupId);
String targetDvTypeCode = targetPg.getDisbursementTypeCode();
String targetDvBankCode = targetPg.getBankCode();
String paymentStatus = paymentGroup.getPaymentStatus().getCode();
if (!(PdpConstants.PaymentStatusCodes.CANCEL_DISBURSEMENT.equals(paymentStatus))) {
if (((PdpConstants.PaymentStatusCodes.EXTRACTED.equals(paymentStatus)) && (ObjectUtils.isNotNull(paymentGroup.getDisbursementDate()))) || (PdpConstants.PaymentStatusCodes.PENDING_ACH.equals(paymentStatus))) {
LOG.debug("cancelDisbursement() Payment status is " + paymentStatus + "; continue with cancel.");
List<PaymentGroup> allDisbursementPaymentGroups = this.paymentGroupService.getByDisbursementNumber(paymentGroup.getDisbursementNbr().intValue());
for (PaymentGroup element : allDisbursementPaymentGroups) {
// should be the same DV type and the same bank
if (!(element.getDisbursementTypeCode().equalsIgnoreCase(targetDvTypeCode) && element.getBankCode().equalsIgnoreCase(targetDvBankCode))) {
continue;
}
PaymentGroupHistory pgh = new PaymentGroupHistory();
if (!element.getPaymentDetails().get(0).isDisbursementActionAllowed()) {
LOG.warn("cancelDisbursement() Payment does not allow disbursement action. This should " + "not happen unless user is URL spoofing.");
throw new RuntimeException("cancelDisbursement() Payment does not allow disbursement action. " + "This should not happen unless user is URL spoofing.");
}
if ((ObjectUtils.isNotNull(element.getDisbursementType())) && (element.getDisbursementType().getCode().equals(PdpConstants.DisbursementTypeCodes.CHECK))) {
pgh.setPmtCancelExtractStat(Boolean.FALSE);
}
changeStatus(element, PdpConstants.PaymentStatusCodes.CANCEL_DISBURSEMENT, PdpConstants.PaymentChangeCodes.CANCEL_DISBURSEMENT, note, user, pgh);
glPendingTransactionService.generateCancellationGeneralLedgerPendingEntry(element);
// set primary cancel indicator for EPIC to use
// these payment details will be canceled when running processPdpCancelAndPaidJOb
Map<String, KualiInteger> primaryKeys = new HashMap<>();
primaryKeys.put(PdpPropertyConstants.PaymentDetail.PAYMENT_DETAIL_PAYMENT_GROUP_ID, element.getId());
// cancel all payment details for payment group
List<PaymentDetail> pds = (List<PaymentDetail>) this.businessObjectService.findMatching(PaymentDetail.class, primaryKeys);
if (pds != null && !pds.isEmpty()) {
for (PaymentDetail pd : pds) {
pd.setPrimaryCancelledPayment(Boolean.TRUE);
this.businessObjectService.save(pd);
}
}
}
LOG.debug("cancelDisbursement() Disbursement cancelled; exit method.");
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("cancelDisbursement() Payment status is " + paymentStatus + " and disbursement date is " + paymentGroup.getDisbursementDate() + "; cannot cancel payment in this status");
}
GlobalVariables.getMessageMap().putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.PaymentDetail.ErrorMessages.ERROR_DISBURSEMENT_INVALID_TO_CANCEL);
return false;
}
} else {
LOG.debug("cancelDisbursement() Disbursement has already been cancelled; exit method.");
}
return true;
}
use of org.kuali.kfs.pdp.businessobject.PaymentDetail in project cu-kfs by CU-CommunityApps.
the class CuExtractPaymentServiceImpl method writeExtractAchFile.
@Override
protected void writeExtractAchFile(PaymentStatus extractedStatus, String filename, Date processDate, SimpleDateFormat sdf) {
BufferedWriter os = null;
try {
List<String> notificationEmailAddresses = this.getBankPaymentFileNotificationEmailAddresses();
// Writes out the BNY Mellon Fast Track formatted file for ACH payments. We need to do this first since the status is set in this method which
// causes the writeExtractAchFileMellonBankFastTrack method to not find anything.
writeExtractAchFileMellonBankFastTrack(extractedStatus, filename, processDate, sdf, notificationEmailAddresses);
// totals for summary
Map<String, Integer> unitCounts = new HashMap<String, Integer>();
Map<String, KualiDecimal> unitTotals = new HashMap<String, KualiDecimal>();
Iterator iter = paymentGroupService.getByDisbursementTypeStatusCode(PdpConstants.DisbursementTypeCodes.ACH, PdpConstants.PaymentStatusCodes.PENDING_ACH);
if (iter.hasNext()) {
OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(filename), StandardCharsets.UTF_8);
os = new BufferedWriter(writer);
os.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
writeOpenTag(os, 0, "achPayments");
while (iter.hasNext()) {
PaymentGroup paymentGroup = (PaymentGroup) iter.next();
if (!testMode) {
paymentGroup.setDisbursementDate(new java.sql.Date(processDate.getTime()));
paymentGroup.setPaymentStatus(extractedStatus);
businessObjectService.save(paymentGroup);
}
writePayeeSpecificsToAchFile(os, paymentGroup, processDate, sdf);
// Write all payment level information
writeOpenTag(os, 4, "payments");
List<PaymentDetail> pdList = paymentGroup.getPaymentDetails();
for (PaymentDetail paymentDetail : pdList) {
writePaymentDetailToAchFile(os, paymentGroup, paymentDetail, unitCounts, unitTotals, sdf);
}
writeCloseTag(os, 4, "payments");
writeCloseTag(os, 2, "ach");
}
writeCloseTag(os, 0, "achPayments");
paymentFileEmailService.sendAchSummaryEmail(unitCounts, unitTotals, dateTimeService.getCurrentDate());
}
} catch (IOException ie) {
LOG.error("extractAchPayments() Problem reading file: " + filename, ie);
throw new IllegalArgumentException("Error writing to output file: " + ie.getMessage());
} finally {
// Close file
if (os != null) {
try {
os.close();
} catch (IOException ie) {
// Not much we can do now
}
}
}
}
use of org.kuali.kfs.pdp.businessobject.PaymentDetail in project cu-kfs by CU-CommunityApps.
the class CuExtractPaymentServiceImpl method writeExtractBundledAchFile.
/**
* A custom method that goes through and extracts all pending ACH payments and bundles them by payee/disbursement nbr.
* Changes made to this method due to re-factoring the code so that common pieces could be used
* by both ExtractPaymentServiceImpl.writeExtractAchFile and AchBundlerExtractPaymentServiceImpl.writeExtractBundledAchFile
* as well as incorporating the Mellon file creation.
* --Added the call to method writeExtractAchFileMellonBankFastTrack
* --Added the call to writePayeeSpecificsToAchFile for re-factored code
* --Added the call to writePaymentDetailToAchFile for re-factored code
* --Made the "finally" clause match the ExtractPaymentServiceImpl.writeExtractAchFile finally so that the XML files are named the same regardless of which routine is invoked.
* --Added call to get the parameterized bank notification email addresses
*
* @param extractedStatus
* @param filename
* @param processDate
* @param sdf
*/
protected void writeExtractBundledAchFile(PaymentStatus extractedStatus, String filename, Date processDate, SimpleDateFormat sdf) {
LOG.info("writeExtractBundledAchFile started.");
BufferedWriter os = null;
try {
List<String> notificationEmailAddresses = getBankPaymentFileNotificationEmailAddresses();
// Writes out the BNY Mellon Fast Track formatted file for ACH payments.
// We need to do this first since the status is set in this method which
// causes the writeExtractAchFileMellonBankFastTrack method to not find anything.
writeExtractAchFileMellonBankFastTrack(extractedStatus, filename, processDate, sdf, notificationEmailAddresses);
// totals for summary
Map<String, Integer> unitCounts = new HashMap<String, Integer>();
Map<String, KualiDecimal> unitTotals = new HashMap<String, KualiDecimal>();
os = new BufferedWriter(new FileWriter(filename, StandardCharsets.UTF_8));
os.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
writeOpenTag(os, 0, "achPayments");
HashSet<String> bankCodes = getAchBundlerHelperService().getDistinctBankCodesForPendingAchPayments();
for (String bankCode : bankCodes) {
HashSet<Integer> disbNbrs = getAchBundlerHelperService().getDistinctDisbursementNumbersForPendingAchPaymentsByBankCode(bankCode);
for (Iterator<Integer> iter = disbNbrs.iterator(); iter.hasNext(); ) {
Integer disbursementNbr = iter.next();
boolean first = true;
KualiDecimal totalNetAmount = new KualiDecimal(0);
// this seems wasteful, but since the total net amount is needed on the first payment detail...it's needed
Iterator<PaymentDetail> i2 = getAchBundlerHelperService().getPendingAchPaymentDetailsByDisbursementNumberAndBank(disbursementNbr, bankCode);
while (i2.hasNext()) {
PaymentDetail pd = i2.next();
totalNetAmount = totalNetAmount.add(pd.getNetPaymentAmount());
}
Iterator<PaymentDetail> paymentDetails = getAchBundlerHelperService().getPendingAchPaymentDetailsByDisbursementNumberAndBank(disbursementNbr, bankCode);
while (paymentDetails.hasNext()) {
PaymentDetail paymentDetail = paymentDetails.next();
PaymentGroup paymentGroup = paymentDetail.getPaymentGroup();
if (!testMode) {
paymentGroup.setDisbursementDate(new java.sql.Date(processDate.getTime()));
paymentGroup.setPaymentStatus(extractedStatus);
businessObjectService.save(paymentGroup);
}
if (first) {
writePayeeSpecificsToAchFile(os, paymentGroup, processDate, sdf);
writeOpenTag(os, 4, "payments");
}
writePaymentDetailToAchFile(os, paymentGroup, paymentDetail, unitCounts, unitTotals, sdf);
first = false;
}
writeCloseTag(os, 4, "payments");
// open for this tag is in method writePayeeSpecificsToAchFile
writeCloseTag(os, 2, "ach");
}
}
writeCloseTag(os, 0, "achPayments");
getPaymentFileEmailService().sendAchSummaryEmail(unitCounts, unitTotals, dateTimeService.getCurrentDate());
} catch (IOException ie) {
LOG.error("MOD: extractAchFile() Problem reading file: " + filename, ie);
throw new IllegalArgumentException("Error writing to output file: " + ie.getMessage());
} finally {
// Close file
if (os != null) {
try {
os.close();
// Need to do this at the end to indicate that the file is NOT USED after it is closed.
renameFile(filename, filename + ".NOT_USED");
} catch (IOException ie) {
// Not much we can do now
LOG.error("IOException encountered in writeExtractBundledAchFile. Message is: " + ie.getMessage());
}
}
}
}
Aggregations