Search in sources :

Example 6 with PaymentNoteText

use of org.kuali.kfs.pdp.businessobject.PaymentNoteText 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());
            }
        }
    }
}
Also used : FileWriter(java.io.FileWriter) BufferedWriter(java.io.BufferedWriter) PaymentDetail(org.kuali.kfs.pdp.businessobject.PaymentDetail) PaymentNoteText(org.kuali.kfs.pdp.businessobject.PaymentNoteText) KualiDecimal(org.kuali.rice.core.api.util.type.KualiDecimal) PaymentGroup(org.kuali.kfs.pdp.businessobject.PaymentGroup) CustomerProfile(org.kuali.kfs.pdp.businessobject.CustomerProfile) IOException(java.io.IOException) Date(java.util.Date) IOException(java.io.IOException) KualiInteger(org.kuali.rice.core.api.util.type.KualiInteger) Country(org.kuali.rice.location.api.country.Country) SimpleDateFormat(java.text.SimpleDateFormat)

Example 7 with PaymentNoteText

use of org.kuali.kfs.pdp.businessobject.PaymentNoteText in project cu-kfs by CU-CommunityApps.

the class CuPdpExtractServiceImpl method addNotes.

@Override
protected void addNotes(AccountsPayableDocument accountsPayableDocument, PaymentDetail paymentDetail) {
    int count = 1;
    if (accountsPayableDocument instanceof PaymentRequestDocument) {
        PaymentRequestDocument prd = (PaymentRequestDocument) accountsPayableDocument;
        if (prd.getSpecialHandlingInstructionLine1Text() != null) {
            PaymentNoteText pnt = new PaymentNoteText();
            pnt.setCustomerNoteLineNbr(new KualiInteger(count++));
            pnt.setCustomerNoteText(CUPurapConstants.SPECIAL_HANDLING_NOTE_LINE_1_NAME + prd.getSpecialHandlingInstructionLine1Text());
            paymentDetail.addNote(pnt);
        }
        if (prd.getSpecialHandlingInstructionLine2Text() != null) {
            PaymentNoteText pnt = new PaymentNoteText();
            pnt.setCustomerNoteLineNbr(new KualiInteger(count++));
            pnt.setCustomerNoteText(CUPurapConstants.SPECIAL_HANDLING_NOTE_LINE_2_ADDRESS + prd.getSpecialHandlingInstructionLine2Text());
            paymentDetail.addNote(pnt);
        }
        if (prd.getSpecialHandlingInstructionLine3Text() != null) {
            PaymentNoteText pnt = new PaymentNoteText();
            pnt.setCustomerNoteLineNbr(new KualiInteger(count++));
            pnt.setCustomerNoteText(CUPurapConstants.SPECIAL_HANDLING_NOTE_LINE_3_CITY_STATE_ZIP + prd.getSpecialHandlingInstructionLine3Text());
            paymentDetail.addNote(pnt);
        }
    }
    if (accountsPayableDocument.getNoteLine1Text() != null) {
        PaymentNoteText pnt = new PaymentNoteText();
        pnt.setCustomerNoteLineNbr(new KualiInteger(count++));
        pnt.setCustomerNoteText(CUPurapConstants.PURAP_NOTES_IDENTIFIER + accountsPayableDocument.getNoteLine1Text());
        paymentDetail.addNote(pnt);
    }
    if (accountsPayableDocument.getNoteLine2Text() != null) {
        PaymentNoteText pnt = new PaymentNoteText();
        pnt.setCustomerNoteLineNbr(new KualiInteger(count++));
        pnt.setCustomerNoteText(CUPurapConstants.PURAP_NOTES_IDENTIFIER + accountsPayableDocument.getNoteLine2Text());
        paymentDetail.addNote(pnt);
    }
    if (accountsPayableDocument.getNoteLine3Text() != null) {
        PaymentNoteText pnt = new PaymentNoteText();
        pnt.setCustomerNoteLineNbr(new KualiInteger(count++));
        pnt.setCustomerNoteText(CUPurapConstants.PURAP_NOTES_IDENTIFIER + accountsPayableDocument.getNoteLine3Text());
        paymentDetail.addNote(pnt);
    }
    PaymentNoteText pnt = new PaymentNoteText();
    pnt.setCustomerNoteLineNbr(new KualiInteger(count++));
    pnt.setCustomerNoteText("Sales Tax: " + accountsPayableDocument.getTotalRemitTax());
}
Also used : PaymentNoteText(org.kuali.kfs.pdp.businessobject.PaymentNoteText) KualiInteger(org.kuali.rice.core.api.util.type.KualiInteger) PaymentRequestDocument(org.kuali.kfs.module.purap.document.PaymentRequestDocument)

Example 8 with PaymentNoteText

use of org.kuali.kfs.pdp.businessobject.PaymentNoteText in project cu-kfs by CU-CommunityApps.

the class CuPaymentSourceHelperServiceImpl method buildNotesForCheckStubText.

@Override
public List<PaymentNoteText> buildNotesForCheckStubText(String text, int previousLineCount) {
    PaymentNoteText pnt = null;
    List<PaymentNoteText> pnts = new ArrayList<PaymentNoteText>();
    final String maxNoteLinesParam = parameterService.getParameterValueAsString(KfsParameterConstants.PRE_DISBURSEMENT_ALL.class, PdpParameterConstants.MAX_NOTE_LINES);
    int maxNoteLines;
    try {
        maxNoteLines = Integer.parseInt(maxNoteLinesParam);
    } catch (NumberFormatException nfe) {
        throw new IllegalArgumentException("Invalid Max Notes Lines parameter, value: " + maxNoteLinesParam + " cannot be converted to an integer");
    }
    // The WordUtils should be sufficient for the majority of cases.  This method will
    // word wrap the whole string based on the MAX_NOTE_LINE_SIZE, separating each wrapped
    // word by a newline character.  The 'wrap' method adds line feeds to the end causing
    // the character length to exceed the max length by 1, hence the need for the replace
    // method before splitting.
    String wrappedText = WordUtils.wrap(text, CuDisbursementVoucherConstants.DV_EXTRACT_MAX_NOTE_LINE_SIZE);
    String[] noteLines = wrappedText.replaceAll("[\r]", "").split("\\n");
    // Loop through all the note lines.
    for (String noteLine : noteLines) {
        if (previousLineCount < (maxNoteLines - 3) && !StringUtils.isEmpty(noteLine)) {
            // The only concern I have for this occurring is with URLs/email addresses.
            if (noteLine.length() > CuDisbursementVoucherConstants.DV_EXTRACT_MAX_NOTE_LINE_SIZE) {
                for (String choppedWord : chopWord(noteLine, CuDisbursementVoucherConstants.DV_EXTRACT_MAX_NOTE_LINE_SIZE)) {
                    // Make sure we're still under the maximum number of note lines.
                    if (previousLineCount < (maxNoteLines - 3) && !StringUtils.isEmpty(choppedWord)) {
                        pnt = new PaymentNoteText();
                        pnt.setCustomerNoteLineNbr(new KualiInteger(previousLineCount++));
                        pnt.setCustomerNoteText(CuDisbursementVoucherConstants.DV_EXTRACT_TYPED_NOTE_PREFIX_IDENTIFIER + choppedWord.replaceAll("\\n", "").trim());
                    } else // We can't add any additional note lines, or we'll exceed the maximum, therefore
                    // just break out of the loop early - there's nothing left to do.
                    {
                        break;
                    }
                }
            } else // This should be the most common case.  Simply create a new PaymentNoteText,
            // add the line at the correct line location.
            {
                pnt = new PaymentNoteText();
                pnt.setCustomerNoteLineNbr(new KualiInteger(previousLineCount++));
                pnt.setCustomerNoteText(CuDisbursementVoucherConstants.DV_EXTRACT_TYPED_NOTE_PREFIX_IDENTIFIER + noteLine.replaceAll("\\n", "").trim());
            }
            if (pnt != null) {
                // This should never be null at this point, but...
                pnts.add(pnt);
            }
        }
    }
    return pnts;
}
Also used : PaymentNoteText(org.kuali.kfs.pdp.businessobject.PaymentNoteText) KualiInteger(org.kuali.rice.core.api.util.type.KualiInteger) ArrayList(java.util.ArrayList) KfsParameterConstants(org.kuali.kfs.sys.service.impl.KfsParameterConstants)

Aggregations

PaymentNoteText (org.kuali.kfs.pdp.businessobject.PaymentNoteText)8 KualiInteger (org.kuali.rice.core.api.util.type.KualiInteger)7 PaymentDetail (org.kuali.kfs.pdp.businessobject.PaymentDetail)4 IOException (java.io.IOException)3 ArrayList (java.util.ArrayList)3 KualiDecimal (org.kuali.rice.core.api.util.type.KualiDecimal)3 BufferedWriter (java.io.BufferedWriter)2 FileWriter (java.io.FileWriter)2 SimpleDateFormat (java.text.SimpleDateFormat)2 Date (java.util.Date)2 Iterator (java.util.Iterator)2 CustomerProfile (org.kuali.kfs.pdp.businessobject.CustomerProfile)2 PaymentGroup (org.kuali.kfs.pdp.businessobject.PaymentGroup)2 KfsParameterConstants (org.kuali.kfs.sys.service.impl.KfsParameterConstants)2 CuDisbursementVoucherPayeeDetail (edu.cornell.kfs.fp.businessobject.CuDisbursementVoucherPayeeDetail)1 CuDisbursementVoucherDocument (edu.cornell.kfs.fp.document.CuDisbursementVoucherDocument)1 RecurringDisbursementVoucherDocument (edu.cornell.kfs.fp.document.RecurringDisbursementVoucherDocument)1 Date (java.sql.Date)1 HashMap (java.util.HashMap)1 List (java.util.List)1