org.kuali.kfs.module.ar.report.service.impl.ContractsGrantsInvoiceReportServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.kuali.kfs.module.ar.report.service.impl.ContractsGrantsInvoiceReportServiceImpl.java

Source

/*
 * The Kuali Financial System, a comprehensive financial management system for higher education.
 *
 * Copyright 2005-2014 The Kuali Foundation
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.kuali.kfs.module.ar.report.service.impl;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.sql.Date;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.kuali.kfs.coa.businessobject.IndirectCostRecoveryRateDetail;
import org.kuali.kfs.coa.businessobject.Organization;
import org.kuali.kfs.integration.cg.ContractsAndGrantsBillingAgency;
import org.kuali.kfs.integration.cg.ContractsAndGrantsBillingAward;
import org.kuali.kfs.integration.cg.ContractsAndGrantsBillingAwardAccount;
import org.kuali.kfs.module.ar.ArConstants;
import org.kuali.kfs.module.ar.ArKeyConstants;
import org.kuali.kfs.module.ar.ArPropertyConstants;
import org.kuali.kfs.module.ar.businessobject.ContractsGrantsInvoiceLookupResult;
import org.kuali.kfs.module.ar.businessobject.ContractsGrantsLetterOfCreditReviewDetail;
import org.kuali.kfs.module.ar.businessobject.CustomerAddress;
import org.kuali.kfs.module.ar.businessobject.InvoiceAddressDetail;
import org.kuali.kfs.module.ar.businessobject.InvoicePaidApplied;
import org.kuali.kfs.module.ar.businessobject.SystemInformation;
import org.kuali.kfs.module.ar.document.ContractsGrantsInvoiceDocument;
import org.kuali.kfs.module.ar.document.ContractsGrantsLetterOfCreditReviewDocument;
import org.kuali.kfs.module.ar.document.service.ContractsGrantsInvoiceDocumentService;
import org.kuali.kfs.module.ar.report.service.ContractsGrantsInvoiceReportService;
import org.kuali.kfs.module.ar.service.ContractsGrantsBillingUtilityService;
import org.kuali.kfs.sys.KFSConstants;
import org.kuali.kfs.sys.KFSPropertyConstants;
import org.kuali.kfs.sys.PdfFormFillerUtil;
import org.kuali.kfs.sys.businessobject.SystemOptions;
import org.kuali.kfs.sys.report.ReportInfo;
import org.kuali.kfs.sys.service.NonTransactional;
import org.kuali.kfs.sys.service.OptionsService;
import org.kuali.kfs.sys.service.ReportGenerationService;
import org.kuali.rice.core.api.config.property.ConfigurationService;
import org.kuali.rice.core.api.datetime.DateTimeService;
import org.kuali.rice.core.api.util.type.KualiDecimal;
import org.kuali.rice.coreservice.framework.parameter.ParameterService;
import org.kuali.rice.kim.api.identity.Person;
import org.kuali.rice.kim.api.identity.PersonService;
import org.kuali.rice.kns.service.DataDictionaryService;
import org.kuali.rice.krad.bo.Note;
import org.kuali.rice.krad.service.BusinessObjectService;
import org.kuali.rice.krad.service.DocumentService;
import org.kuali.rice.krad.service.KualiModuleService;
import org.kuali.rice.krad.service.NoteService;
import org.kuali.rice.krad.util.KRADConstants;
import org.kuali.rice.krad.util.ObjectUtils;

import au.com.bytecode.opencsv.CSVWriter;

import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.Element;
import com.lowagie.text.Font;
import com.lowagie.text.Paragraph;
import com.lowagie.text.Rectangle;
import com.lowagie.text.pdf.AcroFields;
import com.lowagie.text.pdf.PdfCopyFields;
import com.lowagie.text.pdf.PdfPCell;
import com.lowagie.text.pdf.PdfPTable;
import com.lowagie.text.pdf.PdfReader;
import com.lowagie.text.pdf.PdfStamper;
import com.lowagie.text.pdf.PdfWriter;

/**
 * This class implements the methods for report generation services for Contracts & Grants.
 */
public class ContractsGrantsInvoiceReportServiceImpl implements ContractsGrantsInvoiceReportService {
    private final static org.apache.log4j.Logger LOG = org.apache.log4j.Logger
            .getLogger(ContractsGrantsInvoiceReportServiceImpl.class);
    protected DateTimeService dateTimeService;
    protected DataDictionaryService dataDictionaryService;
    protected PersonService personService;
    protected BusinessObjectService businessObjectService;
    protected ParameterService parameterService;
    protected ConfigurationService configService;
    protected KualiModuleService kualiModuleService;
    protected DocumentService documentService;
    protected NoteService noteService;
    protected ReportInfo reportInfo;
    protected ReportGenerationService reportGenerationService;
    protected ContractsGrantsInvoiceDocumentService contractsGrantsInvoiceDocumentService;
    protected ContractsGrantsBillingUtilityService contractsGrantsBillingUtilityService;
    protected OptionsService optionsService;

    /**
     * @see org.kuali.kfs.module.ar.report.service.ContractsGrantsInvoiceReportService#generateInvoice(org.kuali.kfs.module.ar.document.ContractsGrantsLOCReviewDocument)
     */
    @Override
    public byte[] generateLOCReviewAsPdf(ContractsGrantsLetterOfCreditReviewDocument document) {
        Date runDate = new Date(new java.util.Date().getTime());
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        this.generateLOCReviewInPdf(baos, document);
        return baos.toByteArray();
    }

    /**
     * this method generated the actual pdf for the Contracts & Grants LOC Review Document.
     *
     * @param os
     * @param LOCDocument
     */
    protected void generateLOCReviewInPdf(OutputStream os,
            ContractsGrantsLetterOfCreditReviewDocument locDocument) {
        try {
            Document document = new Document(
                    new Rectangle(ArConstants.LOCReviewPdf.LENGTH, ArConstants.LOCReviewPdf.WIDTH));
            PdfWriter.getInstance(document, os);
            document.open();

            Paragraph header = new Paragraph();
            Paragraph text = new Paragraph();
            Paragraph title = new Paragraph();

            // Lets write the header
            header.add(new Paragraph(configService.getPropertyValueAsString(ArKeyConstants.LOC_REVIEW_PDF_TITLE),
                    ArConstants.PdfReportFonts.LOC_REVIEW_TITLE_FONT));
            if (StringUtils.isNotEmpty(locDocument.getLetterOfCreditFundGroupCode())) {
                header.add(new Paragraph(
                        configService.getPropertyValueAsString(ArKeyConstants.LOC_REVIEW_PDF_HEADER_FUND_GROUP_CODE)
                                + locDocument.getLetterOfCreditFundGroupCode(),
                        ArConstants.PdfReportFonts.LOC_REVIEW_TITLE_FONT));
            }
            if (StringUtils.isNotEmpty(locDocument.getLetterOfCreditFundCode())) {
                header.add(new Paragraph(
                        configService.getPropertyValueAsString(ArKeyConstants.LOC_REVIEW_PDF_HEADER_FUND_CODE)
                                + locDocument.getLetterOfCreditFundCode(),
                        ArConstants.PdfReportFonts.LOC_REVIEW_TITLE_FONT));
            }
            header.add(new Paragraph(KFSConstants.BLANK_SPACE));
            header.setAlignment(Element.ALIGN_CENTER);
            title.add(new Paragraph(
                    configService.getPropertyValueAsString(ArKeyConstants.LOC_REVIEW_PDF_HEADER_DOCUMENT_NUMBER)
                            + locDocument.getDocumentNumber(),
                    ArConstants.PdfReportFonts.LOC_REVIEW_HEADER_FONT));
            Person person = getPersonService()
                    .getPerson(locDocument.getFinancialSystemDocumentHeader().getInitiatorPrincipalId());
            // writing the Document details
            title.add(new Paragraph(
                    configService.getPropertyValueAsString(ArKeyConstants.LOC_REVIEW_PDF_HEADER_APP_DOC_STATUS)
                            + locDocument.getFinancialSystemDocumentHeader().getApplicationDocumentStatus(),
                    ArConstants.PdfReportFonts.LOC_REVIEW_HEADER_FONT));
            title.add(
                    new Paragraph(
                            configService.getPropertyValueAsString(
                                    ArKeyConstants.LOC_REVIEW_PDF_HEADER_DOCUMENT_INITIATOR) + person.getName(),
                            ArConstants.PdfReportFonts.LOC_REVIEW_HEADER_FONT));
            title.add(new Paragraph(
                    configService
                            .getPropertyValueAsString(ArKeyConstants.LOC_REVIEW_PDF_HEADER_DOCUMENT_CREATE_DATE)
                            + getDateTimeService().toDateString(
                                    locDocument.getFinancialSystemDocumentHeader().getWorkflowCreateDate()),
                    ArConstants.PdfReportFonts.LOC_REVIEW_HEADER_FONT));

            title.add(new Paragraph(KFSConstants.BLANK_SPACE));
            title.setAlignment(Element.ALIGN_RIGHT);

            text.add(new Paragraph(
                    configService.getPropertyValueAsString(ArKeyConstants.LOC_REVIEW_PDF_SUBHEADER_AWARDS),
                    ArConstants.PdfReportFonts.LOC_REVIEW_SMALL_BOLD));
            text.add(new Paragraph(KFSConstants.BLANK_SPACE));

            document.add(header);
            document.add(title);
            document.add(text);
            PdfPTable table = new PdfPTable(11);
            table.setTotalWidth(ArConstants.LOCReviewPdf.RESULTS_TABLE_WIDTH);
            // fix the absolute width of the table
            table.setLockedWidth(true);

            // relative col widths in proportions - 1/11
            float[] widths = new float[] { 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f };
            table.setWidths(widths);
            table.setHorizontalAlignment(0);
            addAwardHeaders(table);
            if (CollectionUtils.isNotEmpty(locDocument.getHeaderReviewDetails())
                    && CollectionUtils.isNotEmpty(locDocument.getAccountReviewDetails())) {
                for (ContractsGrantsLetterOfCreditReviewDetail item : locDocument.getHeaderReviewDetails()) {
                    table.addCell(Long.toString(item.getProposalNumber()));
                    table.addCell(item.getAwardDocumentNumber());
                    table.addCell(item.getAgencyNumber());
                    table.addCell(item.getCustomerNumber());
                    table.addCell(getDateTimeService().toDateString(item.getAwardBeginningDate()));
                    table.addCell(getDateTimeService().toDateString(item.getAwardEndingDate()));
                    table.addCell(
                            contractsGrantsBillingUtilityService.formatForCurrency(item.getAwardBudgetAmount()));
                    table.addCell(
                            contractsGrantsBillingUtilityService.formatForCurrency(item.getLetterOfCreditAmount()));
                    table.addCell(
                            contractsGrantsBillingUtilityService.formatForCurrency(item.getClaimOnCashBalance()));
                    table.addCell(contractsGrantsBillingUtilityService.formatForCurrency(item.getAmountToDraw()));
                    table.addCell(contractsGrantsBillingUtilityService
                            .formatForCurrency(item.getAmountAvailableToDraw()));

                    PdfPCell cell = new PdfPCell();
                    cell.setPadding(ArConstants.LOCReviewPdf.RESULTS_TABLE_CELL_PADDING);
                    cell.setColspan(ArConstants.LOCReviewPdf.RESULTS_TABLE_COLSPAN);
                    PdfPTable newTable = new PdfPTable(ArConstants.LOCReviewPdf.INNER_TABLE_COLUMNS);
                    newTable.setTotalWidth(ArConstants.LOCReviewPdf.INNER_TABLE_WIDTH);
                    // fix the absolute width of the newTable
                    newTable.setLockedWidth(true);

                    // relative col widths in proportions - 1/8
                    float[] newWidths = new float[] { 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f };
                    newTable.setWidths(newWidths);
                    newTable.setHorizontalAlignment(0);
                    addAccountsHeaders(newTable);
                    for (ContractsGrantsLetterOfCreditReviewDetail newItem : locDocument
                            .getAccountReviewDetails()) {
                        if (item.getProposalNumber().equals(newItem.getProposalNumber())) {
                            newTable.addCell(newItem.getAccountDescription());
                            newTable.addCell(newItem.getChartOfAccountsCode());
                            newTable.addCell(newItem.getAccountNumber());
                            String accountExpirationDate = KFSConstants.EMPTY_STRING;
                            if (!ObjectUtils.isNull(newItem.getAccountExpirationDate())) {
                                accountExpirationDate = getDateTimeService()
                                        .toDateString(newItem.getAccountExpirationDate());
                            }
                            newTable.addCell(accountExpirationDate);
                            newTable.addCell(contractsGrantsBillingUtilityService
                                    .formatForCurrency(newItem.getAwardBudgetAmount()));
                            newTable.addCell(contractsGrantsBillingUtilityService
                                    .formatForCurrency(newItem.getClaimOnCashBalance()));
                            newTable.addCell(contractsGrantsBillingUtilityService
                                    .formatForCurrency(newItem.getAmountToDraw()));
                            newTable.addCell(contractsGrantsBillingUtilityService
                                    .formatForCurrency(newItem.getFundsNotDrawn()));
                        }
                    }
                    cell.addElement(newTable);
                    table.addCell(cell);

                }
                document.add(table);
            }
            document.close();
        } catch (DocumentException e) {
            LOG.error("problem during ContractsGrantsInvoiceReportServiceImpl.generateInvoiceInPdf()", e);
        }
    }

    /**
     * This method is used to set the headers for the CG LOC review Document
     *
     * @param table
     */
    protected void addAccountsHeaders(PdfPTable table) {
        table.addCell(getDataDictionaryService().getAttributeLabel(ContractsGrantsLetterOfCreditReviewDetail.class,
                KFSPropertyConstants.ACCOUNT_DESCRIPTION));
        table.addCell(getDataDictionaryService().getAttributeLabel(ContractsGrantsLetterOfCreditReviewDetail.class,
                KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE));
        table.addCell(getDataDictionaryService().getAttributeLabel(ContractsGrantsLetterOfCreditReviewDetail.class,
                KFSPropertyConstants.ACCOUNT_NUMBER));
        table.addCell(getDataDictionaryService().getAttributeLabel(ContractsGrantsLetterOfCreditReviewDetail.class,
                KFSPropertyConstants.ACCOUNT_EXPIRATION_DATE));
        table.addCell(getDataDictionaryService().getAttributeLabel(ContractsGrantsLetterOfCreditReviewDetail.class,
                ArPropertyConstants.AWARD_BUDGET_AMOUNT));
        table.addCell(getDataDictionaryService().getAttributeLabel(ContractsGrantsLetterOfCreditReviewDetail.class,
                ArPropertyConstants.CLAIM_ON_CASH_BALANCE));
        table.addCell(getDataDictionaryService().getAttributeLabel(ContractsGrantsLetterOfCreditReviewDetail.class,
                ArPropertyConstants.AMOUNT_TO_DRAW));
        table.addCell(getDataDictionaryService().getAttributeLabel(ContractsGrantsLetterOfCreditReviewDetail.class,
                ArPropertyConstants.FUNDS_NOT_DRAWN));
    }

    /**
     * This method is used to set the headers for the CG LOC review Document
     *
     * @param table
     */
    protected void addAwardHeaders(PdfPTable table) {
        table.addCell(getDataDictionaryService().getAttributeLabel(ContractsGrantsLetterOfCreditReviewDetail.class,
                KFSPropertyConstants.PROPOSAL_NUMBER));
        table.addCell(getDataDictionaryService().getAttributeLabel(ContractsGrantsLetterOfCreditReviewDetail.class,
                KFSPropertyConstants.AWARD_DOCUMENT_NUMBER));
        table.addCell(getDataDictionaryService().getAttributeLabel(ContractsGrantsLetterOfCreditReviewDetail.class,
                KFSPropertyConstants.AGENCY_NUMBER));
        table.addCell(getDataDictionaryService().getAttributeLabel(ContractsGrantsLetterOfCreditReviewDetail.class,
                KFSPropertyConstants.CUSTOMER_NUMBER));
        table.addCell(getDataDictionaryService().getAttributeLabel(ContractsGrantsLetterOfCreditReviewDetail.class,
                KFSPropertyConstants.AWARD_BEGINNING_DATE));
        table.addCell(getDataDictionaryService().getAttributeLabel(ContractsGrantsLetterOfCreditReviewDetail.class,
                KFSPropertyConstants.AWARD_ENDING_DATE));
        table.addCell(getDataDictionaryService().getAttributeLabel(ContractsGrantsLetterOfCreditReviewDetail.class,
                ArPropertyConstants.AWARD_BUDGET_AMOUNT));
        table.addCell(getDataDictionaryService().getAttributeLabel(ContractsGrantsLetterOfCreditReviewDetail.class,
                ArPropertyConstants.LETTER_OF_CREDIT_AMOUNT));
        table.addCell(getDataDictionaryService().getAttributeLabel(ContractsGrantsLetterOfCreditReviewDetail.class,
                ArPropertyConstants.CLAIM_ON_CASH_BALANCE));
        table.addCell(getDataDictionaryService().getAttributeLabel(ContractsGrantsLetterOfCreditReviewDetail.class,
                ArPropertyConstants.AMOUNT_TO_DRAW));
        table.addCell(getDataDictionaryService().getAttributeLabel(ContractsGrantsLetterOfCreditReviewDetail.class,
                ArPropertyConstants.FUNDS_NOT_DRAWN));
    }

    /**
     * @see org.kuali.kfs.module.ar.report.service.ContractsGrantsInvoiceReportService#generateFederalFinancialForm(org.kuali.kfs.integration.cg.ContractsAndGrantsBillingAward,
     *      java.lang.String, java.lang.String, java.lang.String, org.kuali.kfs.integration.cg.ContractsAndGrantsBillingAgency)
     */
    @Override
    public File generateFederalFinancialForm(ContractsAndGrantsBillingAward award, String period, String year,
            String formType, ContractsAndGrantsBillingAgency agency) {
        Map<String, String> replacementList = new HashMap<String, String>();
        Date runDate = new Date(new java.util.Date().getTime());
        String reportFileName = getReportInfo().getReportFileName();
        String reportDirectory = getReportInfo().getReportsDirectory();
        try {
            if (formType.equals(ArConstants.FEDERAL_FORM_425) && ObjectUtils.isNotNull(award)) {
                String fullReportFileName = reportGenerationService.buildFullFileName(runDate, reportDirectory,
                        reportFileName, ArConstants.FEDERAL_FUND_425_REPORT_ABBREVIATION)
                        + KFSConstants.ReportGeneration.PDF_FILE_EXTENSION;
                File file = new File(fullReportFileName);
                FileOutputStream fos = new FileOutputStream(file);
                stampPdfFormValues425(award, period, year, fos, replacementList);
                return file;
            } else if (formType.equals(ArConstants.FEDERAL_FORM_425A) && ObjectUtils.isNotNull(agency)) {
                String fullReportFileName = reportGenerationService.buildFullFileName(runDate, reportDirectory,
                        reportFileName, ArConstants.FEDERAL_FUND_425A_REPORT_ABBREVIATION)
                        + KFSConstants.ReportGeneration.PDF_FILE_EXTENSION;
                File file = new File(fullReportFileName);
                FileOutputStream fos = new FileOutputStream(file);
                stampPdfFormValues425A(agency, period, year, fos, replacementList);
                return file;
            }
        } catch (FileNotFoundException ex) {
            throw new RuntimeException("Cannot find pdf to stamp for federal financial form", ex);
        }
        return null;
    }

    /**
     * @param award
     * @return
     */
    protected KualiDecimal getCashReceipts(ContractsAndGrantsBillingAward award) {
        KualiDecimal cashReceipt = KualiDecimal.ZERO;
        Map<String, String> fieldValues = new HashMap<String, String>();
        if (ObjectUtils.isNotNull(award) && ObjectUtils.isNotNull(award.getProposalNumber())) {
            fieldValues.put(KFSPropertyConstants.PROPOSAL_NUMBER, award.getProposalNumber().toString());
        }
        List<ContractsGrantsInvoiceDocument> list = (List<ContractsGrantsInvoiceDocument>) contractsGrantsInvoiceDocumentService
                .retrieveAllCGInvoicesByCriteria(fieldValues);
        if (!CollectionUtils.isEmpty(list)) {
            for (ContractsGrantsInvoiceDocument invoice : list) {
                Map primaryKeys = new HashMap<String, Object>();
                primaryKeys.put(
                        ArPropertyConstants.CustomerInvoiceDocumentFields.FINANCIAL_DOCUMENT_REF_INVOICE_NUMBER,
                        invoice.getDocumentNumber());
                List<InvoicePaidApplied> ipas = (List<InvoicePaidApplied>) businessObjectService
                        .findMatching(InvoicePaidApplied.class, primaryKeys);
                if (ObjectUtils.isNotNull(ipas)) {
                    for (InvoicePaidApplied ipa : ipas) {
                        cashReceipt = cashReceipt.add(ipa.getInvoiceItemAppliedAmount());
                    }
                }
            }
        }
        return cashReceipt;
    }

    /**
     * This method is used to populate the replacement list to replace values from pdf template to actual values for Federal Form
     * 425
     *
     * @param award
     * @param reportingPeriod
     * @param year
     */
    protected void populateListByAward(ContractsAndGrantsBillingAward award, String reportingPeriod, String year,
            Map<String, String> replacementList) {
        KualiDecimal cashDisbursement = KualiDecimal.ZERO;
        final SystemOptions systemOption = optionsService.getCurrentYearOptions();

        for (ContractsAndGrantsBillingAwardAccount awardAccount : award.getActiveAwardAccounts()) {
            int index = 0;
            KualiDecimal baseSum = KualiDecimal.ZERO;
            KualiDecimal amountSum = KualiDecimal.ZERO;
            cashDisbursement = cashDisbursement
                    .add(contractsGrantsInvoiceDocumentService.getBudgetAndActualsForAwardAccount(awardAccount,
                            systemOption.getActualFinancialBalanceTypeCd(), award.getAwardBeginningDate()));
            if (ObjectUtils.isNotNull(awardAccount.getAccount().getFinancialIcrSeriesIdentifier())
                    && ObjectUtils.isNotNull(awardAccount.getAccount().getAcctIndirectCostRcvyTypeCd())) {
                index++;
                contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                        ArPropertyConstants.FederalFormReportFields.INDIRECT_EXPENSE_TYPE + " " + index,
                        awardAccount.getAccount().getAcctIndirectCostRcvyTypeCd());
                contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                        ArPropertyConstants.FederalFormReportFields.INDIRECT_EXPENSE_RATE + " " + index,
                        awardAccount.getAccount().getFinancialIcrSeriesIdentifier());
                if (ObjectUtils.isNotNull(awardAccount.getAccount().getAccountEffectiveDate())) {
                    contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                            ArPropertyConstants.FederalFormReportFields.INDIRECT_EXPENSE_PERIOD_FROM + " " + index,
                            getDateTimeService().toDateString(awardAccount.getAccount().getAccountEffectiveDate()));
                }
                if (ObjectUtils.isNotNull(awardAccount.getAccount().getAccountExpirationDate())) {
                    contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                            ArPropertyConstants.FederalFormReportFields.INDIRECT_EXPENSE_PERIOD_TO + " " + index,
                            getDateTimeService()
                                    .toDateString(awardAccount.getAccount().getAccountExpirationDate()));
                }
                contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                        ArPropertyConstants.FederalFormReportFields.INDIRECT_EXPENSE_BASE + " " + index,
                        contractsGrantsBillingUtilityService.formatForCurrency(award.getAwardTotalAmount()));
                Map<String, Object> key = new HashMap<String, Object>();
                key.put(KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR, year);
                key.put(KFSPropertyConstants.FINANCIAL_ICR_SERIES_IDENTIFIER,
                        awardAccount.getAccount().getFinancialIcrSeriesIdentifier());
                key.put(KFSPropertyConstants.ACTIVE, true);
                key.put(KFSPropertyConstants.TRANSACTION_DEBIT_INDICATOR, KFSConstants.GL_DEBIT_CODE);
                List<IndirectCostRecoveryRateDetail> icrDetail = (List<IndirectCostRecoveryRateDetail>) businessObjectService
                        .findMatchingOrderBy(IndirectCostRecoveryRateDetail.class, key,
                                KFSPropertyConstants.AWARD_INDR_COST_RCVY_ENTRY_NBR, false);
                if (CollectionUtils.isNotEmpty(icrDetail)) {
                    KualiDecimal rate = new KualiDecimal(icrDetail.get(0).getAwardIndrCostRcvyRatePct());
                    if (ObjectUtils.isNotNull(rate)) {
                        contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                                ArPropertyConstants.FederalFormReportFields.INDIRECT_EXPENSE_AMOUNT + " " + index,
                                contractsGrantsBillingUtilityService
                                        .formatForCurrency(award.getAwardTotalAmount().multiply(rate)));
                        contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                                ArPropertyConstants.FederalFormReportFields.INDIRECT_EXPENSE_FEDERAL + " " + index,
                                contractsGrantsBillingUtilityService
                                        .formatForCurrency(award.getAwardTotalAmount().multiply(rate)));
                        amountSum = amountSum.add(award.getAwardTotalAmount().multiply(rate));
                    }
                }
                baseSum = baseSum.add(award.getAwardTotalAmount());
            }
            contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                    ArPropertyConstants.FederalFormReportFields.INDIRECT_EXPENSE_BASE_SUM,
                    contractsGrantsBillingUtilityService.formatForCurrency(baseSum));
            contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                    ArPropertyConstants.FederalFormReportFields.INDIRECT_EXPENSE_AMOUNT_SUM,
                    contractsGrantsBillingUtilityService.formatForCurrency(amountSum));
            contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                    ArPropertyConstants.FederalFormReportFields.INDIRECT_EXPENSE_FEDERAL_SUM,
                    contractsGrantsBillingUtilityService.formatForCurrency(amountSum));
        }

        final SystemInformation sysInfo = retrieveSystemInformationForAward(award, year);
        if (ObjectUtils.isNotNull(sysInfo)) {
            final String address = concatenateAddressFromSystemInformation(sysInfo);
            contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                    ArPropertyConstants.FederalFormReportFields.RECIPIENT_ORGANIZATION, address);
            contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                    ArPropertyConstants.FederalFormReportFields.ZWEI,
                    sysInfo.getUniversityFederalEmployerIdentificationNumber());
        }

        contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                ArPropertyConstants.FederalFormReportFields.FEDERAL_AGENCY, award.getAgency().getFullName());
        contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                ArPropertyConstants.FederalFormReportFields.FEDERAL_GRANT_NUMBER, award.getAwardDocumentNumber());
        if (CollectionUtils.isNotEmpty(award.getActiveAwardAccounts())) {
            contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                    ArPropertyConstants.FederalFormReportFields.RECIPIENT_ACCOUNT_NUMBER,
                    award.getActiveAwardAccounts().get(0).getAccountNumber());
        }
        if (ObjectUtils.isNotNull(award.getAwardBeginningDate())) {
            contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                    ArPropertyConstants.FederalFormReportFields.GRANT_PERIOD_FROM,
                    getDateTimeService().toDateString(award.getAwardBeginningDate()));
        }
        if (ObjectUtils.isNotNull(award.getAwardClosingDate())) {
            contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                    ArPropertyConstants.FederalFormReportFields.GRANT_PERIOD_TO,
                    getDateTimeService().toDateString(award.getAwardClosingDate()));
        }
        contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                ArPropertyConstants.FederalFormReportFields.CASH_RECEIPTS,
                contractsGrantsBillingUtilityService.formatForCurrency(this.getCashReceipts(award)));
        contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                ArPropertyConstants.FederalFormReportFields.TOTAL_FEDERAL_FUNDS_AUTHORIZED,
                contractsGrantsBillingUtilityService.formatForCurrency(award.getAwardTotalAmount()));

        contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                ArPropertyConstants.FederalFormReportFields.REPORTING_PERIOD_END_DATE,
                getReportingPeriodEndDate(reportingPeriod, year));
        if (ObjectUtils.isNotNull(cashDisbursement)) {
            contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                    ArPropertyConstants.FederalFormReportFields.CASH_DISBURSEMENTS,
                    contractsGrantsBillingUtilityService.formatForCurrency(cashDisbursement));
            contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                    ArPropertyConstants.FederalFormReportFields.CASH_ON_HAND, contractsGrantsBillingUtilityService
                            .formatForCurrency(getCashReceipts(award).subtract(cashDisbursement)));
            contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                    ArPropertyConstants.FederalFormReportFields.FEDERAL_SHARE_OF_EXPENDITURES,
                    contractsGrantsBillingUtilityService.formatForCurrency(cashDisbursement));
            contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                    ArPropertyConstants.FederalFormReportFields.TOTAL_FEDERAL_SHARE,
                    contractsGrantsBillingUtilityService.formatForCurrency(cashDisbursement));
            contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                    ArPropertyConstants.FederalFormReportFields.UNOBLIGATED_BALANCE_OF_FEDERAL_FUNDS,
                    contractsGrantsBillingUtilityService
                            .formatForCurrency(award.getAwardTotalAmount().subtract(cashDisbursement)));
        }
        contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                ArPropertyConstants.FederalFormReportFields.FEDERAL_SHARE_OF_UNLIQUIDATED_OBLIGATION,
                contractsGrantsBillingUtilityService.formatForCurrency(KualiDecimal.ZERO));

        contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                ArPropertyConstants.FederalFormReportFields.TOTAL_FEDERAL_INCOME_EARNED, KFSConstants.EMPTY_STRING);
        contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                ArPropertyConstants.FederalFormReportFields.INCOME_EXPENDED_DEDUCATION_ALTERNATIVE,
                KFSConstants.EMPTY_STRING);
        contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                ArPropertyConstants.FederalFormReportFields.INCOME_EXPENDED_ADDITION_ALTERNATIVE,
                KFSConstants.EMPTY_STRING);
        contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                ArPropertyConstants.FederalFormReportFields.UNEXPECTED_PROGRAM_INCOME, KFSConstants.EMPTY_STRING);
        contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                ArPropertyConstants.FederalFormReportFields.NAME, KFSConstants.EMPTY_STRING);
        contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                ArPropertyConstants.FederalFormReportFields.TELEPHONE, KFSConstants.EMPTY_STRING);
        contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                ArPropertyConstants.FederalFormReportFields.EMAIL_ADDRESS, KFSConstants.EMPTY_STRING);
        contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                ArPropertyConstants.FederalFormReportFields.DATE_REPORT_SUBMITTED,
                getDateTimeService().toDateString(getDateTimeService().getCurrentDate()));
        if (ArConstants.QUARTER1.equals(reportingPeriod) || ArConstants.QUARTER2.equals(reportingPeriod)
                || ArConstants.QUARTER3.equals(reportingPeriod) || ArConstants.QUARTER4.equals(reportingPeriod)) {
            contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                    ArPropertyConstants.FederalFormReportFields.QUARTERLY, KFSConstants.OptionLabels.YES);
        }
        if (ArConstants.SEMI_ANNUAL.equals(reportingPeriod)) {
            contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                    ArPropertyConstants.FederalFormReportFields.SEMI_ANNUAL, KFSConstants.OptionLabels.YES);
        }
        if (ArConstants.ANNUAL.equals(reportingPeriod)) {
            contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                    ArPropertyConstants.FederalFormReportFields.ANNUAL, KFSConstants.OptionLabels.YES);
        }
        if (ArConstants.FINAL.equals(reportingPeriod)) {
            contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                    ArPropertyConstants.FederalFormReportFields.FINAL, KFSConstants.OptionLabels.YES);
        }
        String accountingBasis = parameterService.getParameterValueAsString(ArConstants.AR_NAMESPACE_CODE,
                KRADConstants.DetailTypes.ALL_DETAIL_TYPE, ArConstants.BASIS_OF_ACCOUNTING);
        if (ArConstants.BASIS_OF_ACCOUNTING_CASH.equals(accountingBasis)) {
            contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                    ArPropertyConstants.FederalFormReportFields.CASH, KFSConstants.OptionLabels.YES);
        }
        if (ArConstants.BASIS_OF_ACCOUNTING_ACCRUAL.equals(accountingBasis)) {
            contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                    ArPropertyConstants.FederalFormReportFields.ACCRUAL, KFSConstants.OptionLabels.YES);
        }
    }

    /**
     * Concatenates the address from an AR System Information object into a single String
     * @param sysInfo the System Information business object to concatenate the address of
     * @return the concatenated address
     */
    protected String concatenateAddressFromSystemInformation(final SystemInformation sysInfo) {
        String address = sysInfo.getOrganizationRemitToAddressName();
        if (!StringUtils.isBlank(sysInfo.getOrganizationRemitToLine1StreetAddress())) {
            address += ", " + sysInfo.getOrganizationRemitToLine1StreetAddress();
        }
        if (!StringUtils.isBlank(sysInfo.getOrganizationRemitToLine2StreetAddress())) {
            address += ", " + sysInfo.getOrganizationRemitToLine2StreetAddress();
        }
        if (!StringUtils.isBlank(sysInfo.getOrganizationRemitToCityName())) {
            address += ", " + sysInfo.getOrganizationRemitToCityName();
        }
        if (!StringUtils.isBlank(sysInfo.getOrganizationRemitToStateCode())) {
            address += " " + sysInfo.getOrganizationRemitToStateCode();
        }
        if (!StringUtils.isBlank(sysInfo.getOrganizationRemitToZipCode())) {
            address += "-" + sysInfo.getOrganizationRemitToZipCode();
        }
        return address;
    }

    /**
     * Retrieves an AR System Information object for an award
     * @param award the award to retrieve an associated System Information for
     * @param year the year of the System Information object to retrieve
     * @return the System Information object, or null if nothing is found
     */
    protected SystemInformation retrieveSystemInformationForAward(ContractsAndGrantsBillingAward award,
            String year) {
        Map primaryKeys = new HashMap<String, Object>();
        primaryKeys.put(KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR, year);
        primaryKeys.put(KFSPropertyConstants.PROCESSING_CHART_OF_ACCT_CD,
                award.getPrimaryAwardOrganization().getChartOfAccountsCode());
        primaryKeys.put(KFSPropertyConstants.PROCESSING_ORGANIZATION_CODE,
                award.getPrimaryAwardOrganization().getOrganizationCode());
        SystemInformation sysInfo = businessObjectService.findByPrimaryKey(SystemInformation.class, primaryKeys);
        return sysInfo;
    }

    /**
     * This method is used to populate the replacement list to replace values from pdf template to actual values for Federal Form
     * 425A
     *
     * @param awards
     * @param reportingPeriod
     * @param year
     * @param agency
     * @return total amount
     */
    protected List<KualiDecimal> populateListByAgency(List<ContractsAndGrantsBillingAward> awards,
            String reportingPeriod, String year, ContractsAndGrantsBillingAgency agency) {
        Map<String, String> replacementList = new HashMap<String, String>();
        contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                ArPropertyConstants.FederalFormReportFields.REPORTING_PERIOD_END_DATE,
                getReportingPeriodEndDate(reportingPeriod, year));
        contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                ArPropertyConstants.FederalFormReportFields.FEDERAL_AGENCY, agency.getFullName());
        final SystemOptions systemOption = optionsService.getCurrentYearOptions();

        Map primaryKeys = new HashMap<String, Object>();
        primaryKeys.put(KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR, year);
        if (CollectionUtils.isNotEmpty(awards)) {
            primaryKeys.put(KFSPropertyConstants.PROCESSING_CHART_OF_ACCT_CD,
                    awards.get(0).getPrimaryAwardOrganization().getChartOfAccountsCode());
            primaryKeys.put(KFSPropertyConstants.PROCESSING_ORGANIZATION_CODE,
                    awards.get(0).getPrimaryAwardOrganization().getOrganizationCode());
        }

        SystemInformation sysInfo = businessObjectService.findByPrimaryKey(SystemInformation.class, primaryKeys);

        if (ObjectUtils.isNotNull(sysInfo)) {
            String address = concatenateAddressFromSystemInformation(sysInfo);

            contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                    ArPropertyConstants.FederalFormReportFields.RECIPIENT_ORGANIZATION, address);
            contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                    ArPropertyConstants.FederalFormReportFields.ZWEI,
                    sysInfo.getUniversityFederalEmployerIdentificationNumber());
        }

        if (ArConstants.QUARTER1.equals(reportingPeriod) || ArConstants.QUARTER2.equals(reportingPeriod)
                || ArConstants.QUARTER3.equals(reportingPeriod) || ArConstants.QUARTER4.equals(reportingPeriod)) {
            contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                    ArPropertyConstants.FederalFormReportFields.QUARTERLY, KFSConstants.OptionLabels.YES);
        }
        if (ArConstants.SEMI_ANNUAL.equals(reportingPeriod)) {
            contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                    ArPropertyConstants.FederalFormReportFields.SEMI_ANNUAL, KFSConstants.OptionLabels.YES);
        }
        if (ArConstants.ANNUAL.equals(reportingPeriod)) {
            contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                    ArPropertyConstants.FederalFormReportFields.ANNUAL, KFSConstants.OptionLabels.YES);
        }
        if (ArConstants.FINAL.equals(reportingPeriod)) {
            contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                    ArPropertyConstants.FederalFormReportFields.FINAL, KFSConstants.OptionLabels.YES);
        }
        String accountingBasis = parameterService.getParameterValueAsString(ArConstants.AR_NAMESPACE_CODE,
                KRADConstants.DetailTypes.ALL_DETAIL_TYPE, ArConstants.BASIS_OF_ACCOUNTING);
        if (ArConstants.BASIS_OF_ACCOUNTING_CASH.equals(accountingBasis)) {
            contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                    ArPropertyConstants.FederalFormReportFields.CASH, KFSConstants.OptionLabels.YES);
        }
        if (ArConstants.BASIS_OF_ACCOUNTING_ACCRUAL.equals(accountingBasis)) {
            contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                    ArPropertyConstants.FederalFormReportFields.ACCRUAL, KFSConstants.OptionLabels.YES);
        }
        contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                ArPropertyConstants.FederalFormReportFields.DATE_REPORT_SUBMITTED,
                getDateTimeService().toDateString(new Date(new java.util.Date().getTime())));
        KualiDecimal totalCashControl = KualiDecimal.ZERO;
        KualiDecimal totalCashDisbursement = KualiDecimal.ZERO;
        for (int i = 0; i < 30; i++) {
            if (i < awards.size()) {
                contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                        ArPropertyConstants.FederalFormReportFields.FEDERAL_GRANT_NUMBER + " " + (i + 1),
                        awards.get(i).getAwardDocumentNumber());
                contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                        ArPropertyConstants.FederalFormReportFields.RECIPIENT_ACCOUNT_NUMBER + " " + (i + 1),
                        awards.get(i).getActiveAwardAccounts().get(0).getAccountNumber());
                contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                        ArPropertyConstants.FederalFormReportFields.FEDERAL_CASH_DISBURSEMENT + " " + (i + 1),
                        contractsGrantsBillingUtilityService.formatForCurrency(getCashReceipts(awards.get(i))));
                totalCashControl = totalCashControl.add(this.getCashReceipts(awards.get(i)));

                for (ContractsAndGrantsBillingAwardAccount awardAccount : awards.get(i).getActiveAwardAccounts()) {
                    totalCashDisbursement = totalCashDisbursement.add(
                            contractsGrantsInvoiceDocumentService.getBudgetAndActualsForAwardAccount(awardAccount,
                                    systemOption.getActualFinancialBalanceTypeCd(),
                                    awards.get(i).getAwardBeginningDate()));
                }
            }
        }
        ArrayList<KualiDecimal> list = new ArrayList<KualiDecimal>();
        list.add(totalCashControl);
        list.add(totalCashDisbursement);
        return list;
    }

    /**
     * This method returns the last day of the given reporting period.
     *
     * @param reportingPeriod
     * @param year
     * @return
     */
    protected String getReportingPeriodEndDate(String reportingPeriod, String year) {
        Integer yearAsInt = Integer.parseInt(year);
        java.util.Date endDate = null;
        if (ArConstants.QUARTER1.equals(reportingPeriod)) {
            endDate = ArConstants.BillingQuarterLastDays.FIRST_QUARTER.getDateForYear(yearAsInt);
        } else if (ArConstants.QUARTER2.equals(reportingPeriod)
                || ArConstants.SEMI_ANNUAL.equals(reportingPeriod)) {
            endDate = ArConstants.BillingQuarterLastDays.SECOND_QUARTER.getDateForYear(yearAsInt);
        } else if (ArConstants.QUARTER3.equals(reportingPeriod)) {
            endDate = ArConstants.BillingQuarterLastDays.THIRD_QUARTER.getDateForYear(yearAsInt);
        } else {
            endDate = ArConstants.BillingQuarterLastDays.FOURTH_QUARTER.getDateForYear(yearAsInt);
        }
        return getDateTimeService().toDateString(endDate);
    }

    /**
     * Use iText <code>{@link PdfStamper}</code> to stamp information into field values on a PDF Form Template.
     *
     * @param award The award the values will be pulled from.
     * @param reportingPeriod
     * @param year
     * @param returnStream The output stream the federal form will be written to.
     */
    protected void stampPdfFormValues425(ContractsAndGrantsBillingAward award, String reportingPeriod, String year,
            OutputStream returnStream, Map<String, String> replacementList) {
        String reportTemplateName = ArConstants.FF_425_TEMPLATE_NM
                + KFSConstants.ReportGeneration.PDF_FILE_EXTENSION;
        try {
            String federalReportTemplatePath = configService
                    .getPropertyValueAsString(KFSConstants.EXTERNALIZABLE_HELP_URL_KEY);
            populateListByAward(award, reportingPeriod, year, replacementList);
            //final byte[] pdfBytes = renameFieldsIn(federalReportTemplatePath + reportTemplateName, replacementList);
            URL template = new URL(federalReportTemplatePath + reportTemplateName);
            final byte[] pdfBytes = PdfFormFillerUtil.populateTemplate(template.openStream(), replacementList);
            returnStream.write(pdfBytes);
        } catch (IOException | DocumentException ex) {
            throw new RuntimeException("Troubles stamping the old 425!", ex);
        }
    }

    /**
     * Use iText <code>{@link PdfStamper}</code> to stamp information into field values on a PDF Form Template.
     *
     * @param agency The award the values will be pulled from.
     * @param reportingPeriod
     * @param year
     * @param returnStream The output stream the federal form will be written to.
     */
    protected void stampPdfFormValues425A(ContractsAndGrantsBillingAgency agency, String reportingPeriod,
            String year, OutputStream returnStream, Map<String, String> replacementList) {
        String federalReportTemplatePath = configService
                .getPropertyValueAsString(KFSConstants.EXTERNALIZABLE_HELP_URL_KEY);
        try {
            final String federal425ATemplateUrl = federalReportTemplatePath + ArConstants.FF_425A_TEMPLATE_NM
                    + KFSConstants.ReportGeneration.PDF_FILE_EXTENSION;
            final String federal425TemplateUrl = federalReportTemplatePath + ArConstants.FF_425_TEMPLATE_NM
                    + KFSConstants.ReportGeneration.PDF_FILE_EXTENSION;

            Map<String, Object> fieldValues = new HashMap<>();
            fieldValues.put(KFSPropertyConstants.AGENCY_NUMBER, agency.getAgencyNumber());
            fieldValues.put(KFSPropertyConstants.ACTIVE, Boolean.TRUE);
            List<ContractsAndGrantsBillingAward> awards = kualiModuleService
                    .getResponsibleModuleService(ContractsAndGrantsBillingAward.class)
                    .getExternalizableBusinessObjectsList(ContractsAndGrantsBillingAward.class, fieldValues);
            Integer pageNumber = 1, totalPages;
            totalPages = (awards.size() / ArConstants.Federal425APdf.NUMBER_OF_SUMMARIES_PER_PAGE) + 1;
            PdfCopyFields copy = new PdfCopyFields(returnStream);

            // generate replacement list for FF425
            populateListByAgency(awards, reportingPeriod, year, agency);
            contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                    ArPropertyConstants.FederalFormReportFields.TOTAL_PAGES,
                    org.apache.commons.lang.ObjectUtils.toString(totalPages + 1));
            KualiDecimal sumCashControl = KualiDecimal.ZERO;
            KualiDecimal sumCumExp = KualiDecimal.ZERO;
            while (pageNumber <= totalPages) {
                List<ContractsAndGrantsBillingAward> awardsList = new ArrayList<ContractsAndGrantsBillingAward>();
                for (int i = ((pageNumber - 1)
                        * ArConstants.Federal425APdf.NUMBER_OF_SUMMARIES_PER_PAGE); i < (pageNumber
                                * ArConstants.Federal425APdf.NUMBER_OF_SUMMARIES_PER_PAGE); i++) {
                    if (i < awards.size()) {
                        awardsList.add(awards.get(i));
                    }
                }
                // generate replacement list for FF425
                List<KualiDecimal> list = populateListByAgency(awardsList, reportingPeriod, year, agency);
                if (CollectionUtils.isNotEmpty(list)) {
                    sumCashControl = sumCashControl.add(list.get(0));
                    if (list.size() > 1) {
                        sumCumExp = sumCumExp.add(list.get(1));
                    }
                }

                // populate form with document values
                contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                        ArPropertyConstants.FederalFormReportFields.PAGE_NUMBER,
                        org.apache.commons.lang.ObjectUtils.toString(pageNumber + 1));
                if (pageNumber == totalPages) {
                    contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                            ArPropertyConstants.FederalFormReportFields.TOTAL,
                            contractsGrantsBillingUtilityService.formatForCurrency(sumCashControl));
                    contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                            ArPropertyConstants.FederalFormReportFields.CASH_RECEIPTS,
                            contractsGrantsBillingUtilityService.formatForCurrency(sumCashControl));
                    contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                            ArPropertyConstants.FederalFormReportFields.CASH_DISBURSEMENTS,
                            contractsGrantsBillingUtilityService.formatForCurrency(sumCumExp));
                    contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                            ArPropertyConstants.FederalFormReportFields.CASH_ON_HAND,
                            contractsGrantsBillingUtilityService
                                    .formatForCurrency(sumCashControl.subtract(sumCumExp)));
                }
                // add a document
                copy.addDocument(new PdfReader(renameFieldsIn(federal425ATemplateUrl, replacementList)));
                pageNumber++;
            }
            contractsGrantsBillingUtilityService.putValueOrEmptyString(replacementList,
                    ArPropertyConstants.FederalFormReportFields.PAGE_NUMBER, "1");

            // add the FF425 form.
            copy.addDocument(new PdfReader(renameFieldsIn(federal425TemplateUrl, replacementList)));
            // Close the PdfCopyFields object
            copy.close();
        } catch (DocumentException | IOException ex) {
            throw new RuntimeException("Tried to stamp the 425A, but couldn't do it.  Just...just couldn't do it.",
                    ex);
        }
    }

    /**
    *
    * @param template the path to the original form
    * @param list the replacement list
    * @return
    * @throws IOException
    * @throws DocumentException
    */
    protected byte[] renameFieldsIn(String template, Map<String, String> list)
            throws IOException, DocumentException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        // Create the stamper
        PdfStamper stamper = new PdfStamper(new PdfReader(template), baos);
        // Get the fields
        AcroFields fields = stamper.getAcroFields();
        // Loop over the fields
        for (String field : list.keySet()) {
            fields.setField(field, list.get(field));
        }
        // close the stamper
        stamper.close();
        return baos.toByteArray();
    }

    /**
     * @see org.kuali.kfs.module.ar.report.service.ContractsGrantsInvoiceReportService#generateListOfInvoicesPdfToPrint(java.util.Collection)
     */
    @Override
    public byte[] combineInvoicePdfs(Collection<ContractsGrantsInvoiceDocument> list)
            throws DocumentException, IOException {
        Date runDate = new Date(new java.util.Date().getTime());
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        generateCombinedPdfForInvoices(list, baos);
        return baos.toByteArray();
    }

    /**
     * @see org.kuali.kfs.module.ar.report.service.ContractsGrantsInvoiceReportService#generateListOfInvoicesEnvelopesPdfToPrint(java.util.Collection)
     */
    @Override
    public byte[] combineInvoicePdfEnvelopes(Collection<ContractsGrantsInvoiceDocument> list)
            throws DocumentException, IOException {
        Date runDate = new Date(new java.util.Date().getTime());
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        generateCombinedPdfForEnvelopes(list, baos);
        return baos.toByteArray();
    }

    /**
     * Generates the pdf file for printing the invoices.
     *
     * @param list
     * @param outputStream
     * @throws DocumentException
     * @throws IOException
     */
    protected void generateCombinedPdfForInvoices(Collection<ContractsGrantsInvoiceDocument> list,
            OutputStream outputStream) throws DocumentException, IOException {
        PdfCopyFields copy = new PdfCopyFields(outputStream);
        boolean pageAdded = false;
        for (ContractsGrantsInvoiceDocument invoice : list) {
            // add a document
            List<InvoiceAddressDetail> invoiceAddressDetails = invoice.getInvoiceAddressDetails();

            for (InvoiceAddressDetail invoiceAddressDetail : invoiceAddressDetails) {
                if (ArConstants.InvoiceTransmissionMethod.MAIL
                        .equals(invoiceAddressDetail.getInvoiceTransmissionMethodCode())) {
                    Note note = noteService.getNoteByNoteId(invoiceAddressDetail.getNoteId());
                    Integer numberOfCopiesToPrint = invoiceAddressDetail.getCustomerAddress()
                            .getCustomerCopiesToPrint();
                    if (ObjectUtils.isNull(numberOfCopiesToPrint)) {
                        numberOfCopiesToPrint = 1;
                    }

                    if (!ObjectUtils.isNull(note)) {
                        for (int i = 0; i < numberOfCopiesToPrint; i++) {
                            if (!pageAdded) {
                                copy.open();
                            }
                            pageAdded = true;
                            copy.addDocument(new PdfReader(note.getAttachment().getAttachmentContents()));
                        }
                    }
                    invoiceAddressDetail.setInitialTransmissionDate(new Date(new java.util.Date().getTime()));
                }
            }
            documentService.updateDocument(invoice);
        }
        if (pageAdded) {
            copy.close();
        }
    }

    /**
     * Generates the pdf file for printing the envelopes.
     *
     * @param list
     * @param outputStream
     * @throws DocumentException
     * @throws IOException
     */
    protected void generateCombinedPdfForEnvelopes(Collection<ContractsGrantsInvoiceDocument> list,
            OutputStream outputStream) throws DocumentException, IOException {
        Document document = new Document(
                new Rectangle(ArConstants.InvoiceEnvelopePdf.LENGTH, ArConstants.InvoiceEnvelopePdf.WIDTH));
        PdfWriter.getInstance(document, outputStream);
        boolean pageAdded = false;

        for (ContractsGrantsInvoiceDocument invoice : list) {
            // add a document
            for (InvoiceAddressDetail invoiceAddressDetail : invoice.getInvoiceAddressDetails()) {
                if (ArConstants.InvoiceTransmissionMethod.MAIL
                        .equals(invoiceAddressDetail.getInvoiceTransmissionMethodCode())) {
                    CustomerAddress address = invoiceAddressDetail.getCustomerAddress();

                    Integer numberOfEnvelopesToPrint = address.getCustomerEnvelopesToPrintQuantity();
                    if (ObjectUtils.isNull(numberOfEnvelopesToPrint)) {
                        numberOfEnvelopesToPrint = 1;
                    }
                    for (int i = 0; i < numberOfEnvelopesToPrint; i++) {
                        // if a page has not already been added then open the document.
                        if (!pageAdded) {
                            document.open();
                        }
                        pageAdded = true;
                        document.newPage();
                        // adding the sent From address
                        Organization org = invoice.getInvoiceGeneralDetail().getAward()
                                .getPrimaryAwardOrganization().getOrganization();
                        Paragraph sentBy = generateAddressParagraph(org.getOrganizationName(),
                                org.getOrganizationLine1Address(), org.getOrganizationLine2Address(),
                                org.getOrganizationCityName(), org.getOrganizationStateCode(),
                                org.getOrganizationZipCode(), ArConstants.PdfReportFonts.ENVELOPE_SMALL_FONT);
                        sentBy.setIndentationLeft(ArConstants.InvoiceEnvelopePdf.INDENTATION_LEFT);
                        sentBy.setAlignment(Element.ALIGN_LEFT);

                        // adding the send To address
                        String string;
                        Paragraph sendTo = generateAddressParagraph(address.getCustomerAddressName(),
                                address.getCustomerLine1StreetAddress(), address.getCustomerLine2StreetAddress(),
                                address.getCustomerCityName(), address.getCustomerStateCode(),
                                address.getCustomerZipCode(), ArConstants.PdfReportFonts.ENVELOPE_TITLE_FONT);
                        sendTo.setAlignment(Element.ALIGN_CENTER);
                        sendTo.add(new Paragraph(KFSConstants.BLANK_SPACE));

                        document.add(sentBy);
                        document.add(sendTo);
                    }
                }
            }
        }
        if (pageAdded) {
            document.close();
        }
    }

    /**
     * Generates a PDF paragraph for a given Address
     * @param name the name that this envelope is being sent to
     * @param line1Address the first line of the address
     * @param line2Address the second line of the address
     * @param cityName the name of the city to send this to
     * @param stateCode the code of the state or presumably province to send this to
     * @param postalCode the postal code/zip code to send the enveleope to
     * @param font the font to write in
     * @return a PDF Paragraph for the address
     */
    protected Paragraph generateAddressParagraph(String name, String line1Address, String line2Address,
            String cityName, String stateCode, String postalCode, Font font) {
        Paragraph addressParagraph = new Paragraph();
        addressParagraph.add(new Paragraph(name, font));
        if (!StringUtils.isBlank(line1Address)) {
            addressParagraph.add(new Paragraph(line1Address, font));
        }
        if (!StringUtils.isBlank(line2Address)) {
            addressParagraph.add(new Paragraph(line2Address, font));
        }
        String string = "";
        if (!StringUtils.isBlank(cityName)) {
            string += cityName;
        }
        if (!StringUtils.isBlank(stateCode)) {
            string += ", " + stateCode;
        }
        if (!StringUtils.isBlank(postalCode)) {
            string += "-" + postalCode;
        }
        if (!StringUtils.isBlank(string)) {
            addressParagraph.add(new Paragraph(string, font));
        }
        return addressParagraph;
    }

    /**
     * @see org.kuali.kfs.module.ar.report.service.ContractsGrantsInvoiceReportService#generateCSVToExport(org.kuali.kfs.module.ar.document.ContractsGrantsLOCReviewDocument)
     */
    @Override
    public byte[] convertLetterOfCreditReviewToCSV(ContractsGrantsLetterOfCreditReviewDocument LOCDocument) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        CSVWriter csvWriter = new CSVWriter(new OutputStreamWriter(baos));
        try {
            csvWriter.writeNext(new String[] {
                    getDataDictionaryService().getAttributeLabel(ContractsGrantsLetterOfCreditReviewDetail.class,
                            KFSPropertyConstants.PROPOSAL_NUMBER),
                    getDataDictionaryService().getAttributeLabel(ContractsGrantsLetterOfCreditReviewDetail.class,
                            KFSPropertyConstants.AWARD_DOCUMENT_NUMBER),
                    getDataDictionaryService().getAttributeLabel(ContractsGrantsLetterOfCreditReviewDetail.class,
                            KFSPropertyConstants.ACCOUNT_DESCRIPTION),
                    getDataDictionaryService().getAttributeLabel(ContractsGrantsLetterOfCreditReviewDetail.class,
                            KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE),
                    getDataDictionaryService().getAttributeLabel(ContractsGrantsLetterOfCreditReviewDetail.class,
                            KFSPropertyConstants.ACCOUNT_NUMBER),
                    getDataDictionaryService().getAttributeLabel(ContractsGrantsLetterOfCreditReviewDetail.class,
                            KFSPropertyConstants.ACCOUNT_EXPIRATION_DATE),
                    getDataDictionaryService().getAttributeLabel(ContractsGrantsLetterOfCreditReviewDetail.class,
                            ArPropertyConstants.AWARD_BUDGET_AMOUNT),
                    getDataDictionaryService().getAttributeLabel(ContractsGrantsLetterOfCreditReviewDetail.class,
                            ArPropertyConstants.CLAIM_ON_CASH_BALANCE),
                    getDataDictionaryService().getAttributeLabel(ContractsGrantsLetterOfCreditReviewDetail.class,
                            ArPropertyConstants.AMOUNT_TO_DRAW),
                    getDataDictionaryService().getAttributeLabel(ContractsGrantsLetterOfCreditReviewDetail.class,
                            ArPropertyConstants.FUNDS_NOT_DRAWN) });

            if (CollectionUtils.isNotEmpty(LOCDocument.getHeaderReviewDetails())
                    && CollectionUtils.isNotEmpty(LOCDocument.getAccountReviewDetails())) {
                for (ContractsGrantsLetterOfCreditReviewDetail item : LOCDocument.getHeaderReviewDetails()) {
                    String proposalNumber = org.apache.commons.lang.ObjectUtils.toString(item.getProposalNumber());
                    String awardDocumentNumber = org.apache.commons.lang.ObjectUtils
                            .toString(item.getAwardDocumentNumber());

                    for (ContractsGrantsLetterOfCreditReviewDetail newItem : LOCDocument
                            .getAccountReviewDetails()) {
                        final String accountExpirationDate = ObjectUtils.isNull(newItem.getAccountExpirationDate())
                                ? KFSConstants.EMPTY_STRING
                                : getDateTimeService().toDateString(newItem.getAccountExpirationDate());
                        if (org.apache.commons.lang.ObjectUtils.equals(item.getProposalNumber(),
                                newItem.getProposalNumber())) {
                            csvWriter.writeNext(new String[] { proposalNumber, awardDocumentNumber,
                                    newItem.getAccountDescription(), newItem.getChartOfAccountsCode(),
                                    newItem.getAccountNumber(), accountExpirationDate,
                                    contractsGrantsBillingUtilityService
                                            .formatForCurrency(newItem.getAwardBudgetAmount()),
                                    contractsGrantsBillingUtilityService
                                            .formatForCurrency(newItem.getClaimOnCashBalance()),
                                    contractsGrantsBillingUtilityService
                                            .formatForCurrency(newItem.getAmountToDraw()),
                                    contractsGrantsBillingUtilityService
                                            .formatForCurrency(newItem.getFundsNotDrawn()) });
                        }
                    }
                }
            }
        } finally {
            if (csvWriter != null) {
                try {
                    csvWriter.close();
                } catch (IOException ex) {
                    csvWriter = null;
                    throw new RuntimeException(
                            "problem during ContractsGrantsInvoiceReportServiceImpl.generateCSVToExport()", ex);
                }
            }
        }
        return baos.toByteArray();
    }

    /**
     * @see org.kuali.kfs.module.ar.report.service.ContractsGrantsInvoiceReportService#getPopulatedContractsGrantsInvoiceLookupResults(java.util.Collection)
     */
    @Override
    public Collection<ContractsGrantsInvoiceLookupResult> getPopulatedContractsGrantsInvoiceLookupResults(
            Collection<ContractsAndGrantsBillingAward> awards) {
        Collection<ContractsGrantsInvoiceLookupResult> populatedContractsGrantsInvoiceLookupResults = new ArrayList<ContractsGrantsInvoiceLookupResult>();

        if (awards.size() == 0) {
            return populatedContractsGrantsInvoiceLookupResults;
        }

        Iterator iter = getAwardByAgency(awards).entrySet().iterator();
        ContractsGrantsInvoiceLookupResult contractsGrantsInvoiceLookupResult = null;
        while (iter.hasNext()) {

            Map.Entry entry = (Map.Entry) iter.next();
            List<ContractsAndGrantsBillingAward> list = (List<ContractsAndGrantsBillingAward>) entry.getValue();

            // Get data from first award for agency data
            if (CollectionUtils.isNotEmpty(list)) {
                ContractsAndGrantsBillingAgency agency = list.get(0).getAgency();
                contractsGrantsInvoiceLookupResult = new ContractsGrantsInvoiceLookupResult();

                if (ObjectUtils.isNotNull(agency)) {
                    contractsGrantsInvoiceLookupResult.setAgencyNumber(agency.getAgencyNumber());
                    contractsGrantsInvoiceLookupResult.setAgencyReportingName(agency.getReportingName());
                    contractsGrantsInvoiceLookupResult.setAgencyFullName(agency.getFullName());
                    contractsGrantsInvoiceLookupResult.setCustomerNumber(agency.getCustomerNumber());
                }

                contractsGrantsInvoiceLookupResult.setAwards(list);
                populatedContractsGrantsInvoiceLookupResults.add(contractsGrantsInvoiceLookupResult);
            }
        }

        return populatedContractsGrantsInvoiceLookupResults;
    }

    /**
     * This helper method returns a map of a list of awards by agency
     * @param awards
     * @return a Map of the given CGB Awards, keyed by the agency number
     */
    protected Map<String, List<ContractsAndGrantsBillingAward>> getAwardByAgency(
            Collection<ContractsAndGrantsBillingAward> awards) {
        // use a map to sort awards by agency
        Map<String, List<ContractsAndGrantsBillingAward>> awardsByAgency = new HashMap<>();
        for (ContractsAndGrantsBillingAward award : awards) {// To display awards only if their Billing frequency is not LOC Billing
            if (!StringUtils.isBlank(award.getBillingFrequencyCode()) && !StringUtils
                    .equalsIgnoreCase(award.getBillingFrequencyCode(), ArConstants.LOC_BILLING_SCHEDULE_CODE)) {
                String agencyNumber = award.getAgencyNumber();
                if (awardsByAgency.containsKey(agencyNumber)) {
                    awardsByAgency.get(agencyNumber).add(award);
                } else {
                    List<ContractsAndGrantsBillingAward> awardsByAgencyNumber = new ArrayList<ContractsAndGrantsBillingAward>();
                    awardsByAgencyNumber.add(award);
                    awardsByAgency.put(agencyNumber, awardsByAgencyNumber);
                }
            }
        }

        return awardsByAgency;
    }

    public PersonService getPersonService() {
        return personService;
    }

    public void setPersonService(PersonService personService) {
        this.personService = personService;
    }

    /**
     * Gets the businessObjectService attribute.
     *
     * @return Returns the businessObjectService.
     */
    public BusinessObjectService getBusinessObjectService() {
        return businessObjectService;
    }

    /**
     * Sets the businessObjectService attribute value.
     *
     * @param businessObjectService The businessObjectService to set.
     */
    public void setBusinessObjectService(BusinessObjectService businessObjectService) {
        this.businessObjectService = businessObjectService;
    }

    public ParameterService getParameterService() {
        return parameterService;
    }

    public void setParameterService(ParameterService parameterService) {
        this.parameterService = parameterService;
    }

    public void setConfigService(ConfigurationService configService) {
        this.configService = configService;
    }

    /**
     * Sets the kualiModuleService attribute value.
     *
     * @param kualiModuleService The kualiModuleService to set.
     */
    @NonTransactional
    public void setKualiModuleService(KualiModuleService kualiModuleService) {
        this.kualiModuleService = kualiModuleService;
    }

    /**
     * @return the documentService
     */
    public DocumentService getDocumentService() {
        return documentService;
    }

    /**
     * @param documentService the documentService to set
     */
    public void setDocumentService(DocumentService documentService) {
        this.documentService = documentService;
    }

    public NoteService getNoteService() {
        return noteService;
    }

    public void setNoteService(NoteService noteService) {
        this.noteService = noteService;
    }

    public ReportInfo getReportInfo() {
        return reportInfo;
    }

    public void setReportInfo(ReportInfo reportInfo) {
        this.reportInfo = reportInfo;
    }

    public ReportGenerationService getReportGenerationService() {
        return reportGenerationService;
    }

    public void setReportGenerationService(ReportGenerationService reportGenerationService) {
        this.reportGenerationService = reportGenerationService;
    }

    public ContractsGrantsInvoiceDocumentService getContractsGrantsInvoiceDocumentService() {
        return contractsGrantsInvoiceDocumentService;
    }

    public void setContractsGrantsInvoiceDocumentService(
            ContractsGrantsInvoiceDocumentService contractsGrantsInvoiceDocumentService) {
        this.contractsGrantsInvoiceDocumentService = contractsGrantsInvoiceDocumentService;
    }

    public DateTimeService getDateTimeService() {
        return dateTimeService;
    }

    public void setDateTimeService(DateTimeService dateTimeService) {
        this.dateTimeService = dateTimeService;
    }

    public DataDictionaryService getDataDictionaryService() {
        return dataDictionaryService;
    }

    public void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
        this.dataDictionaryService = dataDictionaryService;
    }

    public ContractsGrantsBillingUtilityService getContractsGrantsBillingUtilityService() {
        return contractsGrantsBillingUtilityService;
    }

    public void setContractsGrantsBillingUtilityService(
            ContractsGrantsBillingUtilityService contractsGrantsBillingUtilityService) {
        this.contractsGrantsBillingUtilityService = contractsGrantsBillingUtilityService;
    }

    public OptionsService getOptionsService() {
        return optionsService;
    }

    public void setOptionsService(OptionsService optionsService) {
        this.optionsService = optionsService;
    }

}