Search in sources :

Example 16 with PaymentDetail

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

the class CuDisbursementVoucherExtractionHelperServiceImpl method buildPaymentDetail.

protected PaymentDetail buildPaymentDetail(DisbursementVoucherDocument document, Date processRunDate) {
    if (LOG.isDebugEnabled()) {
        LOG.debug("buildPaymentDetail() started");
    }
    final String maxNoteLinesParam = getParameterService().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");
    }
    PaymentDetail pd = new PaymentDetail();
    if (StringUtils.isNotEmpty(document.getDocumentHeader().getOrganizationDocumentNumber())) {
        pd.setOrganizationDocNbr(document.getDocumentHeader().getOrganizationDocumentNumber());
    }
    pd.setCustPaymentDocNbr(document.getDocumentNumber());
    pd.setInvoiceDate(new java.sql.Date(processRunDate.getTime()));
    pd.setOrigInvoiceAmount(document.getDisbVchrCheckTotalAmount());
    pd.setInvTotDiscountAmount(KualiDecimal.ZERO);
    pd.setInvTotOtherCreditAmount(KualiDecimal.ZERO);
    pd.setInvTotOtherDebitAmount(KualiDecimal.ZERO);
    pd.setInvTotShipAmount(KualiDecimal.ZERO);
    pd.setNetPaymentAmount(document.getDisbVchrCheckTotalAmount());
    pd.setPrimaryCancelledPayment(Boolean.FALSE);
    pd.setFinancialDocumentTypeCode(DisbursementVoucherConstants.DOCUMENT_TYPE_CHECKACH);
    pd.setFinancialSystemOriginCode(KFSConstants.ORIGIN_CODE_KUALI);
    // Handle accounts
    for (SourceAccountingLine sal : (List<? extends SourceAccountingLine>) document.getSourceAccountingLines()) {
        PaymentAccountDetail pad = new PaymentAccountDetail();
        pad.setFinChartCode(sal.getChartOfAccountsCode());
        pad.setAccountNbr(sal.getAccountNumber());
        if (StringUtils.isNotEmpty(sal.getSubAccountNumber())) {
            pad.setSubAccountNbr(sal.getSubAccountNumber());
        } else {
            pad.setSubAccountNbr(KFSConstants.getDashSubAccountNumber());
        }
        pad.setFinObjectCode(sal.getFinancialObjectCode());
        if (StringUtils.isNotEmpty(sal.getFinancialSubObjectCode())) {
            pad.setFinSubObjectCode(sal.getFinancialSubObjectCode());
        } else {
            pad.setFinSubObjectCode(KFSConstants.getDashFinancialSubObjectCode());
        }
        if (StringUtils.isNotEmpty(sal.getOrganizationReferenceId())) {
            pad.setOrgReferenceId(sal.getOrganizationReferenceId());
        }
        if (StringUtils.isNotEmpty(sal.getProjectCode())) {
            pad.setProjectCode(sal.getProjectCode());
        } else {
            pad.setProjectCode(KFSConstants.getDashProjectCode());
        }
        pad.setAccountNetAmount(sal.getAmount());
        pd.addAccountDetail(pad);
    }
    // Handle notes
    DisbursementVoucherPayeeDetail dvpd = document.getDvPayeeDetail();
    int line = 0;
    PaymentNoteText pnt = new PaymentNoteText();
    pnt.setCustomerNoteLineNbr(new KualiInteger(line++));
    pnt.setCustomerNoteText(CuDisbursementVoucherConstants.DV_EXTRACT_NOTE_PREFIX_PREPARER + document.getDisbVchrContactPersonName() + " " + document.getDisbVchrContactPhoneNumber());
    pd.addNote(pnt);
    String dvSpecialHandlingPersonName = null;
    String dvSpecialHandlingLine1Address = null;
    String dvSpecialHandlingLine2Address = null;
    String dvSpecialHandlingCity = null;
    String dvSpecialHandlingState = null;
    String dvSpecialHandlingZip = null;
    dvSpecialHandlingPersonName = dvpd.getDisbVchrSpecialHandlingPersonName();
    dvSpecialHandlingLine1Address = dvpd.getDisbVchrSpecialHandlingLine1Addr();
    dvSpecialHandlingLine2Address = dvpd.getDisbVchrSpecialHandlingLine2Addr();
    dvSpecialHandlingCity = dvpd.getDisbVchrSpecialHandlingCityName();
    dvSpecialHandlingState = dvpd.getDisbVchrSpecialHandlingStateCode();
    dvSpecialHandlingZip = dvpd.getDisbVchrSpecialHandlingZipCode();
    if (StringUtils.isNotEmpty(dvSpecialHandlingPersonName)) {
        pnt = new PaymentNoteText();
        pnt.setCustomerNoteLineNbr(new KualiInteger(line++));
        pnt.setCustomerNoteText("Send Check To: " + dvSpecialHandlingPersonName);
        if (LOG.isDebugEnabled()) {
            LOG.debug("Creating special handling person name note: " + pnt.getCustomerNoteText());
        }
        pd.addNote(pnt);
    }
    if (StringUtils.isNotEmpty(dvSpecialHandlingLine1Address)) {
        pnt = new PaymentNoteText();
        pnt.setCustomerNoteLineNbr(new KualiInteger(line++));
        pnt.setCustomerNoteText(CuDisbursementVoucherConstants.DV_EXTRACT_NOTE_PREFIX_SPECIAL_HANDLING_ADDRESS1 + dvSpecialHandlingLine1Address);
        if (LOG.isDebugEnabled()) {
            LOG.debug("Creating special handling address 1 note: " + pnt.getCustomerNoteText());
        }
        pd.addNote(pnt);
    }
    if (StringUtils.isNotEmpty(dvSpecialHandlingLine2Address)) {
        pnt = new PaymentNoteText();
        pnt.setCustomerNoteLineNbr(new KualiInteger(line++));
        pnt.setCustomerNoteText(CuDisbursementVoucherConstants.DV_EXTRACT_NOTE_PREFIX_SPECIAL_HANDLING_ADDRESS2 + dvSpecialHandlingLine2Address);
        if (LOG.isDebugEnabled()) {
            LOG.debug("Creating special handling address 2 note: " + pnt.getCustomerNoteText());
        }
        pd.addNote(pnt);
    }
    if (StringUtils.isNotEmpty(dvSpecialHandlingCity)) {
        pnt = new PaymentNoteText();
        pnt.setCustomerNoteLineNbr(new KualiInteger(line++));
        pnt.setCustomerNoteText(CuDisbursementVoucherConstants.DV_EXTRACT_NOTE_PREFIX_SPECIAL_HANDLING_ADDRESS3 + dvSpecialHandlingCity + ", " + dvSpecialHandlingState + " " + dvSpecialHandlingZip);
        if (LOG.isDebugEnabled()) {
            LOG.debug("Creating special handling city note: " + pnt.getCustomerNoteText());
        }
        pd.addNote(pnt);
    }
    if (document.isDisbVchrAttachmentCode()) {
        pnt = new PaymentNoteText();
        pnt.setCustomerNoteLineNbr(new KualiInteger(line++));
        pnt.setCustomerNoteText("Attachment Included");
        if (LOG.isDebugEnabled()) {
            LOG.debug("create attachment note: " + pnt.getCustomerNoteText());
        }
        pd.addNote(pnt);
    }
    String paymentReasonCode = dvpd.getDisbVchrPaymentReasonCode();
    if (/*REFACTORME*/
    getParameterEvaluatorService().getParameterEvaluator(DisbursementVoucherDocument.class, DisbursementVoucherConstants.NONEMPLOYEE_TRAVEL_PAY_REASONS_PARM_NM, paymentReasonCode).evaluationSucceeds()) {
        DisbursementVoucherNonEmployeeTravel dvnet = document.getDvNonEmployeeTravel();
        pnt = new PaymentNoteText();
        pnt.setCustomerNoteLineNbr(new KualiInteger(line++));
        pnt.setCustomerNoteText("Reimbursement associated with " + dvnet.getDisbVchrServicePerformedDesc());
        if (LOG.isDebugEnabled()) {
            LOG.debug("Creating non employee travel notes: " + pnt.getCustomerNoteText());
        }
        pd.addNote(pnt);
        pnt = new PaymentNoteText();
        pnt.setCustomerNoteLineNbr(new KualiInteger(line++));
        pnt.setCustomerNoteText("The total per diem amount for your daily expenses is " + dvnet.getDisbVchrPerdiemActualAmount());
        if (LOG.isDebugEnabled()) {
            LOG.debug("Creating non employee travel notes: " + pnt.getCustomerNoteText());
        }
        pd.addNote(pnt);
        if (dvnet.getDisbVchrPersonalCarAmount() != null && dvnet.getDisbVchrPersonalCarAmount().compareTo(KualiDecimal.ZERO) != 0) {
            pnt = new PaymentNoteText();
            pnt.setCustomerNoteLineNbr(new KualiInteger(line++));
            pnt.setCustomerNoteText("The total dollar amount for your vehicle mileage is " + dvnet.getDisbVchrPersonalCarAmount());
            if (LOG.isDebugEnabled()) {
                LOG.debug("Creating non employee travel vehicle note: " + pnt.getCustomerNoteText());
            }
            pd.addNote(pnt);
            for (DisbursementVoucherNonEmployeeExpense exp : (List<DisbursementVoucherNonEmployeeExpense>) dvnet.getDvNonEmployeeExpenses()) {
                if (line < (maxNoteLines - 8)) {
                    pnt = new PaymentNoteText();
                    pnt.setCustomerNoteLineNbr(new KualiInteger(line++));
                    pnt.setCustomerNoteText(exp.getDisbVchrExpenseCompanyName() + " " + exp.getDisbVchrExpenseAmount());
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("Creating non employee travel expense note: " + pnt.getCustomerNoteText());
                    }
                    pd.addNote(pnt);
                }
            }
        }
    } else if (/*REFACTORME*/
    getParameterEvaluatorService().getParameterEvaluator(DisbursementVoucherDocument.class, DisbursementVoucherConstants.PREPAID_TRAVEL_PAYMENT_REASONS_PARM_NM, paymentReasonCode).evaluationSucceeds()) {
        pnt = new PaymentNoteText();
        pnt.setCustomerNoteLineNbr(new KualiInteger(line++));
        pnt.setCustomerNoteText("Payment is for the following individuals/charges:");
        pd.addNote(pnt);
        if (LOG.isDebugEnabled()) {
            LOG.info("Creating prepaid travel note note: " + pnt.getCustomerNoteText());
        }
        DisbursementVoucherPreConferenceDetail dvpcd = document.getDvPreConferenceDetail();
        for (DisbursementVoucherPreConferenceRegistrant dvpcr : (List<DisbursementVoucherPreConferenceRegistrant>) dvpcd.getDvPreConferenceRegistrants()) {
            if (line < (maxNoteLines - 8)) {
                pnt = new PaymentNoteText();
                pnt.setCustomerNoteLineNbr(new KualiInteger(line++));
                pnt.setCustomerNoteText(dvpcr.getDvConferenceRegistrantName() + " " + dvpcr.getDisbVchrExpenseAmount());
                if (LOG.isDebugEnabled()) {
                    LOG.debug("Creating pre-paid conference registrants note: " + pnt.getCustomerNoteText());
                }
                pd.addNote(pnt);
            }
        }
    }
    // Get the original, raw form, note text from the DV document.
    final String text = document.getDisbVchrCheckStubText();
    if (!StringUtils.isBlank(text)) {
        pd.addNotes(getPaymentSourceHelperService().buildNotesForCheckStubText(text, line));
    }
    return pd;
}
Also used : DisbursementVoucherNonEmployeeTravel(org.kuali.kfs.fp.businessobject.DisbursementVoucherNonEmployeeTravel) DisbursementVoucherPreConferenceDetail(org.kuali.kfs.fp.businessobject.DisbursementVoucherPreConferenceDetail) KualiInteger(org.kuali.rice.core.api.util.type.KualiInteger) KfsParameterConstants(org.kuali.kfs.sys.service.impl.KfsParameterConstants) DisbursementVoucherNonEmployeeExpense(org.kuali.kfs.fp.businessobject.DisbursementVoucherNonEmployeeExpense) DisbursementVoucherPayeeDetail(org.kuali.kfs.fp.businessobject.DisbursementVoucherPayeeDetail) CuDisbursementVoucherPayeeDetail(edu.cornell.kfs.fp.businessobject.CuDisbursementVoucherPayeeDetail) SourceAccountingLine(org.kuali.kfs.sys.businessobject.SourceAccountingLine) DisbursementVoucherPreConferenceRegistrant(org.kuali.kfs.fp.businessobject.DisbursementVoucherPreConferenceRegistrant) Date(java.sql.Date) PaymentDetail(org.kuali.kfs.pdp.businessobject.PaymentDetail) PaymentNoteText(org.kuali.kfs.pdp.businessobject.PaymentNoteText) ArrayList(java.util.ArrayList) List(java.util.List) RecurringDisbursementVoucherDocument(edu.cornell.kfs.fp.document.RecurringDisbursementVoucherDocument) DisbursementVoucherDocument(org.kuali.kfs.fp.document.DisbursementVoucherDocument) CuDisbursementVoucherDocument(edu.cornell.kfs.fp.document.CuDisbursementVoucherDocument) PaymentAccountDetail(org.kuali.kfs.pdp.businessobject.PaymentAccountDetail)

Example 17 with PaymentDetail

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

the class CuDisbursementVoucherExtractionHelperServiceImpl method createPaymentGroup.

@Override
public PaymentGroup createPaymentGroup(DisbursementVoucherDocument document, Date processRunDate) {
    if (LOG.isDebugEnabled()) {
        LOG.debug("createPaymentGroupForDisbursementVoucher() started");
    }
    PaymentGroup pg = new PaymentGroup();
    pg.setCombineGroups(Boolean.TRUE);
    pg.setCampusAddress(Boolean.FALSE);
    CuDisbursementVoucherPayeeDetail pd = businessObjectService.findBySinglePrimaryKey(CuDisbursementVoucherPayeeDetail.class, document.getDocumentNumber());
    String rc = pd.getDisbVchrPaymentReasonCode();
    if (KFSConstants.PaymentPayeeTypes.CUSTOMER.equals(document.getDvPayeeDetail().getDisbursementVoucherPayeeTypeCode())) {
        pg.setPayeeIdTypeCd(PdpConstants.PayeeIdTypeCodes.CUSTOMER);
        pg.setTaxablePayment(Boolean.FALSE);
    } else // If the payee is an employee, set these flags accordingly
    if ((pd.isVendor() && SpringContext.getBean(VendorService.class).isVendorInstitutionEmployee(pd.getDisbVchrVendorHeaderIdNumberAsInteger())) || document.getDvPayeeDetail().isEmployee()) {
        pg.setEmployeeIndicator(Boolean.TRUE);
        pg.setPayeeIdTypeCd(PdpConstants.PayeeIdTypeCodes.EMPLOYEE);
        pg.setTaxablePayment(!/*REFACTORME*/
        getParameterEvaluatorService().getParameterEvaluator(DisbursementVoucherDocument.class, DisbursementVoucherConstants.RESEARCH_PAYMENT_REASONS_PARM_NM, rc).evaluationSucceeds() && !getParameterService().getParameterValueAsString(DisbursementVoucherDocument.class, DisbursementVoucherConstants.PAYMENT_REASON_CODE_RENTAL_PAYMENT_PARM_NM).equals(rc) && !getParameterService().getParameterValueAsString(DisbursementVoucherDocument.class, DisbursementVoucherConstants.PAYMENT_REASON_CODE_ROYALTIES_PARM_NM).equals(rc));
    } else // If the payee is an alumni or student, set these flags accordingly
    if (pd.isStudent() || pd.isAlumni()) {
        pg.setPayeeIdTypeCd(PdpConstants.PayeeIdTypeCodes.ENTITY);
        // All payments are taxable except research participant, rental & royalties
        pg.setTaxablePayment(!SpringContext.getBean(ParameterEvaluatorService.class).getParameterEvaluator(CuDisbursementVoucherDocument.class, DisbursementVoucherConstants.RESEARCH_PAYMENT_REASONS_PARM_NM, rc).evaluationSucceeds() && !CuDisbursementVoucherConstants.PaymentReasonCodes.RENTAL_PAYMENT.equals(rc) && !CuDisbursementVoucherConstants.PaymentReasonCodes.ROYALTIES.equals(rc));
    } else {
        // These are taxable
        VendorDetail vendDetail = getVendorService().getVendorDetail(pd.getDisbVchrVendorHeaderIdNumberAsInteger(), pd.getDisbVchrVendorDetailAssignedIdNumberAsInteger());
        String vendorOwnerCode = vendDetail.getVendorHeader().getVendorOwnershipCode();
        String vendorOwnerCategoryCode = vendDetail.getVendorHeader().getVendorOwnershipCategoryCode();
        String payReasonCode = pd.getDisbVchrPaymentReasonCode();
        pg.setPayeeIdTypeCd(PdpConstants.PayeeIdTypeCodes.VENDOR_ID);
        // Assume it is not taxable until proven otherwise
        pg.setTaxablePayment(Boolean.FALSE);
        pg.setPayeeOwnerCd(vendorOwnerCode);
        ParameterEvaluator parameterEvaluator1 = /*REFACTORME*/
        getParameterEvaluatorService().getParameterEvaluator(DvToPdpExtractStep.class, PdpParameterConstants.TAXABLE_PAYMENT_REASON_CODES_BY_OWNERSHIP_CODES_PARAMETER_NAME, PdpParameterConstants.NON_TAXABLE_PAYMENT_REASON_CODES_BY_OWNERSHIP_CODES_PARAMETER_NAME, vendorOwnerCode, payReasonCode);
        ParameterEvaluator parameterEvaluator2 = /*REFACTORME*/
        getParameterEvaluatorService().getParameterEvaluator(DvToPdpExtractStep.class, PdpParameterConstants.TAXABLE_PAYMENT_REASON_CODES_BY_CORPORATION_OWNERSHIP_TYPE_CATEGORY_PARAMETER_NAME, PdpParameterConstants.NON_TAXABLE_PAYMENT_REASON_CODES_BY_CORPORATION_OWNERSHIP_TYPE_CATEGORY_PARAMETER_NAME, vendorOwnerCategoryCode, payReasonCode);
        if (parameterEvaluator1.evaluationSucceeds()) {
            pg.setTaxablePayment(Boolean.TRUE);
        } else if (getParameterService().getParameterValueAsString(DvToPdpExtractStep.class, PdpParameterConstants.CORPORATION_OWNERSHIP_TYPE_PARAMETER_NAME).equals("CP") && StringUtils.isEmpty(vendorOwnerCategoryCode) && /*REFACTORME*/
        getParameterEvaluatorService().getParameterEvaluator(DvToPdpExtractStep.class, PdpParameterConstants.TAXABLE_PAYMENT_REASON_CODES_FOR_BLANK_CORPORATION_OWNERSHIP_TYPE_CATEGORIES_PARAMETER_NAME, payReasonCode).evaluationSucceeds()) {
            pg.setTaxablePayment(Boolean.TRUE);
        } else if (getParameterService().getParameterValueAsString(DvToPdpExtractStep.class, PdpParameterConstants.CORPORATION_OWNERSHIP_TYPE_PARAMETER_NAME).equals("CP") && !StringUtils.isEmpty(vendorOwnerCategoryCode) && parameterEvaluator2.evaluationSucceeds()) {
            pg.setTaxablePayment(Boolean.TRUE);
        }
    }
    pg.setCity(pd.getDisbVchrPayeeCityName());
    pg.setCountry(pd.getDisbVchrPayeeCountryCode());
    pg.setLine1Address(pd.getDisbVchrPayeeLine1Addr());
    pg.setLine2Address(pd.getDisbVchrPayeeLine2Addr());
    pg.setPayeeName(pd.getDisbVchrPayeePersonName());
    pg.setPayeeId(pd.getDisbVchrPayeeIdNumber());
    pg.setState(pd.getDisbVchrPayeeStateCode());
    pg.setZipCd(pd.getDisbVchrPayeeZipCode());
    pg.setPaymentDate(document.getDisbursementVoucherDueDate());
    pg.setProcessImmediate(document.isImmediatePaymentIndicator());
    pg.setPymtAttachment(document.isDisbVchrAttachmentCode());
    pg.setPymtSpecialHandling(document.isDisbVchrSpecialHandlingCode());
    pg.setNraPayment(pd.isDisbVchrAlienPaymentCode());
    pg.setBankCode(document.getDisbVchrBankCode());
    pg.setPaymentStatusCode(PdpConstants.PaymentStatusCodes.OPEN);
    // now add the payment detail
    final PaymentDetail paymentDetail = buildPaymentDetail(document, processRunDate);
    pg.addPaymentDetails(paymentDetail);
    paymentDetail.setPaymentGroup(pg);
    return pg;
}
Also used : PaymentGroup(org.kuali.kfs.pdp.businessobject.PaymentGroup) VendorService(org.kuali.kfs.vnd.document.service.VendorService) VendorDetail(org.kuali.kfs.vnd.businessobject.VendorDetail) CuDisbursementVoucherDocument(edu.cornell.kfs.fp.document.CuDisbursementVoucherDocument) CuDisbursementVoucherPayeeDetail(edu.cornell.kfs.fp.businessobject.CuDisbursementVoucherPayeeDetail) PaymentDetail(org.kuali.kfs.pdp.businessobject.PaymentDetail) ParameterEvaluator(org.kuali.rice.core.api.parameter.ParameterEvaluator) DvToPdpExtractStep(org.kuali.kfs.fp.batch.DvToPdpExtractStep) RecurringDisbursementVoucherDocument(edu.cornell.kfs.fp.document.RecurringDisbursementVoucherDocument) DisbursementVoucherDocument(org.kuali.kfs.fp.document.DisbursementVoucherDocument) CuDisbursementVoucherDocument(edu.cornell.kfs.fp.document.CuDisbursementVoucherDocument)

Example 18 with PaymentDetail

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

the class CuAchAdviceNotificationServiceImpl method sendAdviceNotifications.

/**
 * Set to NonTransactional so the payment advice email sent date will be updated and saved after the email is sent
 *
 * @see org.kuali.kfs.pdp.batch.service.AchAdviceNotificationService#sendAdviceNotifications()
 */
@NonTransactional
public void sendAdviceNotifications() {
    if (achBundlerHelperService.shouldBundleAchPayments()) {
        // ACH payments were bundled so the corresponding advice email notifications should also be bundled
        HashSet<Integer> disbNbrs = achBundlerAdviceDao.getDistinctDisbursementNumbersForAchPaymentsNeedingAdviceNotification();
        for (Iterator<Integer> disbIter = disbNbrs.iterator(); disbIter.hasNext(); ) {
            Integer disbursementNbr = disbIter.next();
            // get all payment details to include in the advice based on disbursement number which is associated to a single vendor
            List<PaymentDetail> paymentDetails = achBundlerAdviceDao.getAchPaymentDetailsNeedingAdviceNotificationByDisbursementNumber(disbursementNbr);
            // get one payment detail record so that we can get the needed payment group record, since all payment details are for the same vendor, all payment group records should match
            Iterator<PaymentDetail> paymentDetailsIter = paymentDetails.iterator();
            PaymentDetail payDetail = paymentDetailsIter.next();
            PaymentGroup payGroup = payDetail.getPaymentGroup();
            CustomerProfile customer = payGroup.getBatch().getCustomerProfile();
            // verify the customer profile is setup to create advices
            if (customer.getAdviceCreate()) {
                pdpEmailService.sendAchAdviceEmail(payGroup, paymentDetails, customer);
            }
            // update sent date on the payment, must loop through all payment details because payment groups could be unique.
            for (Iterator<PaymentDetail> paymentDetailsIter2 = paymentDetails.iterator(); paymentDetailsIter2.hasNext(); ) {
                PaymentDetail pd = paymentDetailsIter2.next();
                PaymentGroup pg = pd.getPaymentGroup();
                pg.setAdviceEmailSentDate(dateTimeService.getCurrentTimestamp());
                businessObjectService.save(pg);
            }
        }
    // for each disb number
    } else {
        // Execute the original KFS code to send unbundled ACH advice email notifications with a
        // change to the looping on the payment detail records as noted below with notation KFSPTS-1460
        // get list of payments to send notification for
        List<PaymentGroup> paymentGroups = paymentGroupService.getAchPaymentsNeedingAdviceNotification();
        for (PaymentGroup paymentGroup : paymentGroups) {
            CustomerProfile customer = paymentGroup.getBatch().getCustomerProfile();
            // verify the customer profile is setup to create advices
            if (customer.getAdviceCreate()) {
                // KFSPTS-1460 - for loop removed
                // for (PaymentDetail paymentDetail : paymentGroup.getPaymentDetails()) {
                // pdpEmailService.sendAchAdviceEmail(paymentGroup, paymentDetail, customer);
                // }
                List<PaymentDetail> paymentDetails = paymentGroup.getPaymentDetails();
                pdpEmailService.sendAchAdviceEmail(paymentGroup, paymentDetails, customer);
            }
            // update advice sent date on payment
            paymentGroup.setAdviceEmailSentDate(dateTimeService.getCurrentTimestamp());
            businessObjectService.save(paymentGroup);
        }
    }
}
Also used : PaymentGroup(org.kuali.kfs.pdp.businessobject.PaymentGroup) PaymentDetail(org.kuali.kfs.pdp.businessobject.PaymentDetail) CustomerProfile(org.kuali.kfs.pdp.businessobject.CustomerProfile) NonTransactional(org.kuali.kfs.sys.service.NonTransactional)

Example 19 with PaymentDetail

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

the class CuExtractPaymentServiceImpl method writeExtractAchFileMellonBankFastTrack.

// Adjusted the code in this method to deal with the output of the payment details based upon
// payee with multiple payments in the same payment group.
protected void writeExtractAchFileMellonBankFastTrack(PaymentStatus extractedStatus, String filename, Date processDate, SimpleDateFormat sdf, List<String> notificationEmailAddresses) {
    BufferedWriter os = null;
    // Used in the Fast Track file HEADER record
    sdf = new SimpleDateFormat("yyyyMMddHHmmss");
    // headerDate must be day after processDate to prevent additional cost for same day ACH payments
    Date headerDate = calculateHeaderDate(processDate);
    // Used in the Fast Track file PAY01000 record
    SimpleDateFormat sdfPAY1000Rec = new SimpleDateFormat("yyyyMMdd");
    // 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 = "";
    boolean specialHandlingCode = false;
    boolean attachmentCode = false;
    String divisionCode = "";
    String achCode = "";
    String dateQualifer = "";
    boolean wroteFastTrackHeaderRecords = false;
    // Change the filename so that it ends in .txt instead of .xml.
    filename = filename.replace(".xml", ".txt");
    int totalRecordCount = 0;
    KualiDecimal totalPaymentAmounts = KualiDecimal.ZERO;
    CustomerProfile cp = null;
    String sCountryName = "";
    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";
        HashSet<String> bankCodes = this.getAchBundlerHelperService().getDistinctBankCodesForPendingAchPayments();
        for (String bankCode : bankCodes) {
            HashSet<Integer> disbNbrs = this.getAchBundlerHelperService().getDistinctDisbursementNumbersForPendingAchPaymentsByBankCode(bankCode);
            for (Iterator<Integer> iter = disbNbrs.iterator(); iter.hasNext(); ) {
                Integer disbursementNbr = iter.next();
                boolean first = true;
                // compute total net amount as it is needed on first payment detail
                KualiDecimal totalNetAmount = new KualiDecimal(0);
                Iterator<PaymentDetail> payDetailIter = this.getAchBundlerHelperService().getPendingAchPaymentDetailsByDisbursementNumberAndBank(disbursementNbr, bankCode);
                while (payDetailIter.hasNext()) {
                    PaymentDetail pd = payDetailIter.next();
                    totalNetAmount = totalNetAmount.add(pd.getNetPaymentAmount());
                }
                Iterator<PaymentDetail> paymentDetails = this.getAchBundlerHelperService().getPendingAchPaymentDetailsByDisbursementNumberAndBank(disbursementNbr, bankCode);
                while (paymentDetails.hasNext()) {
                    PaymentDetail pd = paymentDetails.next();
                    PaymentGroup pg = pd.getPaymentGroup();
                    // Get our Bank Account Number and our bank routing number
                    ourBankAccountNumber = pg.getBank().getBankAccountNumber().replace("-", "");
                    ourBankRoutingNumber = pg.getBank().getBankRoutingNumber();
                    if (!wroteFastTrackHeaderRecords) {
                        // open the file for writing
                        os = new BufferedWriter(new FileWriter(filename));
                        // Write the Fast Track header record (FIL00010) once for each file
                        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.  This has to be different for ACH than it is for CHECKS per BNY Mellon.
                        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(headerDate) + // File Date and Time - 14 Bytes  YYMMDD format
                        cDelim + // Reserved Field - 3 Bytes
                        cDelim + "\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();
                            os.write("FIL00020" + cDelim + emailAddress + cDelim + cDelim + "\n");
                            totalRecordCount = totalRecordCount + 1;
                        }
                        wroteFastTrackHeaderRecords = true;
                    }
                    if (first) {
                        // Get country name for code
                        int CountryNameMaxLength = 15;
                        Country country = null;
                        // KFSUPGRADE-859 check if the country name is not blank, otherwise country service will throw exception
                        if (StringUtils.isNotBlank(pg.getCountry())) {
                            country = this.getCountryService().getCountry(pg.getCountry());
                        }
                        if (country != null) {
                            sCountryName = country.getName().substring(0, ((country.getName().length() >= CountryNameMaxLength) ? CountryNameMaxLength : country.getName().length()));
                            if (sCountryName.toUpperCase().contains("UNITED STATES"))
                                sCountryName = "";
                        } else if (ObjectUtils.isNotNull(pg.getCountry()))
                            sCountryName = pg.getCountry().substring(0, ((pg.getCountry().length() >= CountryNameMaxLength) ? CountryNameMaxLength : pg.getCountry().length()));
                        if (sCountryName.toUpperCase().contains("UNITED STATES"))
                            sCountryName = "";
                        else
                            sCountryName = "";
                        int dvCodeInt = 0;
                        // 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());
                            else
                                LOG.error("No customer profile exists for payee name: " + pg.getPayeeName());
                        }
                        // Get special handling indicator
                        specialHandlingCode = pg.getPymtSpecialHandling();
                        // Get attachment indicator
                        attachmentCode = pg.getPymtAttachment();
                        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);
                        // Determine the ACH Code to send down.  Here are the rules:
                        // 1.  If they are a vendor and they've selected checking account, the ACH code is CTX (vendors can only have ACH to checking)
                        // 2.  If they are a vendor and they've selected savings account, the ACH code is PPD (sometimes sole proprietors are vendors and they use personal not corporate accounts)
                        // 3.  If they are NOT a vendor, then the ACH code is always PPD (allows employees to have their's deposited in checking or savings)
                        // Determine which ACH code to use: CTX for corporate accounts, or PPD for Personal accounts
                        // String payeeId = paymentGroup.getPayeeId();                          // Returns the ID of the payee and is the vendor number if the next var indicates that
                        // String payeeIdTypeDesc = paymentGroup.getPayeeIdTypeDesc();          // Returns "Vendor Number" if the payeeID is a vendor number
                        String AchBankRoutingNbr = "";
                        String AchAccountType = "DA";
                        String AchBankAccountNumber = "";
                        String CheckNumber = "";
                        // payeeIdTypeCode returns a "V" if it is a vendor
                        boolean isVendor = (pg.getPayeeIdTypeCd().equals("V")) ? true : false;
                        String kfsAccountType = "";
                        if (ObjectUtils.isNotNull(pg.getAchAccountType()))
                            // Returns either a 22 for checking or a 32 for Savings account
                            kfsAccountType = pg.getAchAccountType();
                        // For Mellon this converts to either DA (checking) or SG (savings)
                        if (kfsAccountType.startsWith("22")) {
                            AchAccountType = "DA";
                        }
                        if (kfsAccountType.startsWith("32")) {
                            AchAccountType = "SG";
                        }
                        if (kfsAccountType.contains("PPD")) {
                            achCode = "PPD";
                        }
                        if (kfsAccountType.contains("CTX")) {
                            achCode = "CTX";
                        }
                        if (ObjectUtils.isNotNull(pg.getAchBankRoutingNbr()))
                            AchBankRoutingNbr = pg.getAchBankRoutingNbr();
                        if (ObjectUtils.isNotNull(pg.getAchAccountNumber().getAchBankAccountNbr()))
                            AchBankAccountNumber = pg.getAchAccountNumber().getAchBankAccountNbr().toString();
                        if (ObjectUtils.isNotNull(pg.getDisbursementNbr().toString()))
                            CheckNumber = pg.getDisbursementNbr().toString();
                        // Write only 1 PAY01000 record for each payee
                        os.write(// Record Type - 8 bytes
                        "PAY01000" + cDelim + "1" + // 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 + "ACH" + // ACH= ACH Payment method - 3 Bytes
                        cDelim + achCode + // PPD is used for ACH payments to a personal bank account - 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 + "1150532082" + // Originating company identifier - 10 bytes - This has to be different for ACH than it is for CHECKS per BNY Mellon
                        cDelim + "01" + // Receiving bank id qualifier - 2 bytes
                        cDelim + AchBankRoutingNbr + // Receiving bank id - 12 bytes
                        cDelim + AchAccountType + // Receiving account number qualifier - 3 bytes - must be DA for checking or SG for savings.
                        cDelim + AchBankAccountNumber + // Receiving account number - 35 bytes
                        cDelim + sdfPAY1000Rec.format(processDate) + // Effective date - 8 bytes - YYMMDDHHMMSS
                        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 + cDelim + "\n");
                        totalPaymentAmounts = totalPaymentAmounts.add(totalNetAmount);
                        // Write the Payer Detail(PDT02010) record for the payer (us) (only one per payee)
                        os.write(// Record Type - 8 bytes
                        "PDT02010" + cDelim + "PR" + // Name qualifier - 3 bytes
                        cDelim + // ID code qualifier - 2 bytes
                        cDelim + // ID code - 80 bytes
                        cDelim + PAYER_NAME + // Name - 60 bytes
                        cDelim + // Additional name 1 - 60 bytes
                        cDelim + // Additional name 2 - 60 bytes
                        cDelim + PAYER_ADDRESS_LINE1 + // Address line 1 - 55 bytes
                        cDelim + PAYER_ADDRESS_LINE2 + // Address line 2 - 55 bytes
                        cDelim + // Address line 3 - 55 bytes
                        cDelim + // Address line 4 - 55 bytes
                        cDelim + // Address line 5 - 55 bytes
                        cDelim + // Address line 6 - 55 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 payee detail (PDT02010) record for the payee (them) (only one per payee)
                        // temp variables to observe length limitations
                        int AddrMaxLength = 35;
                        int CityMaxLength = 30;
                        int StateMaxLength = 2;
                        int ZipMaxLength = 15;
                        int PayeeNameMaxLength = 35;
                        String PayeeName = "";
                        String AddrLine1 = "";
                        String AddrLine2 = "";
                        String AddrLine3 = "";
                        String AddrLine4 = "";
                        String City = "";
                        String State = "";
                        String Zip = "";
                        if (ObjectUtils.isNotNull(pg.getPayeeName()))
                            PayeeName = pg.getPayeeName().substring(0, ((pg.getPayeeName().length() >= PayeeNameMaxLength) ? PayeeNameMaxLength : pg.getPayeeName().length()));
                        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()))
                            City = pg.getCity().substring(0, ((pg.getCity().length() >= CityMaxLength) ? CityMaxLength : pg.getCity().length()));
                        if (ObjectUtils.isNotNull(pg.getState()))
                            State = pg.getState().substring(0, ((pg.getState().length() >= StateMaxLength) ? StateMaxLength : pg.getState().length()));
                        if (ObjectUtils.isNotNull(pg.getZipCd()))
                            Zip = (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
                        cDelim + // ID code qualifier - 2 bytes
                        cDelim + // ID code - 80 bytes
                        cDelim + PayeeName + // Name - 30 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 + City + // City - 30 bytes
                        cDelim + State + // State/Province - 2 bytes
                        cDelim + Zip + // Postal code - 15 bytes
                        cDelim + // Country code - 3 bytes
                        cDelim + sCountryName + // Country name - 30 bytes  (do not use is Country Code is used)
                        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 one for each PDT02010 record
                        totalRecordCount = totalRecordCount + 3;
                        // Set this here so it is only executed once per payee
                        first = false;
                    }
                    // If (first)
                    // Write the REM03020 record
                    // Set up remittanceIdCode and remittanceIdText based on whether its a DV or something else.
                    String remittanceIdCode = (subUnitCode.equals(CuDisbursementVoucherConstants.DV_EXTRACT_SUB_UNIT_CODE)) ? "TN" : "IV";
                    String remittanceIdText = (subUnitCode.equals(CuDisbursementVoucherConstants.DV_EXTRACT_SUB_UNIT_CODE)) ? ObjectUtils.isNotNull(pd.getCustPaymentDocNbr()) ? "Doc No:" + pd.getCustPaymentDocNbr() : "" : ObjectUtils.isNotNull(pd.getInvoiceNbr()) ? pd.getInvoiceNbr() : "";
                    // 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");
                            break;
                        }
                    }
                    if (ObjectUtils.isNotNull(pd.getOrigInvoiceAmount())) {
                        ftTotalAmount = pd.getOrigInvoiceAmount().toString();
                        if (ftTotalAmount.length() > 18) {
                            LOG.error("Original Invoice Amount is more than 18 bytes");
                            break;
                        }
                    }
                    if (ObjectUtils.isNotNull(pd.getInvTotDiscountAmount())) {
                        ftDiscountAmt = pd.getInvTotDiscountAmount().toString();
                        if (ftDiscountAmt.length() > 18) {
                            LOG.error("Discount Amount is more than 18 bytes");
                            break;
                        }
                    }
                    String InvoiceDate = "";
                    if (ObjectUtils.isNotNull(pd.getInvoiceDate())) {
                        InvoiceDate = pd.getInvoiceDate().toString().replace("-", "");
                        dateQualifer = "003";
                    }
                    os.write(// Record type - 8 bytes
                    "REM03020" + cDelim + remittanceIdCode + // Remittance qualifier code - 3 bytes
                    cDelim + remittanceIdText + // Remittance ID - 50 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 - NOT USED PER SPEC
                    cDelim + // Note 2 - 80 bytes - NOT USED PER SPEC
                    cDelim + // Ref qualifier 1
                    cDelim + // Ref ID 1
                    cDelim + // Ref description 1
                    cDelim + // Ref qualifier 2
                    cDelim + // Ref ID 2
                    cDelim + // Ref description 2
                    cDelim + // Ref qualifier 3
                    cDelim + // Ref ID 3
                    cDelim + // Ref description 3
                    cDelim + // Ref qualifier 4
                    cDelim + // Ref ID 4
                    cDelim + // Ref description 4
                    cDelim + dateQualifer + // Date qualifier 1
                    cDelim + InvoiceDate + // Date 1
                    cDelim + // Date qualifier 2
                    cDelim + // Date 2
                    cDelim + // Date qualifier 3
                    cDelim + // Date 3
                    cDelim + // Date qualifier 4
                    cDelim + cDelim + "\n");
                    // One for the REM03020 record
                    totalRecordCount = totalRecordCount + 1;
                }
            // while there are payment details
            }
        // for each disbNbr
        }
        if (wroteFastTrackHeaderRecords) {
            // Need to update the total record count here to make sure it includes the trailer record
            totalRecordCount = totalRecordCount + 1;
            // Now write the 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("extractAchPayments() Problem reading file:  " + filename, ie);
        throw new IllegalArgumentException("Error writing to output file: " + ie.getMessage());
    } catch (Exception ex) {
        LOG.error("General Exception with writeExtractBundledAchFileMellonBankFastTrack().  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(filename, filename + ".READY");
            } catch (IOException ie) {
                // Not much we can do now
                LOG.error("IOException in extractAchPayments():  " + filename, ie);
            }
        }
    }
}
Also used : PaymentGroup(org.kuali.kfs.pdp.businessobject.PaymentGroup) FileWriter(java.io.FileWriter) CustomerProfile(org.kuali.kfs.pdp.businessobject.CustomerProfile) IOException(java.io.IOException) Date(java.util.Date) IOException(java.io.IOException) BufferedWriter(java.io.BufferedWriter) KualiInteger(org.kuali.rice.core.api.util.type.KualiInteger) PaymentDetail(org.kuali.kfs.pdp.businessobject.PaymentDetail) KualiDecimal(org.kuali.rice.core.api.util.type.KualiDecimal) Country(org.kuali.rice.location.api.country.Country) SimpleDateFormat(java.text.SimpleDateFormat)

Example 20 with PaymentDetail

use of org.kuali.kfs.pdp.businessobject.PaymentDetail 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)

Aggregations

PaymentDetail (org.kuali.kfs.pdp.businessobject.PaymentDetail)25 PaymentGroup (org.kuali.kfs.pdp.businessobject.PaymentGroup)11 KualiInteger (org.kuali.rice.core.api.util.type.KualiInteger)11 HashMap (java.util.HashMap)7 KualiDecimal (org.kuali.rice.core.api.util.type.KualiDecimal)7 Date (java.sql.Date)6 ArrayList (java.util.ArrayList)6 CustomerProfile (org.kuali.kfs.pdp.businessobject.CustomerProfile)6 BufferedWriter (java.io.BufferedWriter)5 IOException (java.io.IOException)5 GeneralLedgerPendingEntrySequenceHelper (org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySequenceHelper)5 FileWriter (java.io.FileWriter)4 Date (java.util.Date)4 List (java.util.List)4 PaymentAccountDetail (org.kuali.kfs.pdp.businessobject.PaymentAccountDetail)4 PaymentNoteText (org.kuali.kfs.pdp.businessobject.PaymentNoteText)4 SimpleDateFormat (java.text.SimpleDateFormat)3 Map (java.util.Map)3 AccountingPeriod (org.kuali.kfs.coa.businessobject.AccountingPeriod)3 OffsetDefinition (org.kuali.kfs.coa.businessobject.OffsetDefinition)3