/*
* 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.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Map;
import java.util.zip.CRC32;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import javax.mail.MessagingException;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
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.InvoiceAddressDetail;
import org.kuali.kfs.module.ar.document.ContractsGrantsInvoiceDocument;
import org.kuali.kfs.module.ar.document.service.ContractsGrantsInvoiceDocumentService;
import org.kuali.kfs.module.ar.report.service.ContractsGrantsInvoiceReportService;
import org.kuali.kfs.module.ar.report.service.TransmitContractsAndGrantsInvoicesService;
import org.kuali.kfs.module.ar.service.AREmailService;
import org.kuali.kfs.sys.KFSConstants;
import org.kuali.kfs.sys.KFSKeyConstants;
import org.kuali.kfs.sys.KFSPropertyConstants;
import org.kuali.kfs.sys.context.SpringContext;
import org.kuali.kfs.sys.util.KfsDateUtils;
import org.kuali.rice.core.api.datetime.DateTimeService;
import org.kuali.rice.core.api.search.SearchOperator;
import org.kuali.rice.core.api.util.type.KualiDecimal;
import org.kuali.rice.kew.api.document.DocumentStatus;
import org.kuali.rice.kew.api.exception.WorkflowException;
import org.kuali.rice.kim.api.identity.Person;
import org.kuali.rice.kim.api.identity.PersonService;
import org.kuali.rice.kim.api.identity.principal.Principal;
import org.kuali.rice.kim.api.services.KimApiServiceLocator;
import org.kuali.rice.krad.exception.InvalidAddressException;
import org.kuali.rice.krad.exception.ValidationException;
import org.kuali.rice.krad.service.DocumentService;
import org.kuali.rice.krad.util.GlobalVariables;
import org.kuali.rice.krad.util.ObjectUtils;
import com.lowagie.text.DocumentException;
/**
* Default implementation of the TransmitContractsAndGrantsInvoicesService
*/
public class TransmitContractsAndGrantsInvoicesServiceImpl implements TransmitContractsAndGrantsInvoicesService {
protected ContractsGrantsInvoiceDocumentService contractsGrantsInvoiceDocumentService;
protected ContractsGrantsInvoiceReportService contractsGrantsInvoiceReportService;
protected DateTimeService dateTimeService;
protected DocumentService documentService;
protected AREmailService arEmailService;
/**
* @see org.kuali.kfs.module.ar.report.service.TransmitContractsAndGrantsInvoicesService#getInvoicesByParametersFromRequest(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String)
*/
@Override
public Collection<ContractsGrantsInvoiceDocument> getInvoicesByParametersFromRequest(Map fieldValues) throws WorkflowException, ParseException {
Date fromDate = null;
Date toDate = null;
String unformattedToDate = (String)fieldValues.get(ArPropertyConstants.TransmitContractsAndGrantsInvoicesLookupFields.INVOICE_PRINT_DATE_TO);
if (StringUtils.isNotEmpty(unformattedToDate)) {
toDate = dateTimeService.convertToDate(unformattedToDate);
}
String unformattedFromDate = (String)fieldValues.get(ArPropertyConstants.TransmitContractsAndGrantsInvoicesLookupFields.INVOICE_PRINT_DATE_FROM);
if (StringUtils.isNotEmpty(unformattedFromDate)) {
fromDate = dateTimeService.convertToDate(unformattedFromDate);
}
String invoiceInitiatorPrincipalName = (String) fieldValues.get(ArPropertyConstants.TransmitContractsAndGrantsInvoicesLookupFields.INVOICE_INITIATOR_PRINCIPAL_NAME);
if (StringUtils.isNotEmpty(invoiceInitiatorPrincipalName)) {
Principal principal = KimApiServiceLocator.getIdentityService().getPrincipalByPrincipalName(invoiceInitiatorPrincipalName);
if (ObjectUtils.isNotNull(principal)) {
fieldValues.put(KFSPropertyConstants.DOCUMENT_HEADER + "." + KFSPropertyConstants.INITIATOR_PRINCIPAL_ID, principal.getPrincipalId());
} else {
throw new IllegalArgumentException("The parameter value for initiatorPrincipalName [" + invoiceInitiatorPrincipalName + "] passed in does not map to a person.");
}
}
String proposalNumber = (String) fieldValues.get(KFSPropertyConstants.PROPOSAL_NUMBER);
if (StringUtils.isNotEmpty(proposalNumber)) {
fieldValues.put(ArPropertyConstants.ContractsGrantsInvoiceDocumentFields.PROPOSAL_NUMBER, proposalNumber);
}
String invoiceAmount = (String) fieldValues.get(ArPropertyConstants.TransmitContractsAndGrantsInvoicesLookupFields.INVOICE_AMOUNT);
if (StringUtils.isNotEmpty(invoiceAmount)) {
fieldValues.put(KFSPropertyConstants.DOCUMENT_HEADER + "." + KFSPropertyConstants.FINANCIAL_DOCUMENT_TOTAL_AMOUNT, invoiceAmount);
}
String invoiceTransmissionMethodCode = (String) fieldValues.get(ArPropertyConstants.INVOICE_TRANSMISSION_METHOD_CODE);
if (StringUtils.isNotBlank(invoiceTransmissionMethodCode)) {
fieldValues.put(ArPropertyConstants.TransmitContractsAndGrantsInvoicesLookupFields.INVOICE_TRANSMISSION_METHOD_CODE, invoiceTransmissionMethodCode);
}
fieldValues.put(ArPropertyConstants.TransmitContractsAndGrantsInvoicesLookupFields.INITIAL_TRANSMISSION_DATE, SearchOperator.NULL.op());
fieldValues.put(ArPropertyConstants.TransmitContractsAndGrantsInvoicesLookupFields.INVOICE_TRANSMISSION_METHOD_CODE, invoiceTransmissionMethodCode);
fieldValues.put(KFSPropertyConstants.DOCUMENT_HEADER + "." + KFSPropertyConstants.WORKFLOW_DOCUMENT_STATUS_CODE, DocumentStatus.FINAL.getCode() + SearchOperator.OR.op() + DocumentStatus.PROCESSED.getCode());
// filter out LOC CINV docs, we don't want those included in this process
fieldValues.put(ArPropertyConstants.INVOICE_GENERAL_DETAIL + "." + ArPropertyConstants.BILLING_FREQUENCY_CODE, SearchOperator.NOT + ArConstants.LOC_BILLING_SCHEDULE_CODE);
Collection<ContractsGrantsInvoiceDocument> list = getContractsGrantsInvoiceDocumentService().retrieveAllCGInvoicesByCriteria(fieldValues);
Collection<ContractsGrantsInvoiceDocument> finalList = new ArrayList<ContractsGrantsInvoiceDocument>();
for (ContractsGrantsInvoiceDocument item : list) {
ContractsGrantsInvoiceDocument invoice = (ContractsGrantsInvoiceDocument)getDocumentService().getByDocumentHeaderId(item.getDocumentNumber());
if (!invoice.isInvoiceReversal() && !invoice.hasInvoiceBeenCorrected()) {
if (isInvoiceBetween(invoice, fromDate, toDate)) {
if ((StringUtils.equals(ArConstants.InvoiceTransmissionMethod.EMAIL, invoiceTransmissionMethodCode) && isInvoiceValidToEmail(invoice)) ||
(StringUtils.equals(ArConstants.InvoiceTransmissionMethod.MAIL, invoiceTransmissionMethodCode) && isInvoiceValidToMail(invoice))) {
finalList.add(invoice);
}
}
}
}
return finalList;
}
/**
* Checks whether invoice is between the dates provided.
* @param invoice
* @param fromDate
* @param toDate
* @return
*/
protected boolean isInvoiceBetween(ContractsGrantsInvoiceDocument invoice, Date fromDate, Date toDate) {
Date dateCreated = invoice.getDocumentHeader().getWorkflowDocument().getDateCreated().toDate();
if (ObjectUtils.isNotNull(fromDate)) {
if (fromDate.after(dateCreated)) {
return false;
}
}
if (ObjectUtils.isNotNull(toDate)) {
if (toDate.before(dateCreated) && !KfsDateUtils.isSameDay(toDate, dateCreated)) {
return false;
}
}
return true;
}
@Override
public boolean isInvoiceValidToEmail(ContractsGrantsInvoiceDocument contractsGrantsInvoiceDocument) {
for (InvoiceAddressDetail invoiceAddressDetail : contractsGrantsInvoiceDocument.getInvoiceAddressDetails()) {
if (ObjectUtils.isNull(invoiceAddressDetail.getInitialTransmissionDate()) && ArConstants.InvoiceTransmissionMethod.EMAIL.equals(invoiceAddressDetail.getInvoiceTransmissionMethodCode())) {
return true;
}
}
return false;
}
@Override
public boolean isInvoiceValidToMail(ContractsGrantsInvoiceDocument contractsGrantsInvoiceDocument) {
for (InvoiceAddressDetail invoiceAddressDetail : contractsGrantsInvoiceDocument.getInvoiceAddressDetails()) {
if (ObjectUtils.isNull(invoiceAddressDetail.getInitialTransmissionDate()) && ArConstants.InvoiceTransmissionMethod.MAIL.equals(invoiceAddressDetail.getInvoiceTransmissionMethodCode())) {
return true;
}
}
return false;
}
@Override
public boolean printInvoicesAndEnvelopesZip(Collection<ContractsGrantsInvoiceDocument> list, ByteArrayOutputStream baos) throws DocumentException, IOException {
if (CollectionUtils.isNotEmpty(list)) {
byte[] envelopes = contractsGrantsInvoiceReportService.combineInvoicePdfEnvelopes(list);
byte[] report = contractsGrantsInvoiceReportService.combineInvoicePdfs(list);
boolean invoiceFileWritten = false;
boolean envelopeFileWritten = false;
ZipOutputStream zos = new ZipOutputStream(baos);
try {
int bytesRead;
byte[] buffer = new byte[1024];
CRC32 crc = new CRC32();
String invoiceFileName = ArConstants.INVOICES_FILE_PREFIX + getDateTimeService().toDateStringForFilename(getDateTimeService().getCurrentDate()) + KFSConstants.ReportGeneration.PDF_FILE_EXTENSION;
invoiceFileWritten = writeFile(report, zos, invoiceFileName);
String envelopeFileName = ArConstants.INVOICE_ENVELOPES_FILE_PREFIX + getDateTimeService().toDateStringForFilename(getDateTimeService().getCurrentDate()) + KFSConstants.ReportGeneration.PDF_FILE_EXTENSION;
envelopeFileWritten = writeFile(envelopes, zos, envelopeFileName);
} finally {
zos.close();
}
return invoiceFileWritten || envelopeFileWritten;
}
return false;
}
/**
*
* @param report
* @param invoiceFileWritten
* @param zos
* @param buffer
* @param crc
* @return
* @throws IOException
*/
private boolean writeFile(byte[] arrayToWrite, ZipOutputStream zos, String fileName) throws IOException {
int bytesRead;
byte[] buffer = new byte[1024];
CRC32 crc = new CRC32();
if (ObjectUtils.isNotNull(arrayToWrite) && arrayToWrite.length > 0) {
BufferedInputStream bis = new BufferedInputStream(new ByteArrayInputStream(arrayToWrite));
try {
while ((bytesRead = bis.read(buffer)) != -1) {
crc.update(buffer, 0, bytesRead);
}
bis.close();
// Reset to beginning of input stream
bis = new BufferedInputStream(new ByteArrayInputStream(arrayToWrite));
ZipEntry entry = new ZipEntry(fileName);
entry.setMethod(ZipEntry.STORED);
entry.setCompressedSize(arrayToWrite.length);
entry.setSize(arrayToWrite.length);
entry.setCrc(crc.getValue());
zos.putNextEntry(entry);
while ((bytesRead = bis.read(buffer)) != -1) {
zos.write(buffer, 0, bytesRead);
}
} finally {
bis.close();
}
return true;
}
return false;
}
@Override
public void validateSearchParameters(Map<String,String> fieldValues) {
String invoiceTransmissionMethodCode = fieldValues.get(ArPropertyConstants.INVOICE_TRANSMISSION_METHOD_CODE);
String invoiceInitiatorPrincipalName = fieldValues.get(ArPropertyConstants.TransmitContractsAndGrantsInvoicesLookupFields.INVOICE_INITIATOR_PRINCIPAL_NAME);
String invoicePrintDateFromString = fieldValues.get(ArPropertyConstants.TransmitContractsAndGrantsInvoicesLookupFields.INVOICE_PRINT_DATE_FROM);
String invoicePrintDateToString = fieldValues.get(ArPropertyConstants.TransmitContractsAndGrantsInvoicesLookupFields.INVOICE_PRINT_DATE_TO);
String invoiceAmount = fieldValues.get(ArPropertyConstants.TransmitContractsAndGrantsInvoicesLookupFields.INVOICE_AMOUNT);
// To validate the input fields before fetching invoices.
if (StringUtils.isBlank(invoiceTransmissionMethodCode)) {
GlobalVariables.getMessageMap().putError(ArPropertyConstants.INVOICE_TRANSMISSION_METHOD_CODE, KFSKeyConstants.ERROR_REQUIRED, ArPropertyConstants.TransmitContractsAndGrantsInvoicesLookupFields.INVOICE_TRANSMISSION_METHOD_CODE_LABEL);
}
if (StringUtils.isNotBlank(invoicePrintDateFromString)) {
try {
dateTimeService.convertToDate(invoicePrintDateFromString);
}
catch (ParseException e) {
GlobalVariables.getMessageMap().putError(ArPropertyConstants.TransmitContractsAndGrantsInvoicesLookupFields.INVOICE_PRINT_DATE_FROM, KFSKeyConstants.ERROR_DATE_TIME, ArConstants.PRINT_INVOICES_FROM_LABEL);
}
}
if (StringUtils.isNotBlank(invoicePrintDateToString)) {
try {
dateTimeService.convertToDate(invoicePrintDateToString);
}
catch (ParseException e) {
GlobalVariables.getMessageMap().putError(ArPropertyConstants.TransmitContractsAndGrantsInvoicesLookupFields.INVOICE_PRINT_DATE_TO, KFSKeyConstants.ERROR_DATE_TIME, ArConstants.PRINT_INVOICES_TO_LABEL);
}
}
if (StringUtils.isNotBlank(invoiceAmount) && !KualiDecimal.isNumeric(invoiceAmount)) {
GlobalVariables.getMessageMap().putError(ArPropertyConstants.TransmitContractsAndGrantsInvoicesLookupFields.INVOICE_AMOUNT, KFSKeyConstants.ERROR_NUMERIC, ArConstants.INVOICE_AMOUNT_LABEL);
}
if (StringUtils.isNotEmpty(invoiceInitiatorPrincipalName)) {
Person person = SpringContext.getBean(PersonService.class).getPersonByPrincipalName(invoiceInitiatorPrincipalName);
if (ObjectUtils.isNull(person)) {
GlobalVariables.getMessageMap().putErrorForSectionId(ArPropertyConstants.LOOKUP_SECTION_ID, ArKeyConstants.NO_PRINCIPAL_NAME_FOUND);
}
}
if (GlobalVariables.getMessageMap().hasErrors()) {
throw new ValidationException("Error(s) in search criteria");
}
}
/**
* @see org.kuali.kfs.module.ar.report.service.TransmitContractsAndGrantsInvoicesService#sendEmailForListofInvoicesToAgency(java.util.Collection)
*/
@Override
public boolean sendEmailForListofInvoicesToAgency(Collection<ContractsGrantsInvoiceDocument> list) throws InvalidAddressException, MessagingException {
return arEmailService.sendInvoicesViaEmail(list);
}
public ContractsGrantsInvoiceDocumentService getContractsGrantsInvoiceDocumentService() {
return contractsGrantsInvoiceDocumentService;
}
public void setContractsGrantsInvoiceDocumentService(ContractsGrantsInvoiceDocumentService contractsGrantsInvoiceDocumentService) {
this.contractsGrantsInvoiceDocumentService = contractsGrantsInvoiceDocumentService;
}
public ContractsGrantsInvoiceReportService getContractsGrantsInvoiceReportService() {
return contractsGrantsInvoiceReportService;
}
public void setContractsGrantsInvoiceReportService(ContractsGrantsInvoiceReportService contractsGrantsInvoiceReportService) {
this.contractsGrantsInvoiceReportService = contractsGrantsInvoiceReportService;
}
public DateTimeService getDateTimeService() {
return dateTimeService;
}
public void setDateTimeService(DateTimeService dateTimeService) {
this.dateTimeService = dateTimeService;
}
public DocumentService getDocumentService() {
return documentService;
}
public void setDocumentService(DocumentService documentService) {
this.documentService = documentService;
}
public AREmailService getArEmailService() {
return arEmailService;
}
public void setArEmailService(AREmailService arEmailService) {
this.arEmailService = arEmailService;
}
}