/** * 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 com.google.common.io.ByteStreams; import com.google.common.io.Files; import pt.ist.fenixWebFramework.renderers.utils.RenderUtils; @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 = org.fenixedu.academic.util.FileUtils.copyToTemporaryFile(bean.getInputStream()); File unzipDir = null; try { unzipDir = org.fenixedu.academic.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()) { ExecutionYear executionYear = ExecutionYear.readByDateTime(detailLine.getWhenOccuredTransaction()); PaymentCode paymentCodeToProcess = getPaymentCodeToProcess(getPaymentCode(detailLine, result), executionYear, result); reportDTO.addAmount(detailLine, paymentCodeToProcess); } 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); } }