use of org.kuali.kfs.pdp.businessobject.PaymentGroup in project cu-kfs by CU-CommunityApps.
the class CuExtractPaymentServiceImpl method writeExtractCheckFileMellonBankFastTrack.
// This method is called by the method that generates the XML file for checks to be printed by BNY Mellon
protected void writeExtractCheckFileMellonBankFastTrack(PaymentStatus extractedStatus, PaymentProcess p, String filename, Integer processId, List<String> notificationEmailAddresses) {
// Used in the Fast Track file HEADER record
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
// Used in the Fast Track file PAY01000 record
SimpleDateFormat sdfPAY1000Rec = new SimpleDateFormat("yyyyMMdd");
// Used in the issuance file
SimpleDateFormat sdfIDate = new SimpleDateFormat("yyMMdd");
// Used in the issuance file (NO SECONDS)
SimpleDateFormat sdfITime = new SimpleDateFormat("HHmm");
Date processDate = dateTimeService.getCurrentDate();
BufferedWriter os = null;
BufferedWriter osI = null;
// column delimiter: Per BNY Mellon FastTrack spec, your choices are: "^" or ",". If you change this make sure you change the associated name on the next line!
String cDelim = "^";
// column delimiter name: Per BNY Mellon FastTrack spec, your choices are: FFCARET and FFCOMMA for variable record types
String cDname = "FFCARET";
// record type: Per BNY Mellon's FastTrack spec, can be either V for variable or F for fixed.
String hdrRecType = "V";
// For Mellon Fast Track files - indicates whether the generated file is for (T)est or for (P)roduction
String testIndicator;
String ourBankAccountNumber = "";
String ourBankRoutingNumber = "";
String subUnitCode = "";
String divisionCode = "";
boolean specialHandlingCode = false;
boolean attachmentCode = false;
boolean immediateCheckCode = false;
String PreparerInfoText = "";
String altAddrSendTo = "";
String altAddrAddr1 = "";
String altAddrAddr2 = "";
String altAddrAddr3 = "";
String altAddrAddr4 = "";
String altAddrCity = "";
String altAddrState = "";
String altAddrZip = "";
String altCountryName = "";
// this is needed because the notes combine these into one field
String altAddrCityStateZip = "";
String altRefQualifer = "";
int NumOfAltAddressLines = 0;
int SendToPrefLength = 0;
// Note lines that are not the alternate address
String RefDesc1 = "";
// Note lines that are not the alternate address
String RefDesc2 = "";
// Note lines that are not the alternate address
String RefDesc3 = "";
// Note lines that are not the alternate address
String RefDesc4 = "";
String FirstNoteAfterAddressInfo = "";
String SecondNoteAfterAddressInfo = "";
String ThirdNoteAfterAddressInfo = "";
String Ref1Qualifier = "";
String Ref2Qualifier = "";
String Ref3Qualifier = "";
String Ref4Qualifier = "";
String dateQualifier = "";
// Filename for the Fast Track file generated by this method
String ftFilename = "";
// Filename for the issuance (or account reconciliation) file generated by this method for immediate payments
String arFilename = "";
// The total number of "add" issues (see record #6)
int arNumOfAddIssues = 0;
// The total number of issuance records
int numOfIssuanceRecords = 0;
// The total dollar amount of the add issues for the issuance file.
KualiDecimal arTotalOfAddIssues = KualiDecimal.ZERO;
boolean wroteMellonIssuanceHeaderRecords = false;
boolean wroteMellonFastTrackHeaderRecords = false;
CustomerProfile cp = null;
String sCountryName = "";
String CheckNumber = "";
boolean MissingCommaFromSpecialHandlingAddress = false;
int totalRecordCount = 0;
KualiDecimal totalPaymentAmounts = KualiDecimal.ZERO;
try {
// Establish whether we are in a TEST or PRODUCTION environment. This will change the indicator in the Mellon header record
if (isProduction())
testIndicator = "P";
else
testIndicator = "T";
// Change the filename so that it ends in .txt instead of .xml
ftFilename = filename.replace(".xml", ".txt");
arFilename = ftFilename.replace("check", "check_immediate");
// Obtain the bank account number from the check information provided
List<String> bankCodes1 = paymentGroupService.getDistinctBankCodesForProcessAndType(processId, PdpConstants.DisbursementTypeCodes.CHECK);
if (!bankCodes1.isEmpty()) {
List<Integer> disbNbrs1 = paymentGroupService.getDisbursementNumbersByDisbursementTypeAndBankCode(processId, PdpConstants.DisbursementTypeCodes.CHECK, bankCodes1.get(0));
if (!disbNbrs1.isEmpty()) {
Iterator<PaymentDetail> myPds = paymentDetailService.getByDisbursementNumber(disbNbrs1.get(0));
if (myPds.hasNext()) {
PaymentDetail myPd = myPds.next();
PaymentGroup myPg = myPd.getPaymentGroup();
if (ObjectUtils.isNotNull(myPg)) {
ourBankAccountNumber = myPg.getBank().getBankAccountNumber().replace("-", "");
ourBankRoutingNumber = myPg.getBank().getBankRoutingNumber();
} else {
LOG.error("No Payment group information exists for requisition number: " + myPd.getRequisitionNbr() + ". Payee name is: " + myPg.getPayeeName());
throw new Exception("No Payment group information exists for requisition number: " + myPd.getRequisitionNbr() + ". Payee name is: " + myPg.getPayeeName());
}
}
}
}
// At this point we will start looping through all the checks and write the PAY01000 record (one for each payee),
// two (2) PDT2010 records (one for the Payer & Payee) and as many REM3020 records as needed for each amount being
// paid to this payee.
List<String> bankCodes = paymentGroupService.getDistinctBankCodesForProcessAndType(processId, PdpConstants.DisbursementTypeCodes.CHECK);
for (String bankCode : bankCodes) {
List<Integer> disbNbrs = paymentGroupService.getDisbursementNumbersByDisbursementTypeAndBankCode(processId, PdpConstants.DisbursementTypeCodes.CHECK, bankCode);
for (Iterator<Integer> iter = disbNbrs.iterator(); iter.hasNext(); ) {
Integer disbursementNbr = iter.next();
// If this payee has multiple checks coming to them, this ensures we only generate 1 PAY01000 record
boolean first = true;
KualiDecimal totalNetAmount = new KualiDecimal(0);
// We continue to need this for the FastTrack file as well since the total net amount is needed on the PAY01000 record for each payee
Iterator<PaymentDetail> i2 = paymentDetailService.getByDisbursementNumber(disbursementNbr, processId, PdpConstants.DisbursementTypeCodes.CHECK, bankCode);
while (i2.hasNext()) {
PaymentDetail pd = i2.next();
totalNetAmount = totalNetAmount.add(pd.getNetPaymentAmount());
}
Iterator<PaymentDetail> paymentDetails = paymentDetailService.getByDisbursementNumber(disbursementNbr, processId, PdpConstants.DisbursementTypeCodes.CHECK, bankCode);
while (paymentDetails.hasNext()) {
PaymentDetail pd = paymentDetails.next();
PaymentGroup pg = pd.getPaymentGroup();
// We save these values to the DB AFTER we know that this check has passed all validation and has actually been written
pg.setDisbursementDate(new java.sql.Date(processDate.getTime()));
pg.setPaymentStatus(extractedStatus);
// Get immediate (a.k.a. local print, a.k.a. manual) check indicator
immediateCheckCode = pg.getProcessImmediate();
// If it exists, get the check (a.k.a. disbursement number)
CheckNumber = (ObjectUtils.isNotNull(pg.getDisbursementNbr()) ? pg.getDisbursementNbr().toString() : "");
// Parse Notes
Iterator<PaymentNoteText> ix = pd.getNotes().iterator();
NumOfAltAddressLines = 0;
altAddrSendTo = "";
altAddrAddr1 = "";
altAddrAddr2 = "";
altAddrAddr3 = "";
altAddrAddr4 = "";
altAddrCity = "";
altAddrState = "";
altAddrZip = "";
altCountryName = "";
altAddrCityStateZip = "";
PreparerInfoText = "";
SendToPrefLength = 0;
FirstNoteAfterAddressInfo = "";
SecondNoteAfterAddressInfo = "";
ThirdNoteAfterAddressInfo = "";
MissingCommaFromSpecialHandlingAddress = false;
while (ix.hasNext()) {
PaymentNoteText note = (PaymentNoteText) ix.next();
String NoteLine = note.getCustomerNoteText();
if (NoteLine.contains(CuDisbursementVoucherConstants.DV_EXTRACT_NOTE_PREFIX_PREPARER)) {
SendToPrefLength = CuDisbursementVoucherConstants.DV_EXTRACT_NOTE_PREFIX_SPECIAL_HANDLING_NAME.length();
PreparerInfoText = NoteLine;
} else if (NoteLine.contains(CuDisbursementVoucherConstants.DV_EXTRACT_NOTE_PREFIX_SPECIAL_HANDLING_NAME)) {
SendToPrefLength = CuDisbursementVoucherConstants.DV_EXTRACT_NOTE_PREFIX_SPECIAL_HANDLING_NAME.length();
altAddrSendTo = NoteLine.substring(SendToPrefLength);
NumOfAltAddressLines = NumOfAltAddressLines + 1;
} else if (NoteLine.contains(CuDisbursementVoucherConstants.DV_EXTRACT_NOTE_PREFIX_SPECIAL_HANDLING_ADDRESS1)) {
SendToPrefLength = CuDisbursementVoucherConstants.DV_EXTRACT_NOTE_PREFIX_SPECIAL_HANDLING_ADDRESS1.length();
altAddrAddr1 = NoteLine.substring(SendToPrefLength);
NumOfAltAddressLines = NumOfAltAddressLines + 1;
} else if (NoteLine.contains(CuDisbursementVoucherConstants.DV_EXTRACT_NOTE_PREFIX_SPECIAL_HANDLING_ADDRESS2)) {
SendToPrefLength = CuDisbursementVoucherConstants.DV_EXTRACT_NOTE_PREFIX_SPECIAL_HANDLING_ADDRESS2.length();
altAddrAddr2 = NoteLine.substring(SendToPrefLength);
NumOfAltAddressLines = NumOfAltAddressLines + 1;
} else if (NoteLine.contains(CuDisbursementVoucherConstants.DV_EXTRACT_NOTE_PREFIX_SPECIAL_HANDLING_ADDRESS3)) {
SendToPrefLength = CuDisbursementVoucherConstants.DV_EXTRACT_NOTE_PREFIX_SPECIAL_HANDLING_ADDRESS3.length();
if (NoteLine.contains(",")) {
altAddrCityStateZip = NoteLine.substring(SendToPrefLength);
// sTemp will be the string we modify as we go along
String sTemp = altAddrCityStateZip;
int spaceForZip = 0;
spaceForZip = sTemp.lastIndexOf(" ");
if (spaceForZip == -1) {
// ZIP is missing so set it to "" per discussion with Marcia
altAddrZip = "";
LOG.warn("WARNING: No zip code was provided. Changing it to blanks for check number: " + CheckNumber);
} else {
if (sTemp.substring(spaceForZip).trim().toLowerCase().equals("null")) {
// Use the space between the state and zip to obtain the "null" zip
altAddrZip = sTemp.substring(spaceForZip);
// Remove the "null" zip characters from sTemp
sTemp = sTemp.replace(altAddrZip, "");
// Since ZIP is missing set it to "" per discussion with Marcia
altAddrZip = "";
LOG.warn("WARNING: No zip code was provided. Changing it to blanks for check number: " + CheckNumber);
} else {
// Use the space between the state and zip to obtain the zip
altAddrZip = sTemp.substring(spaceForZip);
// Remove the space + Zip characters from sTemp
sTemp = sTemp.replace(altAddrZip, "");
// Trim any leading or trailing blanks
altAddrZip = altAddrZip.trim();
}
}
// Use the comma to find the where the state starts
altAddrState = sTemp.substring(sTemp.lastIndexOf(",") + 1);
// Remove what we found from sTemp
sTemp = sTemp.replace(altAddrState, "");
// Trim any leading or trailing blanks
altAddrState = altAddrState.trim();
// There should only be one comma. If commas are in the city name, that will be an issue
altAddrCity = sTemp.replace(",", "");
// Trim any leading or trailing blanks.
altAddrCity = altAddrCity.trim();
NumOfAltAddressLines = NumOfAltAddressLines + 1;
} else {
// We can't find a comma, so we won't know where the city ends and the state begins so as per discussion,
// log it as a warning and move on to the next NoteLine because we may have notes that we want to process.
LOG.warn("WARNING: No comma was provided separating the city and state for check number: " + CheckNumber);
MissingCommaFromSpecialHandlingAddress = true;
continue;
}
} else // if ( NoteLine.contains(CuDisbursementVoucherConstants.DV_EXTRACT_NOTE_PREFIX_SPECIAL_HANDLING_ADDRESS3) )
// Retrieve up to 3 subsequent note lines and only the first 72 characters as per the BNY Mellon spec.
{
// the next line until we either run out of lines or reach the max number to take (which is 3).
if (NoteLine.length() >= 2) {
if (NoteLine.substring(0, 2).contains(CuDisbursementVoucherConstants.DV_EXTRACT_TYPED_NOTE_PREFIX_IDENTIFIER)) {
// Trim the first two characters from the note and assign it as the first user typed note line
NoteLine = NoteLine.substring(2);
FirstNoteAfterAddressInfo = (NoteLine.length() <= 72) ? NoteLine : NoteLine.substring(0, 72);
// See if we have a second user typed note line. If so, then get it.
if (ix.hasNext()) {
note = (PaymentNoteText) ix.next();
NoteLine = note.getCustomerNoteText();
if (NoteLine.length() >= 2) {
if (NoteLine.substring(0, 2).contains(CuDisbursementVoucherConstants.DV_EXTRACT_TYPED_NOTE_PREFIX_IDENTIFIER)) {
NoteLine = NoteLine.substring(2);
SecondNoteAfterAddressInfo = (NoteLine.length() <= 72) ? NoteLine : NoteLine.substring(0, 72);
// Try to get the third user typed note line
if (ix.hasNext()) {
note = (PaymentNoteText) ix.next();
NoteLine = note.getCustomerNoteText();
if (NoteLine.length() >= 2) {
if (NoteLine.substring(0, 2).contains(CuDisbursementVoucherConstants.DV_EXTRACT_TYPED_NOTE_PREFIX_IDENTIFIER)) {
NoteLine = NoteLine.substring(2);
ThirdNoteAfterAddressInfo = (NoteLine.length() <= 72) ? NoteLine : NoteLine.substring(0, 72);
// Break here because the Mellon spec only allows us to use the first three user typed note lines
break;
} else
// Since we're on our potentially last note if this isn't a user types note, then we're done with the while loop
break;
} else
// Since this is the last potential user note and it doesn't contain :: break out of the while loop
break;
} else
// Out of all notes, so break out of the while loop
break;
} else
// Getting here means that we ran into a note line that doesn't have the :: in front. Continuing will over write the first notes we did capture, so
break;
} else
// Interspersed system generated notes can't occur (yet) so break out since we've already processed 1 user entered note.
break;
} else
// We've processed the first user entered note, but now we find a system generated note, so break out since this would mean that user notes are done as they are contiguous
break;
} else
// Getting here means this is not a user note and since we haven't found the first user note, keep looking
continue;
} else
// Getting here means this is not a user note, so since we still haven't found the first user note keep on looking.
continue;
}
// else (user notes section of this code)
}
if (MissingCommaFromSpecialHandlingAddress)
break;
// Get customer profile information
if (ObjectUtils.isNotNull(pg.getBatch())) {
cp = pg.getBatch().getCustomerProfile();
if (ObjectUtils.isNotNull(cp))
if (ObjectUtils.isNotNull(cp.getSubUnitCode()))
subUnitCode = cp.getSubUnitCode();
else {
LOG.error("No Sub Unit Code provided for requisition number: " + pd.getRequisitionNbr());
break;
}
else {
LOG.error("No customer profile exists for payee name: " + pg.getPayeeName());
break;
}
}
if (first && !immediateCheckCode) {
if (!wroteMellonFastTrackHeaderRecords) {
// Open the file
os = new BufferedWriter(new FileWriter(ftFilename));
// Write the Fast Track header record (FIL00010)
os.write(// Record Type
"FIL00010" + cDelim + hdrRecType + // Variable (V) or Fixed (F) flag
cDelim + cDname + // Delimiter name - Must be either FFCARET or FFCOMMA. Others are allowed but BNY Mellon will have to be contacted first.
cDelim + "CORNELLUNIVKFS" + // Customer Id - a unique identifier for the customer
cDelim + testIndicator + // Test Indicator: T = Test run, P = Production Run
cDelim + "820" + // EDI Document Id (3 Bytes)
cDelim + "043000261" + // Our Mellon bank id (15 Bytes)
cDelim + // Customer Division Id - 35 bytes - Optional
cDelim + sdf.format(processDate) + // File Date and Time - 14 Bytes
cDelim + // Reserved Field - 3 Bytes
cDelim + // Filler - 872 bytes
"\n");
totalRecordCount = 1;
// Write the Fast Track email records (FIL00020) once for each file
for (Iterator<String> emailIter = notificationEmailAddresses.iterator(); emailIter.hasNext(); ) {
String emailAddress = emailIter.next();
// write (FIL00020) record for each email address
os.write("FIL00020" + cDelim + emailAddress + cDelim + cDelim + "\n");
totalRecordCount = totalRecordCount + 1;
}
wroteMellonFastTrackHeaderRecords = true;
}
// Get country name
int CountryNameMaxLength = 15;
Country country = null;
if (StringUtils.isNotBlank(pg.getCountry())) {
country = countryService.getCountry(pg.getCountry());
}
if (country != null)
sCountryName = country.getName().substring(0, ((country.getName().length() >= CountryNameMaxLength) ? CountryNameMaxLength : country.getName().length()));
else if (ObjectUtils.isNotNull(pg.getCountry()))
sCountryName = pg.getCountry().substring(0, ((pg.getCountry().length() >= CountryNameMaxLength) ? CountryNameMaxLength : pg.getCountry().length()));
else
// Do this only if both the getPostalCountryName() AND getCountry() are empty
sCountryName = "";
// Do final country name processing per requirements from BNY Mellon.
if (sCountryName.toUpperCase().contains("UNITED STATES"))
sCountryName = "";
// Get special handling indicator
specialHandlingCode = pg.getPymtSpecialHandling();
// Get attachment indicator
attachmentCode = pg.getPymtAttachment();
// Determines the division code based on special handling indicator and attachment indicator
int dvCodeInt = 0;
if (specialHandlingCode == false && attachmentCode == false) {
dvCodeInt = CUPdpConstants.DivisionCodes.US_MAIL;
}
if (specialHandlingCode == true && attachmentCode == false) {
dvCodeInt = CUPdpConstants.DivisionCodes.US_MAIL;
}
if (specialHandlingCode == false && attachmentCode == true) {
dvCodeInt = CUPdpConstants.DivisionCodes.CU_MAIL_SERVICES;
}
if (specialHandlingCode == true && attachmentCode == true) {
dvCodeInt = CUPdpConstants.DivisionCodes.CU_MAIL_SERVICES;
}
divisionCode = String.format(String.format("%%0%dd", 3), dvCodeInt);
Date DisbursementDate;
if (ObjectUtils.isNotNull(pg.getDisbursementDate()))
DisbursementDate = pg.getDisbursementDate();
else {
LOG.error("Disbursement Date is NULL for Disbursement Number: " + CheckNumber);
break;
}
// Write the Fast Track PAY01000 record (only one per payee)
os.write(// Record Type - 8 bytes
"PAY01000" + cDelim + "7" + // 7=Payment and Electronic Advice (Transaction handling code - 2 bytes)
cDelim + totalNetAmount.toString() + // Total amount of check (Payment amount - 18 bytes)
cDelim + "C" + // C=Credit, D=Debit (Credit or debit Flag - 1 Byte)
cDelim + "CHK" + // CHK=Check (Payment method - 3 Bytes)
cDelim + "PBC" + // PBC is used for checks (Payment Format - 10 bytes)
cDelim + "01" + // Originators bank id qualifier - 2 bytes
cDelim + ourBankRoutingNumber + // Originators bank id - 12 bytes
cDelim + "DA" + // Originating account number Qualifier - 3 bytes
cDelim + ourBankAccountNumber + // Originating account number - 35 bytes
cDelim + "2150532082" + // Originating company identifier - 10 bytes This has to be different for this file than it is for ACH payments per BNY Mellon
cDelim + // Receiving bank id qualifier - 2 bytes
cDelim + // Receiving bank id - 12 bytes
cDelim + // Receiving account number qualifier - 3 bytes
cDelim + // Receiving account number - 35 bytes
cDelim + sdfPAY1000Rec.format(DisbursementDate) + // Effective date - 8 bytes - YYMMDD format
cDelim + // Business function code - 3 bytes
cDelim + CheckNumber + // Trace number (check number) - 50 bytes
cDelim + divisionCode + // Division code - 50 bytes
cDelim + // Currency code - 3 bytes
cDelim + // Note 1 - 80 bytes
cDelim + // Note 2 - 80 bytes
cDelim + // Note 3 - 80 bytes
cDelim + // Note 4 - 80 bytes
cDelim + // Filler
cDelim + "\n");
totalPaymentAmounts = totalPaymentAmounts.add(totalNetAmount);
// Write the Fast Track Payer Detail(PDT02010) record (only one per payee)
os.write(// Record Type - 8 bytes
"PDT02010" + cDelim + "PR" + // Name qualifier - 3 bytes (PR = Payer)
cDelim + // ID code qualifier - 2 bytes
cDelim + // ID code - 80 bytes
cDelim + PAYER_NAME + // Name - 35 bytes
cDelim + // Additional name 1 - 60 bytes
cDelim + // Additional name 2 - 60 bytes
cDelim + PAYER_ADDRESS_LINE1 + // Address line 1 - 35 bytes
cDelim + PAYER_ADDRESS_LINE2 + // Address line 2 - 35 bytes
cDelim + // Address line 3 - 35 bytes
cDelim + // Address line 4 - 35 bytes
cDelim + // Address line 5 - 35 bytes
cDelim + // Address line 6 - 35 bytes
cDelim + PAYER_CITY + // City - 30 bytes
cDelim + PAYER_STATE + // State/Province - 2 bytes
cDelim + PAYER_ZIP_CODE + // Postal code - 15 bytes
cDelim + // Country code - 3 bytes
cDelim + // Country name - 30 bytes
cDelim + // Ref qualifier 1 - 3 bytes
cDelim + // Ref ID 1 - 50 bytes
cDelim + // Ref description 1 - 80 bytes
cDelim + // Ref qualifier 1 - 3 bytes
cDelim + // Ref ID 1 - 50 bytes
cDelim + // Ref description 1 - 80 bytes
cDelim + cDelim + "\n");
// Write the Fast Track initial payee detail (PDT02010) record
int AddrMaxLength = 35;
int CityMaxLength = 30;
int StateMaxLength = 2;
int ZipMaxLength = 15;
int PayeeNameMaxLength = 35;
int PayeeIdMaxLength = 18;
String PayeeName = "";
String PayeeId = "";
String PayeeIdQualifier = "";
String AddrLine1 = "";
String AddrLine2 = "";
String AddrLine3 = "";
String AddrLine4 = "";
String AddrCity = "";
String AddrState = "";
String AddrZip = "";
if (ObjectUtils.isNotNull(pg.getPayeeName()))
PayeeName = pg.getPayeeName().substring(0, ((pg.getPayeeName().length() >= PayeeNameMaxLength) ? PayeeNameMaxLength : pg.getPayeeName().length()));
if (ObjectUtils.isNotNull(pg.getPayeeId())) {
PayeeId = pg.getPayeeId().substring(0, ((pg.getPayeeId().length() >= PayeeIdMaxLength) ? PayeeIdMaxLength : pg.getPayeeId().length()));
PayeeIdQualifier = "ZZ";
}
if (ObjectUtils.isNotNull(pg.getLine1Address()))
AddrLine1 = pg.getLine1Address().substring(0, ((pg.getLine1Address().length() >= AddrMaxLength) ? AddrMaxLength : pg.getLine1Address().length()));
if (ObjectUtils.isNotNull(pg.getLine2Address()))
AddrLine2 = pg.getLine2Address().substring(0, ((pg.getLine2Address().length() >= AddrMaxLength) ? AddrMaxLength : pg.getLine2Address().length()));
if (ObjectUtils.isNotNull(pg.getLine3Address()))
AddrLine3 = pg.getLine3Address().substring(0, ((pg.getLine3Address().length() >= AddrMaxLength) ? AddrMaxLength : pg.getLine3Address().length()));
if (ObjectUtils.isNotNull(pg.getLine4Address()))
AddrLine4 = pg.getLine4Address().substring(0, ((pg.getLine4Address().length() >= AddrMaxLength) ? AddrMaxLength : pg.getLine4Address().length()));
if (ObjectUtils.isNotNull(pg.getCity()))
AddrCity = pg.getCity().substring(0, ((pg.getCity().length() >= CityMaxLength) ? CityMaxLength : pg.getCity().length()));
if (ObjectUtils.isNotNull(pg.getState()))
AddrState = pg.getState().substring(0, ((pg.getState().length() >= StateMaxLength) ? StateMaxLength : pg.getState().length()));
if (ObjectUtils.isNotNull(pg.getZipCd()))
AddrZip = (pg.getZipCd().substring(0, ((pg.getZipCd().length() >= ZipMaxLength) ? ZipMaxLength : pg.getZipCd().length()))).replace("-", "");
os.write(// Record Type - 8 bytes
"PDT02010" + cDelim + "PE" + // Name qualifier - 3 bytes (PE = Payee) This record's data prints on the check
cDelim + PayeeIdQualifier + // ID code qualifier - 2 bytes
cDelim + PayeeId + // ID code - 80 bytes
cDelim + PayeeName + // Name - 35 bytes
cDelim + // Additional name 1 - 60 bytes
cDelim + // Additional name 2 - 60 bytes
cDelim + AddrLine1 + // Address line 1 - 35 bytes
cDelim + AddrLine2 + // Address line 2 - 35 bytes
cDelim + AddrLine3 + // Address line 3 - 35 bytes
cDelim + AddrLine4 + // Address line 4 - 35 bytes
cDelim + // Address line 5 - 35 bytes
cDelim + // Address line 6 - 35 bytes
cDelim + AddrCity + // City - 30 bytes
cDelim + AddrState + // State/Province - 2 bytes
cDelim + AddrZip + // Postal code - 15 bytes
cDelim + // Country code - 3 bytes
cDelim + sCountryName + // Country name - 15 bytes (do not use if Country Code is used)
cDelim + // Ref qualifier 1 - 3 bytes (not used)
cDelim + // Ref ID 1 - 50 bytes (not used)
cDelim + // Ref description 1 - 80 bytes (not used)
cDelim + // Ref qualifier 1 - 3 bytes (not used)
cDelim + // Ref ID 1 - 50 bytes (not used)
cDelim + // Ref description 1 - 80 bytes (not used)
cDelim + cDelim + "\n");
// If no alternate address is provided, we must populate with the customer address
if (NumOfAltAddressLines == 0) {
// If we were not provided an alternate address to mail the check to, then assign the same values
// in the third PDT02010 record the same exact values as the second PDT02010 record.
altAddrSendTo = PayeeName;
altAddrAddr1 = AddrLine1;
altAddrAddr2 = AddrLine2;
altAddrAddr3 = AddrLine3;
altAddrAddr4 = AddrLine4;
altAddrCity = AddrCity;
altAddrState = AddrState;
altAddrZip = AddrZip;
altAddrCityStateZip = altAddrCity + ", " + altAddrState + " " + altAddrZip;
altCountryName = sCountryName;
altRefQualifer = "ZZ";
} else {
altAddrZip = altAddrZip.replace("-", "");
// If we have two addresses, an original and an alternate, make sure the alternate does not inherit the original's country name!
altCountryName = "";
}
// Write the Fast Track second payee detail (PDT02010) record (for alternate addressing (special handling addressing case)
os.write(// Record Type - 8 bytes
"PDT02010" + cDelim + "FE" + // Name qualifier - 3 bytes (FE = Remit) This record's data prints on the remittance
cDelim + PayeeIdQualifier + // ID code qualifier - 2 bytes
cDelim + PayeeId + // ID code - 80 bytes
cDelim + altAddrSendTo + // Name - 35 bytes
cDelim + // Additional name 1 - 60 bytes
cDelim + // Additional name 2 - 60 bytes
cDelim + altAddrAddr1 + // Address line 1 - 35 bytes
cDelim + altAddrAddr2 + // Address line 2 - 35 bytes
cDelim + altAddrAddr3 + // Address line 3 - 35 bytes
cDelim + altAddrAddr4 + // Address line 4 - 35 bytes
cDelim + // Address line 5 - 35 bytes
cDelim + // Address line 6 - 35 bytes
cDelim + altAddrCity + // City - 30 bytes
cDelim + altAddrState + // State/Province - 2 bytes
cDelim + altAddrZip + // Postal code - 15 bytes
cDelim + // Country code - 3 bytes
cDelim + altCountryName + // Country name - 30 15 bytes (do not use if Country Code is used) Alternate addresses are never outside the USA.
cDelim + // Ref qualifier 1 - 3 bytes
cDelim + // Ref ID 1 - 50 bytes
cDelim + // Ref description 1 - 80 bytes
cDelim + // Ref qualifier 1 - 3 bytes
cDelim + // Ref ID 1 - 50 bytes
cDelim + // Ref description 1 - 80 bytes
cDelim + cDelim + "\n");
// One for the PAY01000 record and three for the PDT02010 records
totalRecordCount = totalRecordCount + 4;
// Set this here so it is only executed once per payee
first = false;
}
// if (first && !immediateCheckCode)
// Write the Fast Track REM03020 records
// Up to 3 characters
String remittanceIdCode = "";
// Up to 22 characters
String remittanceIdText = "";
// Here we will NOT have an invoice number but we will have an eDoc number and NO PO number
if (subUnitCode.equals(CuDisbursementVoucherConstants.DV_EXTRACT_SUB_UNIT_CODE)) {
remittanceIdCode = "TN";
// Here, we are guaranteed to have a pd.getCustPaymentDocNbr
remittanceIdText = "Doc No:" + pd.getCustPaymentDocNbr();
// Assign RefDesc1
RefDesc1 = "";
} else // Here we will have an invoice number and an eDoc number and a PO number
if (subUnitCode.equals("PRAP")) {
remittanceIdCode = "IV";
// Here, we are guaranteed to have a pd.getInvoiceNbr
remittanceIdText = pd.getInvoiceNbr();
// Assign RefDesc1
if (ObjectUtils.isNotNull(pd.getPurchaseOrderNbr()))
RefDesc1 = "PO:" + pd.getPurchaseOrderNbr() + ", Doc No:" + pd.getCustPaymentDocNbr();
else
RefDesc1 = "Doc No:" + pd.getCustPaymentDocNbr();
} else // Here we will have an invoice number and an eDoc number but we will NOT have PO number
if (!subUnitCode.equals(CuDisbursementVoucherConstants.DV_EXTRACT_SUB_UNIT_CODE) && !subUnitCode.equals("PRAP")) {
remittanceIdCode = "IV";
// Here, we are guaranteed to have a pd.getInvoiceNbr
remittanceIdText = pd.getInvoiceNbr();
// Assign RefDesc1
RefDesc1 = "Doc No:" + pd.getCustPaymentDocNbr();
}
// Assign the RefDesc fields.
if (!PreparerInfoText.isEmpty())
if (RefDesc1.isEmpty()) {
if (FirstNoteAfterAddressInfo.isEmpty()) {
if (SecondNoteAfterAddressInfo.isEmpty()) {
if (ThirdNoteAfterAddressInfo.isEmpty()) {
// no notes
RefDesc1 = PreparerInfoText;
RefDesc2 = "";
RefDesc3 = "";
RefDesc4 = "";
} else {
// Note3
RefDesc1 = PreparerInfoText;
RefDesc2 = ThirdNoteAfterAddressInfo;
RefDesc3 = "";
RefDesc4 = "";
}
} else {
if (ThirdNoteAfterAddressInfo.isEmpty()) {
// Note2
RefDesc1 = PreparerInfoText;
RefDesc2 = SecondNoteAfterAddressInfo;
RefDesc3 = "";
RefDesc4 = "";
} else {
// Note2, Note3
RefDesc1 = PreparerInfoText;
RefDesc2 = SecondNoteAfterAddressInfo;
RefDesc3 = ThirdNoteAfterAddressInfo;
RefDesc4 = "";
}
}
} else {
// Note1
if (SecondNoteAfterAddressInfo.isEmpty()) {
if (ThirdNoteAfterAddressInfo.isEmpty()) {
// Note1
RefDesc1 = PreparerInfoText;
RefDesc2 = FirstNoteAfterAddressInfo;
RefDesc3 = "";
RefDesc4 = "";
} else {
// Note1, Note3
RefDesc1 = PreparerInfoText;
RefDesc2 = FirstNoteAfterAddressInfo;
RefDesc3 = ThirdNoteAfterAddressInfo;
RefDesc4 = "";
}
} else {
// Note2
if (ThirdNoteAfterAddressInfo.isEmpty()) {
// Note1, Note2
RefDesc1 = PreparerInfoText;
RefDesc2 = FirstNoteAfterAddressInfo;
RefDesc3 = SecondNoteAfterAddressInfo;
RefDesc4 = "";
} else {
// Note1, Note2, Note3
RefDesc1 = PreparerInfoText;
RefDesc2 = FirstNoteAfterAddressInfo;
RefDesc3 = SecondNoteAfterAddressInfo;
RefDesc4 = ThirdNoteAfterAddressInfo;
}
}
}
} else {
// RefDesc1 contains text
if (FirstNoteAfterAddressInfo.isEmpty()) {
if (SecondNoteAfterAddressInfo.isEmpty()) {
if (ThirdNoteAfterAddressInfo.isEmpty()) {
// no notes
RefDesc2 = PreparerInfoText;
RefDesc3 = "";
RefDesc4 = "";
} else {
// Note3
RefDesc2 = PreparerInfoText;
RefDesc3 = ThirdNoteAfterAddressInfo;
RefDesc4 = "";
}
} else {
if (ThirdNoteAfterAddressInfo.isEmpty()) {
// Note2
RefDesc2 = PreparerInfoText;
RefDesc3 = SecondNoteAfterAddressInfo;
RefDesc4 = "";
} else {
// Note2, Note3
RefDesc2 = PreparerInfoText;
RefDesc3 = SecondNoteAfterAddressInfo;
RefDesc4 = ThirdNoteAfterAddressInfo;
}
}
} else {
// Note1
if (SecondNoteAfterAddressInfo.isEmpty()) {
if (ThirdNoteAfterAddressInfo.isEmpty()) {
// Note1
RefDesc2 = PreparerInfoText;
RefDesc3 = FirstNoteAfterAddressInfo;
RefDesc4 = "";
} else {
// Note1, Note3
RefDesc2 = PreparerInfoText;
RefDesc3 = FirstNoteAfterAddressInfo;
RefDesc4 = ThirdNoteAfterAddressInfo;
}
} else {
// Note2
if (ThirdNoteAfterAddressInfo.isEmpty()) {
// Note1, Note2
RefDesc2 = PreparerInfoText;
RefDesc3 = FirstNoteAfterAddressInfo;
RefDesc4 = SecondNoteAfterAddressInfo;
} else {
// Note1, Note2, Note3
RefDesc2 = PreparerInfoText;
RefDesc3 = FirstNoteAfterAddressInfo;
RefDesc4 = SecondNoteAfterAddressInfo;
}
}
}
}
else // PreparerInfoText is empty
if (RefDesc1.isEmpty())
if (FirstNoteAfterAddressInfo.isEmpty()) {
if (SecondNoteAfterAddressInfo.isEmpty()) {
if (ThirdNoteAfterAddressInfo.isEmpty()) {
// no notes
RefDesc1 = "";
RefDesc2 = "";
RefDesc3 = "";
RefDesc4 = "";
} else {
// Note3
RefDesc1 = ThirdNoteAfterAddressInfo;
RefDesc2 = "";
RefDesc3 = "";
RefDesc4 = "";
}
} else {
if (ThirdNoteAfterAddressInfo.isEmpty()) {
// Note2
RefDesc1 = SecondNoteAfterAddressInfo;
RefDesc2 = "";
RefDesc3 = "";
RefDesc4 = "";
} else {
// Note2, Note3
RefDesc1 = SecondNoteAfterAddressInfo;
RefDesc2 = ThirdNoteAfterAddressInfo;
RefDesc3 = "";
RefDesc4 = "";
}
}
} else {
// Note1
if (SecondNoteAfterAddressInfo.isEmpty()) {
if (ThirdNoteAfterAddressInfo.isEmpty()) {
// Note1
RefDesc1 = FirstNoteAfterAddressInfo;
RefDesc2 = "";
RefDesc3 = "";
RefDesc4 = "";
} else {
// Note1, Note3
RefDesc1 = FirstNoteAfterAddressInfo;
RefDesc2 = ThirdNoteAfterAddressInfo;
RefDesc3 = "";
RefDesc4 = "";
}
} else {
// Note2
if (ThirdNoteAfterAddressInfo.isEmpty()) {
// Note1, Note2
RefDesc1 = FirstNoteAfterAddressInfo;
RefDesc2 = SecondNoteAfterAddressInfo;
RefDesc3 = "";
RefDesc4 = "";
} else {
// Note1, Note2, Note3
RefDesc1 = FirstNoteAfterAddressInfo;
RefDesc2 = SecondNoteAfterAddressInfo;
RefDesc3 = ThirdNoteAfterAddressInfo;
RefDesc4 = "";
}
}
}
else // RefDesc1 contains text
if (FirstNoteAfterAddressInfo.isEmpty()) {
if (SecondNoteAfterAddressInfo.isEmpty()) {
if (ThirdNoteAfterAddressInfo.isEmpty()) {
// no notes
RefDesc2 = "";
RefDesc3 = "";
RefDesc4 = "";
} else {
// Note3
RefDesc2 = ThirdNoteAfterAddressInfo;
RefDesc3 = "";
RefDesc4 = "";
}
} else {
if (ThirdNoteAfterAddressInfo.isEmpty()) {
// Note2
RefDesc2 = SecondNoteAfterAddressInfo;
RefDesc3 = "";
RefDesc4 = "";
} else {
// Note2, Note3
RefDesc2 = SecondNoteAfterAddressInfo;
RefDesc3 = ThirdNoteAfterAddressInfo;
RefDesc4 = "";
}
}
} else {
// Note1
if (SecondNoteAfterAddressInfo.isEmpty()) {
if (ThirdNoteAfterAddressInfo.isEmpty()) {
// Note1
RefDesc2 = FirstNoteAfterAddressInfo;
RefDesc3 = "";
RefDesc4 = "";
} else {
// Note1, Note3
RefDesc2 = FirstNoteAfterAddressInfo;
RefDesc3 = ThirdNoteAfterAddressInfo;
RefDesc4 = "";
}
} else {
// Note2
if (ThirdNoteAfterAddressInfo.isEmpty()) {
// Note1, Note2
RefDesc2 = FirstNoteAfterAddressInfo;
RefDesc3 = SecondNoteAfterAddressInfo;
RefDesc4 = "";
} else {
// Note1, Note2, Note3
RefDesc2 = FirstNoteAfterAddressInfo;
RefDesc3 = SecondNoteAfterAddressInfo;
RefDesc4 = ThirdNoteAfterAddressInfo;
}
}
}
// If we have something in RefDesc1 then RefQualifier must be "ZZ" according to Mellon's spec.
if (!RefDesc1.isEmpty())
Ref1Qualifier = "ZZ";
else
Ref1Qualifier = "";
// If we have something in RefDesc2 then RefQualifier must be "ZZ" according to Mellon's spec.
if (!RefDesc2.isEmpty())
Ref2Qualifier = "ZZ";
else
Ref2Qualifier = "";
// If we have something in RefDesc3 then RefQualifier must be "ZZ" according to Mellon's spec.
if (!RefDesc3.isEmpty())
Ref3Qualifier = "ZZ";
else
Ref3Qualifier = "";
// If we have something in RefDesc4 then RefQualifier must be "ZZ" according to Mellon's spec.
if (!RefDesc4.isEmpty())
Ref4Qualifier = "ZZ";
else
Ref4Qualifier = "";
// Only perform the following if this is an immediate check
if (immediateCheckCode) {
if (!wroteMellonIssuanceHeaderRecords) {
// Open the File
osI = new BufferedWriter(new FileWriter(arFilename));
// Write the Mellon issuance header record
osI.write(//
"1" + //
repeatThis(" ", 2) + //
"MELLONBANK" + //
"CORNELL " + //
sdfIDate.format(processDate) + //
sdfITime.format(processDate) + repeatThis(" ", 209) + //
"\n");
// Write the BNY Mellon issuance service record
osI.write(//
"2" + //
repeatThis(" ", 30) + //
"100" + //
"242" + //
"0242" + //
"1" + repeatThis(" ", 200) + //
"\n");
// 2 issuance records added here.
numOfIssuanceRecords = numOfIssuanceRecords + 2;
wroteMellonIssuanceHeaderRecords = true;
}
String arPayeeName = "";
String arLine1Address = "";
if (ObjectUtils.isNotNull(pg.getPayeeName()))
arPayeeName = pg.getPayeeName();
if (ObjectUtils.isNotNull(pg.getLine1Address()))
arLine1Address = pg.getLine1Address();
// Write the BNY Mellon issuance detail (regular format) record
CheckNumber = repeatThis("0", 10 - pg.getDisbursementNbr().toString().length()) + pg.getDisbursementNbr().toString();
String AmountOfCheck = totalNetAmount.toString().replace(".", "");
AmountOfCheck = repeatThis("0", 10 - AmountOfCheck.length()) + AmountOfCheck;
osI.write(// Record Type - 1 Byte
"6" + // Status Code-> 2: Add Issue, 6: Void Issue - 1 Byte
"2" + // Origin - Company Name - 10 Bytes
"CORNELL " + // Destination - Identification of receiving location: MELLONWEST, MELLONEAST, MELLONTRUS, BOSTONNOW - 10 bytes
"MELLONWEST" + repeatThis("0", 10 - ourBankAccountNumber.length()) + // Checking account number - 10 Bytes
ourBankAccountNumber + // Check Serial Number (check number) - 10 Bytes
CheckNumber + // Check Amount: Format $$$$$$$$cc - 10 Bytes
AmountOfCheck + // Issue Date: Format YYMMDD - 6 Bytes
sdfIDate.format(processDate) + // Additional Data (Optional) - 10 bytes
repeatThis(" ", 10) + // Register Information (Optional) - 5 Bytes
repeatThis(" ", 5) + // Not used - 49 bytes
repeatThis(" ", 49) + // Payee Line 1 - Payee Name (Required) - 60 Bytes
String.format("%-60.60s", arPayeeName.toUpperCase()) + String.format("%-60.60s", arLine1Address.toUpperCase()) + // Payee Line 2 - Payee Name or first line of address (Required) - 60 Bytes
"\n");
// Totals the number of add issues across ALL checks issued not just for this one payee.
arNumOfAddIssues = arNumOfAddIssues + 1;
// Same as for the count but for the total dollar amount.
arTotalOfAddIssues = arTotalOfAddIssues.add(totalNetAmount);
// Add to the total number of issuance records
numOfIssuanceRecords = numOfIssuanceRecords + 1;
} else // if (immediateCheckCode)
{
// All of these are limited to 18 bytes in Fast Track.
String ftNetPayAmount = "";
String ftTotalAmount = "";
String ftDiscountAmt = "";
if (ObjectUtils.isNotNull(pd.getNetPaymentAmount())) {
ftNetPayAmount = pd.getNetPaymentAmount().toString();
if (ftNetPayAmount.length() > 18) {
LOG.error("Net Payment Amount is more than 18 bytes for check number " + CheckNumber);
break;
}
}
if (ObjectUtils.isNotNull(pd.getOrigInvoiceAmount())) {
ftTotalAmount = pd.getOrigInvoiceAmount().toString();
if (ftTotalAmount.length() > 18) {
LOG.error("Original Invoice Amount is more than 18 bytes for check number " + CheckNumber);
break;
}
}
if (ObjectUtils.isNotNull(pd.getInvTotDiscountAmount())) {
ftDiscountAmt = pd.getInvTotDiscountAmount().toString();
if (ftDiscountAmt.length() > 18) {
LOG.error("Discount Amount is more than 18 bytes for check number " + CheckNumber);
break;
}
}
String InvoiceDate = "";
if (ObjectUtils.isNotNull(pd.getInvoiceDate())) {
InvoiceDate = pd.getInvoiceDate().toString().replace("-", "");
dateQualifier = "003";
} else {
LOG.error("Invoice date is blank for check number " + CheckNumber);
break;
}
// Write the Fast Track REM03020 record
os.write(// Record type - 8 bytes
"REM03020" + cDelim + remittanceIdCode + // Remittance qualifier code - 3 bytes
cDelim + remittanceIdText + // Remittance ID - 22 bytes
cDelim + ftNetPayAmount + // Net invoice amount - 18 bytes
cDelim + ftTotalAmount + // Total invoice amount - 18 bytes
cDelim + ftDiscountAmt + // Discount amount - 18 bytes
cDelim + // Note 1 - 80 bytes
cDelim + // Note 2 - 80 bytes
cDelim + Ref1Qualifier + // Ref qualifier 1
cDelim + // Ref ID 1
cDelim + RefDesc1 + // Ref description 1 (up to 72 bytes)
cDelim + Ref2Qualifier + // Ref qualifier 2
cDelim + // Ref ID 2 (not used)
cDelim + RefDesc2 + // Ref description 2 (up to 72 bytes)
cDelim + Ref3Qualifier + // Ref qualifier 3
cDelim + // Ref ID 3 (not used)
cDelim + RefDesc3 + // Ref description 3 (up to 72 bytes)
cDelim + Ref4Qualifier + // Ref qualifier 4
cDelim + // Ref ID 4 (not used)
cDelim + RefDesc4 + // Ref description 4 (up to 72 bytes)
cDelim + dateQualifier + // Date qualifier 1
cDelim + InvoiceDate + // Date 1
cDelim + // Date qualifier 2 (not used)
cDelim + // Date 2 (not used)
cDelim + // Date qualifier 3 (not used)
cDelim + // Date 3 (not used)
cDelim + // Date qualifier 4 (not used)
cDelim + // Date 4 (not used)
cDelim + "\n");
// One for the REM03020 records
totalRecordCount = totalRecordCount + 1;
}
// Copied from the above XML generating method since this is the method that needs to record what was processed and what was not.
if (!testMode) {
pg.setDisbursementDate(new java.sql.Date(processDate.getTime()));
pg.setPaymentStatus(extractedStatus);
this.businessObjectService.save(pg);
}
}
// while (paymentDetails.hasNext())
}
// for (Iterator<Integer> iter = disbNbrs.iterator(); iter.hasNext();)
}
// for (String bankCode : bankCodes)
// Need to update the total record count here to make sure it includes the trailer record
totalRecordCount = totalRecordCount + 1;
if (wroteMellonIssuanceHeaderRecords) {
// Write the BNY Mellon issuance service total record
String NumOfAddIssues = repeatThis("0", 10 - Integer.toString(arNumOfAddIssues).length()) + Integer.toString(arNumOfAddIssues);
String TotalAmountOfAddIssues = arTotalOfAddIssues.toString().replace(".", "");
TotalAmountOfAddIssues = repeatThis("0", 12 - TotalAmountOfAddIssues.length()) + TotalAmountOfAddIssues;
osI.write(// Record Type
"8" + // Total Number of Add Issues, 10 Bytes, numeric only, right justified, prefixed with zeros as needed to make length
NumOfAddIssues + // Total Amount of Add Issues, 12 Bytes, numeric only (no decimals) right justified, prefixed with zeros as needed to make length
TotalAmountOfAddIssues + // Total number of voided checks, 10 Bytes for voided checks which at this point we don't do.
repeatThis("0", 10) + // Total amount of voided checks, 12 Bytes for the total voided amounts
repeatThis("0", 12) + repeatThis(" ", 197) + // Required Filler
"\n");
// This is to account for record #8 above
numOfIssuanceRecords = numOfIssuanceRecords + 1;
// Write the BNY Mellon issuance trailer record
// Need to add an issuance record here to account for the trailer record
numOfIssuanceRecords = numOfIssuanceRecords + 1;
String numberOfIssuanceRecords = repeatThis("0", 6 - Integer.toString(numOfIssuanceRecords).length()) + Integer.toString(numOfIssuanceRecords);
osI.write(// Record Type
"9" + // Total Number of Records, 6 bytes, numeric only, right justified, prefixed with zeros
numberOfIssuanceRecords + repeatThis(" ", 235) + // Required Filler
"\n");
}
if (wroteMellonFastTrackHeaderRecords) {
// Fast Track trailer record
os.write(// Record Type
"TRL09000" + cDelim + totalRecordCount + // Total # of records in the file including header and trailer. 15 bytes numeric only
cDelim + totalPaymentAmounts + // Total amount of all net payments for the file. 25 bytes numeric only
cDelim + // EOR
"\n");
}
}// try
catch (IOException ie) {
LOG.error("IO Exception with writeExtractCheckFileMellonBankFastTrack() Problem reading file: " + ftFilename, ie);
throw new IllegalArgumentException("Error writing to output file: " + ie.getMessage());
} catch (Exception ex) {
LOG.error("General Exception with writeExtractCheckFileMellonBankFastTrack(). Error is: " + ex.getMessage(), ex);
} finally {
// Close file
if (os != null) {
try {
os.close();
// Need to do this at the end to indicate that the file is ready after it is closed.
renameFile(ftFilename, ftFilename + ".READY");
} catch (IOException ie) {
// Not much we can do now
LOG.error("IOException encountered in writeExtractCheckFileMellonBankFastTrack. Message is: " + ie.getMessage());
}
}
// osI.close();
if (osI != null) {
try {
osI.close();
// Rename the resulting file to have a .READY at the end
// Need to do this at the end to indicate that the file is ready after it is closed.
renameFile(arFilename, arFilename + ".READY");
} catch (IOException ie) {
// Not much we can do now
LOG.error("IOException encountered in writeExtractAchFile. Message is: " + ie.getMessage());
}
}
}
}
use of org.kuali.kfs.pdp.businessobject.PaymentGroup in project cu-kfs by CU-CommunityApps.
the class CuFormatServiceImpl method startFormatProcess.
@Override
public FormatProcessSummary startFormatProcess(Person user, String campus, List<CustomerProfile> customers, Date paydate, String paymentTypes, String paymentDistribution) {
LOG.debug("startFormatProcess() started");
for (CustomerProfile element : customers) {
LOG.debug("startFormatProcess() Customer: " + element);
}
// Create the process
Date d = new Date();
PaymentProcess paymentProcess = new PaymentProcess();
paymentProcess.setCampusCode(campus);
paymentProcess.setProcessUser(user);
paymentProcess.setProcessTimestamp(new Timestamp(d.getTime()));
this.businessObjectService.save(paymentProcess);
// add an entry in the format process table (to lock the format process)
FormatProcess formatProcess = new FormatProcess();
formatProcess.setPhysicalCampusProcessCode(campus);
formatProcess.setBeginFormat(dateTimeService.getCurrentTimestamp());
formatProcess.setPaymentProcIdentifier(paymentProcess.getId().intValue());
this.businessObjectService.save(formatProcess);
Timestamp now = new Timestamp((new Date()).getTime());
java.sql.Date sqlDate = new java.sql.Date(paydate.getTime());
Calendar c = Calendar.getInstance();
c.setTime(sqlDate);
c.set(Calendar.HOUR, 11);
c.set(Calendar.MINUTE, 59);
c.set(Calendar.SECOND, 59);
c.set(Calendar.MILLISECOND, 59);
c.set(Calendar.AM_PM, Calendar.PM);
Timestamp paydateTs = new Timestamp(c.getTime().getTime());
LOG.debug("startFormatProcess() last update = " + now);
LOG.debug("startFormatProcess() entered paydate = " + paydate);
LOG.debug("startFormatProcess() actual paydate = " + paydateTs);
PaymentStatus format = this.businessObjectService.findBySinglePrimaryKey(PaymentStatus.class, PdpConstants.PaymentStatusCodes.FORMAT);
List customerIds = new ArrayList();
for (Iterator iter = customers.iterator(); iter.hasNext(); ) {
CustomerProfile element = (CustomerProfile) iter.next();
customerIds.add(element.getId());
}
// Mark all of them ready for format
Iterator groupIterator = ((CuFormatPaymentDao) formatPaymentDao).markPaymentsForFormat(customerIds, paydateTs, paymentTypes, paymentDistribution);
while (groupIterator.hasNext()) {
PaymentGroup paymentGroup = (PaymentGroup) groupIterator.next();
paymentGroup.setLastUpdatedTimestamp(paydateTs);
paymentGroup.setPaymentStatus(format);
paymentGroup.setProcess(paymentProcess);
businessObjectService.save(paymentGroup);
}
// summarize them
FormatProcessSummary preFormatProcessSummary = new FormatProcessSummary();
Iterator<PaymentGroup> iterator = this.paymentGroupService.getByProcess(paymentProcess);
while (iterator.hasNext()) {
PaymentGroup paymentGroup = iterator.next();
preFormatProcessSummary.add(paymentGroup);
}
// if no payments found for format clear the format process
if (preFormatProcessSummary.getProcessSummaryList().size() == 0) {
LOG.debug("startFormatProcess() No payments to process. Format process ending");
// ?? maybe call end format process
clearUnfinishedFormat(paymentProcess.getId().intValue());
}
return preFormatProcessSummary;
}
use of org.kuali.kfs.pdp.businessobject.PaymentGroup in project cu-kfs by CU-CommunityApps.
the class CuPaymentFileServiceImpl method loadPayments.
@Override
public void loadPayments(PaymentFileLoad paymentFile, LoadPaymentStatus status, String incomingFileName) {
status.setChart(paymentFile.getChart());
status.setUnit(paymentFile.getUnit());
status.setSubUnit(paymentFile.getSubUnit());
status.setCreationDate(paymentFile.getCreationDate());
status.setDetailCount(paymentFile.getActualPaymentCount());
status.setDetailTotal(paymentFile.getCalculatedPaymentTotalAmount());
// create batch record for payment load
Batch batch = createNewBatch(paymentFile, getBaseFileName(incomingFileName));
businessObjectService.save(batch);
paymentFile.setBatchId(batch.getId());
status.setBatchId(batch.getId());
// do warnings and set defaults
List<String> warnings = paymentFileValidationService.doSoftEdits(paymentFile);
status.setWarnings(warnings);
// store groups
for (PaymentGroup paymentGroup : paymentFile.getPaymentGroups()) {
assignDisbursementTypeCode(paymentGroup);
updatePaymentFieldsForEmployeePayee(paymentFile, paymentGroup);
businessObjectService.save(paymentGroup);
}
// CU Customization: Check for and warn about inactive vendors.
checkForInactiveVendors(paymentFile.getPaymentGroups(), batch.getCustomerProfile());
// send list of warnings
paymentFileEmailService.sendLoadEmail(paymentFile, warnings);
if (paymentFile.isTaxEmailRequired()) {
paymentFileEmailService.sendTaxEmail(paymentFile);
}
removeDoneFile(incomingFileName);
LOG.debug("loadPayments() was successful");
status.setLoadStatus(LoadPaymentStatus.LoadStatus.SUCCESS);
}
use of org.kuali.kfs.pdp.businessobject.PaymentGroup in project cu-kfs by CU-CommunityApps.
the class CuPaymentMaintenanceServiceImpl method cancelReissueDisbursement.
@Override
public boolean cancelReissueDisbursement(Integer paymentGroupId, String note, Person user) {
LOG.debug("cancelReissueDisbursement() started");
if (!pdpAuthorizationService.hasCancelPaymentPermission(user.getPrincipalId())) {
LOG.warn("cancelReissueDisbursement() User " + user.getPrincipalId() + " does not have rights to cancel payments. This should not happen unless user is URL spoofing.");
throw new RuntimeException("cancelReissueDisbursement() 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("cancelReissueDisbursement() Disbursement not found; throw exception.");
GlobalVariables.getMessageMap().putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.PaymentDetail.ErrorMessages.ERROR_DISBURSEMENT_NOT_FOUND);
return false;
}
String paymentStatus = paymentGroup.getPaymentStatus().getCode();
if (!(PdpConstants.PaymentStatusCodes.OPEN.equals(paymentStatus))) {
if (((PdpConstants.PaymentStatusCodes.EXTRACTED.equals(paymentStatus)) && (ObjectUtils.isNotNull(paymentGroup.getDisbursementDate()))) || (PdpConstants.PaymentStatusCodes.PENDING_ACH.equals(paymentStatus))) {
if (LOG.isDebugEnabled()) {
LOG.debug("cancelReissueDisbursement() Payment status is " + paymentStatus + "; continue with cancel.");
}
List<PaymentGroup> allDisbursementPaymentGroups = this.paymentGroupService.getByDisbursementNumber(paymentGroup.getDisbursementNbr().intValue());
for (PaymentGroup pg : allDisbursementPaymentGroups) {
PaymentGroupHistory pgh = new PaymentGroupHistory();
if (!pg.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(pg.getDisbursementType())) && (pg.getDisbursementType().getCode().equals(PdpConstants.DisbursementTypeCodes.CHECK))) {
pgh.setPmtCancelExtractStat(Boolean.FALSE);
}
pgh.setOrigProcessImmediate(pg.getProcessImmediate());
pgh.setOrigPmtSpecHandling(pg.getPymtSpecialHandling());
pgh.setBank(pg.getBank());
pgh.setOrigPaymentDate(pg.getPaymentDate());
// put a check for null since disbursement date was not set in testMode / dev
if (ObjectUtils.isNotNull(pg.getDisbursementDate())) {
pgh.setOrigDisburseDate(new Timestamp(pg.getDisbursementDate().getTime()));
}
pgh.setOrigAchBankRouteNbr(pg.getAchBankRoutingNbr());
pgh.setOrigDisburseNbr(pg.getDisbursementNbr());
pgh.setOrigAdviceEmail(pg.getAdviceEmailAddress());
pgh.setDisbursementType(pg.getDisbursementType());
pgh.setProcess(pg.getProcess());
glPendingTransactionService.generateReissueGeneralLedgerPendingEntry(pg);
LOG.debug("cancelReissueDisbursement() Status is '" + paymentStatus + "; delete row from AchAccountNumber table.");
AchAccountNumber achAccountNumber = pg.getAchAccountNumber();
if (ObjectUtils.isNotNull(achAccountNumber)) {
this.businessObjectService.delete(achAccountNumber);
pg.setAchAccountNumber(null);
}
// if bank functionality is not enabled or the group bank is inactive clear bank code
if (!bankService.isBankSpecificationEnabled() || !pg.getBank().isActive()) {
pg.setBank(null);
}
pg.setDisbursementDate((java.sql.Date) null);
pg.setAchBankRoutingNbr(null);
pg.setAchAccountType(null);
pg.setPhysCampusProcessCd(null);
pg.setDisbursementNbr((KualiInteger) null);
pg.setAdviceEmailAddress(null);
// KFSPTS-1413 - do not reset the disb type as it prevents these payments from being picked up properly on reissue.
// pg.setDisbursementType(null);
pg.setProcess(null);
pg.setProcessImmediate(false);
changeStatus(pg, PdpConstants.PaymentStatusCodes.OPEN, PdpConstants.PaymentChangeCodes.CANCEL_REISSUE_DISBURSEMENT, note, user, pgh);
}
LOG.debug("cancelReissueDisbursement() Disbursement cancelled and reissued; exit method.");
} else {
LOG.debug("cancelReissueDisbursement() Payment status is " + paymentStatus + " and disbursement date is " + paymentGroup.getDisbursementDate() + "; cannot cancel payment");
GlobalVariables.getMessageMap().putError(KFSConstants.GLOBAL_ERRORS, PdpKeyConstants.PaymentDetail.ErrorMessages.ERROR_DISBURSEMENT_INVALID_TO_CANCEL_AND_REISSUE);
return false;
}
} else {
LOG.debug("cancelReissueDisbursement() Disbursement already cancelled and reissued; exit method.");
}
return true;
}
use of org.kuali.kfs.pdp.businessobject.PaymentGroup in project cu-kfs by CU-CommunityApps.
the class CuDisbursementVoucherExtractionHelperServiceImplTest method test.
public void test() {
CuDisbursementVoucherDocument dv = null;
try {
dv = (CuDisbursementVoucherDocument) SpringContext.getBean(DocumentService.class).getNewDocument(DisbursementVoucherDocument.class);
} catch (WorkflowException e) {
throw new RuntimeException("Error creating new disbursement voucher document: " + e.getMessage(), e);
}
if (dv != null) {
dv.getDocumentHeader().setDocumentDescription("Test Document Description");
dv.getDocumentHeader().setExplanation("Stuff");
dv.initiateDocument();
VendorDetail vendor = SpringContext.getBean(VendorService.class).getVendorDetail("13366-0");
VendorAddress vendoraddress = SpringContext.getBean(VendorService.class).getVendorDefaultAddress(vendor.getVendorHeaderGeneratedIdentifier(), vendor.getVendorDetailAssignedIdentifier(), "RM", "");
System.out.println(vendoraddress.getVendorCityName() + "\n");
dv.templateVendor(vendor, vendoraddress);
dv.setPayeeAssigned(true);
dv.getDvPayeeDetail().setDisbVchrPaymentReasonCode("S");
dv.setDisbVchrCheckTotalAmount(new KualiDecimal(86.00));
dv.setDisbVchrPaymentMethodCode("P");
dv.setDisbVchrCheckStubText("check text");
dv.setCampusCode("IT");
SourceAccountingLine accountingLine = new SourceAccountingLine();
accountingLine.setChartOfAccountsCode("IT");
accountingLine.setAccountNumber("G081040");
accountingLine.setFinancialObjectCode("2045");
accountingLine.setAmount((new KualiDecimal(86.00)));
accountingLine.setPostingYear(dv.getPostingYear());
accountingLine.setDocumentNumber(dv.getDocumentNumber());
dv.addSourceAccountingLine(accountingLine);
try {
documentService.saveDocument(dv);
} catch (WorkflowException e) {
throw new RuntimeException("Error saving new disbursement voucher document: " + e.getMessage(), e);
}
}
Date transactionTimestamp = new Date(SpringContext.getBean(DateTimeService.class).getCurrentDate().getTime());
Date processRunDate = new java.sql.Date(transactionTimestamp.getTime());
PaymentGroup pg = cuDisbursementVoucherExtractionHelperService.createPaymentGroup(dv, processRunDate);
assertTrue(pg.getPaymentDetails().get(0).getCustPaymentDocNbr().equalsIgnoreCase(dv.getDocumentNumber()));
}
Aggregations