use of org.kuali.kfs.pdp.businessobject.PaymentGroup 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));
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());
}
}
}
}
use of org.kuali.kfs.pdp.businessobject.PaymentGroup 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), "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.PaymentGroup in project cu-kfs by CU-CommunityApps.
the class CuPaymentFileServiceImpl method checkForInactiveVendors.
/**
* Checks whether any of the batch load's payment groups reference inactive vendors,
* and sends warning emails appropriately if so.
*
* @param paymentGroups The payment groups that were loaded.
* @param customer The customer's profile.
*/
private void checkForInactiveVendors(List<PaymentGroup> paymentGroups, CustomerProfile customer) {
final int MESSAGE_START_SIZE = 300;
StringBuilder inactiveVendorsMessage = new StringBuilder(MESSAGE_START_SIZE);
for (PaymentGroup paymentGroup : paymentGroups) {
// Determine whether the payment group's vendor is inactive.
VendorDetail vendor = vendorService.getVendorDetail(paymentGroup.getPayeeId());
if (vendor != null && !vendor.isActiveIndicator()) {
// If vendor is inactive, then append warning text to final email message.
LOG.warn("Found payment group with inactive vendor payee. Payment Group ID: " + paymentGroup.getId() + ", Vendor ID: " + paymentGroup.getPayeeId());
String warnMessageStart = getStartOfVendorInactiveMessage(vendor);
if (inactiveVendorsMessage.length() == 0) {
// Add header if necessary.
inactiveVendorsMessage.append("The PDP feed submitted by your unit includes payments to inactive vendors. ").append("Action is needed on your part to rectify the situation for future payments. ").append("Review the inactive reason to determine action needed. Details follow:\n\n");
}
// Append payment detail information to the message. (As per the payment file XSD, there should be at least one detail.)
for (PaymentDetail paymentDetail : paymentGroup.getPaymentDetails()) {
inactiveVendorsMessage.append(warnMessageStart).append("Customer Payment Doc Nbr: ").append(paymentDetail.getCustPaymentDocNbr()).append('\n').append("Payment Group ID: ").append(paymentDetail.getPaymentGroupId()).append('\n').append("Payment Date: ").append(paymentGroup.getPaymentDate()).append('\n').append("Amount: ").append(paymentDetail.getNetPaymentAmount()).append("\n\n");
}
}
}
// If one or more inactive vendors were found, then send notification emails to warn about their presence.
if (inactiveVendorsMessage.length() > 0) {
sendInactiveVendorsMessage(customer, inactiveVendorsMessage.toString());
}
}
use of org.kuali.kfs.pdp.businessobject.PaymentGroup 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, CUKFSKeyConstants.ERROR_BATCH_UPLOAD_PARSING_XML, 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++;
// 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.PaymentGroup 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;
}
Aggregations