/**
* Copyright © 2002 Instituto Superior Técnico
*
* This file is part of FenixEdu Academic.
*
* FenixEdu Academic is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FenixEdu Academic 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with FenixEdu Academic. If not, see <http://www.gnu.org/licenses/>.
*/
package org.fenixedu.academic.ui.struts.action.manager.payments;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.Arrays;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.fenixedu.academic.domain.ExecutionYear;
import org.fenixedu.academic.domain.Person;
import org.fenixedu.academic.domain.accounting.PaymentCode;
import org.fenixedu.academic.domain.accounting.PaymentCodeMapping;
import org.fenixedu.academic.domain.accounting.PaymentCodeState;
import org.fenixedu.academic.domain.accounting.SibsPaymentFileProcessReport;
import org.fenixedu.academic.dto.accounting.sibsPaymentFileProcessReport.SibsPaymentFileProcessReportDTO;
import org.fenixedu.academic.predicate.AccessControl;
import org.fenixedu.academic.ui.struts.action.base.FenixDispatchAction;
import org.fenixedu.academic.ui.struts.action.manager.ManagerApplications.ManagerPaymentsApp;
import org.fenixedu.academic.util.Bundle;
import org.fenixedu.academic.util.sibs.incomming.SibsIncommingPaymentFile;
import org.fenixedu.academic.util.sibs.incomming.SibsIncommingPaymentFileDetailLine;
import org.fenixedu.bennu.core.i18n.BundleUtil;
import org.fenixedu.bennu.struts.annotations.Forward;
import org.fenixedu.bennu.struts.annotations.Forwards;
import org.fenixedu.bennu.struts.annotations.Mapping;
import org.fenixedu.bennu.struts.portal.EntryPoint;
import org.fenixedu.bennu.struts.portal.StrutsFunctionality;
import org.fenixedu.commons.StringNormalizer;
import pt.ist.fenixWebFramework.renderers.utils.RenderUtils;
import com.google.common.io.ByteStreams;
import com.google.common.io.Files;
@StrutsFunctionality(app = ManagerPaymentsApp.class, path = "sibs-payments", titleKey = "label.payments.uploadPaymentsFile")
@Mapping(path = "/SIBSPayments", module = "manager")
@Forwards({ @Forward(name = "prepareUploadSIBSPaymentFiles", path = "/manager/payments/prepareUploadSIBSPaymentFiles.jsp") })
public class SIBSPaymentsDA extends FenixDispatchAction {
static private final String PAYMENT_FILE_EXTENSION = "INP";
static private final String ZIP_FILE_EXTENSION = "ZIP";
static public class UploadBean implements Serializable {
private static final long serialVersionUID = 3625314688141697558L;
private transient InputStream inputStream;
private String filename;
public InputStream getInputStream() {
return inputStream;
}
public void setInputStream(InputStream inputStream) {
this.inputStream = inputStream;
}
public String getFilename() {
return filename;
}
public void setFilename(String filename) {
this.filename = StringNormalizer.normalize(filename);
}
}
private class ProcessResult {
private final HttpServletRequest request;
private boolean processFailed = false;
public ProcessResult(HttpServletRequest request) {
this.request = request;
}
public void addMessage(String message, String... args) {
addActionMessage("message", request, message, args);
}
public void addError(String message, String... args) {
addActionMessage("message", request, message, args);
reportFailure();
}
protected void reportFailure() {
processFailed = true;
}
public boolean hasFailed() {
return processFailed;
}
}
@EntryPoint
public ActionForward prepareUploadSIBSPaymentFiles(ActionMapping mapping, ActionForm form, HttpServletRequest request,
HttpServletResponse response) {
UploadBean bean = getRenderedObject("uploadBean");
RenderUtils.invalidateViewState("uploadBean");
if (bean == null) {
bean = new UploadBean();
}
request.setAttribute("uploadBean", bean);
return mapping.findForward("prepareUploadSIBSPaymentFiles");
}
public ActionForward uploadSIBSPaymentFiles(ActionMapping mapping, ActionForm form, HttpServletRequest request,
HttpServletResponse response) throws IOException {
UploadBean bean = getRenderedObject("uploadBean");
RenderUtils.invalidateViewState("uploadBean");
if (bean == null) {
return prepareUploadSIBSPaymentFiles(mapping, form, request, response);
}
if (StringUtils.endsWithIgnoreCase(bean.getFilename(), ZIP_FILE_EXTENSION)) {
File zipFile = pt.utl.ist.fenix.tools.util.FileUtils.copyToTemporaryFile(bean.getInputStream());
File unzipDir = null;
try {
unzipDir = pt.utl.ist.fenix.tools.util.FileUtils.unzipFile(zipFile);
if (!unzipDir.isDirectory()) {
addActionMessage("error", request, "error.manager.SIBS.zipException", bean.getFilename());
return prepareUploadSIBSPaymentFiles(mapping, form, request, response);
}
} catch (Exception e) {
addActionMessage("error", request, "error.manager.SIBS.zipException", getMessage(e));
return prepareUploadSIBSPaymentFiles(mapping, form, request, response);
} finally {
zipFile.delete();
}
recursiveZipProcess(unzipDir, request);
} else if (StringUtils.endsWithIgnoreCase(bean.getFilename(), PAYMENT_FILE_EXTENSION)) {
InputStream inputStream = bean.getInputStream();
File dir = Files.createTempDir();
File tmp = new File(dir, bean.getFilename());
tmp.deleteOnExit();
try (OutputStream out = new FileOutputStream(tmp)) {
ByteStreams.copy(inputStream, out);
} finally {
inputStream.close();
}
File file = tmp;
ProcessResult result = new ProcessResult(request);
result.addMessage("label.manager.SIBS.processingFile", file.getName());
try {
processFile(file, request);
} catch (FileNotFoundException e) {
addActionMessage("error", request, "error.manager.SIBS.zipException", getMessage(e));
} catch (IOException e) {
addActionMessage("error", request, "error.manager.SIBS.IOException", getMessage(e));
} catch (Exception e) {
addActionMessage("error", request, "error.manager.SIBS.fileException", getMessage(e));
} finally {
file.delete();
}
} else {
addActionMessage("error", request, "error.manager.SIBS.notSupportedExtension", bean.getFilename());
}
return prepareUploadSIBSPaymentFiles(mapping, form, request, response);
}
private static String getMessage(Exception ex) {
String message = (ex.getMessage() == null) ? ex.getClass().getSimpleName() : ex.getMessage();
return BundleUtil.getString(Bundle.MANAGER, message);
}
private void recursiveZipProcess(File unzipDir, HttpServletRequest request) {
File[] filesInZip = unzipDir.listFiles();
Arrays.sort(filesInZip);
for (File file : filesInZip) {
if (file.isDirectory()) {
recursiveZipProcess(file, request);
} else {
if (!StringUtils.endsWithIgnoreCase(file.getName(), PAYMENT_FILE_EXTENSION)) {
file.delete();
continue;
}
try {
processFile(file, request);
} catch (FileNotFoundException e) {
addActionMessage("message", request, "error.manager.SIBS.zipException", getMessage(e));
} catch (IOException e) {
addActionMessage("message", request, "error.manager.SIBS.IOException", getMessage(e));
} catch (Exception e) {
addActionMessage("message", request, "error.manager.SIBS.fileException", getMessage(e));
} finally {
file.delete();
}
}
}
unzipDir.delete();
}
private void processFile(File file, HttpServletRequest request) throws IOException {
final ProcessResult result = new ProcessResult(request);
result.addMessage("label.manager.SIBS.processingFile", file.getName());
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream(file);
final Person person = AccessControl.getPerson();
final SibsIncommingPaymentFile sibsFile = SibsIncommingPaymentFile.parse(file.getName(), fileInputStream);
result.addMessage("label.manager.SIBS.linesFound", String.valueOf(sibsFile.getDetailLines().size()));
result.addMessage("label.manager.SIBS.startingProcess");
for (final SibsIncommingPaymentFileDetailLine detailLine : sibsFile.getDetailLines()) {
try {
processCode(detailLine, person, result);
} catch (Exception e) {
result.addError("error.manager.SIBS.processException", detailLine.getCode(), getMessage(e));
}
}
result.addMessage("label.manager.SIBS.creatingReport");
if (!result.hasFailed()) {
if (SibsPaymentFileProcessReport.hasAny(sibsFile.getWhenProcessedBySibs(), sibsFile.getVersion())) {
result.addMessage("warning.manager.SIBS.reportAlreadyProcessed");
} else {
try {
createSibsFileReport(sibsFile, result);
} catch (Exception ex) {
result.addError("error.manager.SIBS.reportException", getMessage(ex));
}
}
} else {
result.addError("error.manager.SIBS.nonProcessedCodes");
}
result.addMessage("label.manager.SIBS.done");
} finally {
if (fileInputStream != null) {
fileInputStream.close();
}
}
}
private void processCode(SibsIncommingPaymentFileDetailLine detailLine, Person person, ProcessResult result) throws Exception {
final PaymentCode paymentCode = getPaymentCode(detailLine, result);
if (paymentCode == null) {
result.addMessage("error.manager.SIBS.codeNotFound", detailLine.getCode());
throw new Exception();
}
final PaymentCode codeToProcess =
getPaymentCodeToProcess(paymentCode, ExecutionYear.readByDateTime(detailLine.getWhenOccuredTransaction()), result);
if (codeToProcess.getState() == PaymentCodeState.INVALID) {
result.addMessage("warning.manager.SIBS.invalidCode", codeToProcess.getCode());
}
if (codeToProcess.isProcessed() && codeToProcess.getWhenUpdated().isBefore(detailLine.getWhenOccuredTransaction())) {
result.addMessage("warning.manager.SIBS.codeAlreadyProcessed", codeToProcess.getCode());
}
codeToProcess.process(person, detailLine.getAmount(), detailLine.getWhenOccuredTransaction(),
detailLine.getSibsTransactionId(), StringUtils.EMPTY);
}
private void createSibsFileReport(SibsIncommingPaymentFile sibsIncomingPaymentFile, ProcessResult result) throws Exception {
final SibsPaymentFileProcessReportDTO reportDTO = new SibsPaymentFileProcessReportDTO(sibsIncomingPaymentFile);
for (final SibsIncommingPaymentFileDetailLine detailLine : sibsIncomingPaymentFile.getDetailLines()) {
reportDTO.addAmount(detailLine, getPaymentCode(detailLine, result));
}
SibsPaymentFileProcessReport.create(reportDTO);
result.addMessage("label.manager.SIBS.reportCreated");
}
private PaymentCode getPaymentCodeToProcess(final PaymentCode paymentCode, ExecutionYear executionYear, ProcessResult result) {
final PaymentCodeMapping mapping = paymentCode.getOldPaymentCodeMapping(executionYear);
final PaymentCode codeToProcess;
if (mapping != null) {
result.addMessage("warning.manager.SIBS.foundMapping", paymentCode.getCode(), mapping.getNewPaymentCode().getCode());
result.addMessage("warning.manager.SIBS.invalidating", paymentCode.getCode());
codeToProcess = mapping.getNewPaymentCode();
paymentCode.setState(PaymentCodeState.INVALID);
} else {
codeToProcess = paymentCode;
}
return codeToProcess;
}
private PaymentCode getPaymentCode(final SibsIncommingPaymentFileDetailLine detailLine, ProcessResult result) {
return getPaymentCode(detailLine.getCode(), result);
}
private PaymentCode getPaymentCode(final String code, ProcessResult result) {
/*
* TODO:
*
* 09/07/2009 - Payments are not related only to students. readAll() may
* be heavy to get the PaymentCode.
*
*
* Ask Nadir and Joao what is best way to deal with PaymentCode
* retrieval.
*/
return PaymentCode.readByCode(code);
}
}