/**
* Copyright © ${project.inceptionYear} Instituto Superior Técnico
*
* This file is part of Fenix IST.
*
* Fenix IST 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.
*
* Fenix IST 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 Fenix IST. If not, see <http://www.gnu.org/licenses/>.
*/
package pt.ist.fenix.task.exportData.accounting;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.fenixedu.academic.domain.Person;
import org.fenixedu.academic.domain.accounting.AcademicEvent;
import org.fenixedu.academic.domain.accounting.AccountingTransactionDetail;
import org.fenixedu.academic.domain.accounting.Event;
import org.fenixedu.academic.domain.accounting.ResidenceEvent;
import org.fenixedu.academic.domain.administrativeOffice.AdministrativeOfficeType;
import org.fenixedu.academic.domain.documents.AnnualIRSDeclarationDocument;
import org.fenixedu.academic.report.IRSCustomDeclaration;
import org.fenixedu.academic.report.IRSCustomDeclaration.IRSDeclarationDTO;
import org.fenixedu.academic.util.report.ReportsUtils;
import org.fenixedu.bennu.core.domain.Bennu;
import org.fenixedu.bennu.scheduler.CronTask;
import org.fenixedu.bennu.scheduler.annotation.Task;
import org.joda.time.LocalDate;
import pt.ist.fenixframework.Atomic;
import pt.ist.fenixframework.Atomic.TxMode;
import com.google.common.base.Strings;
import com.google.common.io.ByteStreams;
@Task(englishTitle = "ExportIRSDeclarations", readOnly = true)
public class ExportIRSDeclarations extends CronTask {
final static int YEAR_TO_PROCESS = new LocalDate().getYear() - 1;
int exportedDeclarationsCounter;
StringBuilder output = new StringBuilder();
static File academicSignature = null;
@Override
public void runTask() throws URISyntaxException, IOException {
taskLog("Start ExportIRSDeclaration");
academicSignature = getSignatureFile();
exportedDeclarationsCounter = 0;
final int PAGE_SIZE = 1000;
Set<Event> paymentsForCivilYear = getEventIdsWithPaymentsForCivilYear();
taskLog("Events to process : " + paymentsForCivilYear.size());
final Map<Person, ExportIRSDeclaration> documents = getDocuments(paymentsForCivilYear);
List<ExportIRSDeclaration> documentsToProcess = new ArrayList<ExportIRSDeclaration>(documents.values());
int documentsSize = documents.size();
final int numberOfPages = (int) Math.ceil(documentsSize / (double) 1000);
taskLog("Number of documents to process: " + documentsSize);
taskLog("Number of pages: " + numberOfPages);
for (int i = 0; i < numberOfPages; i++) {
final int fromIndex = i * PAGE_SIZE;
final int toIndex = (fromIndex + PAGE_SIZE) > documentsSize ? documentsSize : (fromIndex + PAGE_SIZE);
taskLog("From index: " + fromIndex);
taskLog("To index: " + toIndex);
writeDocuments(documentsToProcess.subList(fromIndex, toIndex));
taskLog("Finished page : " + (i + 1));
}
taskLog("Exported document: " + exportedDeclarationsCounter);
academicSignature.delete();
output("pessoas_nao_processadas.tsv", output.toString().getBytes());
taskLog("The end");
}
@Atomic(mode = TxMode.WRITE)
private void writeDocuments(List<ExportIRSDeclaration> documents) {
for (ExportIRSDeclaration exportIRSDeclaration : documents) {
if (exportIRSDeclaration.getPerson().hasAnnualIRSDocumentFor(YEAR_TO_PROCESS)) {
exportIRSDeclaration.getPerson().getAnnualIRSDocumentFor(YEAR_TO_PROCESS).delete();
}
try {
final IRSCustomDeclaration customDeclaration = new IRSCustomDeclaration(exportIRSDeclaration.getDeclarationDTO());
addUnitCoordinatorSignature(customDeclaration);
final byte[] report =
ReportsUtils.generateReport(customDeclaration.getReportTemplateKey(), customDeclaration.getParameters(),
customDeclaration.getDataSource()).getData();
new AnnualIRSDeclarationDocument(exportIRSDeclaration.getPerson(), null, customDeclaration.getReportFileName()
+ ".pdf", report, YEAR_TO_PROCESS);
} catch (Exception e) {
taskLog("Excepção com a pessoa: " + exportIRSDeclaration.getPerson().getUsername());
throw e;
}
}
}
private Map<Person, ExportIRSDeclaration> getDocuments(Set<Event> paymentsForCivilYear) {
final Map<Person, ExportIRSDeclaration> result = new HashMap<Person, ExportIRSDeclaration>();
for (final Event event : paymentsForCivilYear) {
if (isToProcess(event)) {
if (event.getPerson().getStudent() == null) {
output.append("Person without student\t").append(event.getPerson().getName()).append("\t")
.append(event.getPerson().getIdDocumentType().name()).append("\t")
.append(event.getPerson().getDocumentIdNumber()).append("\n");
}
createDeclarationData(event, result);
}
}
return result;
}
private boolean isToProcess(Event event) {
if (event instanceof AcademicEvent) {
final AcademicEvent academicEvent = (AcademicEvent) event;
if (academicEvent.getAdministrativeOffice() != null
&& academicEvent.getAdministrativeOffice().getAdministrativeOfficeType()
.equals(AdministrativeOfficeType.DEGREE)
&& event.getMaxDeductableAmountForLegalTaxes(YEAR_TO_PROCESS).isPositive()) {
return true;
}
} else if (event instanceof ResidenceEvent) {
final ResidenceEvent residenceEvent = (ResidenceEvent) event;
return event.getMaxDeductableAmountForLegalTaxes(YEAR_TO_PROCESS).isPositive();
}
return false;
}
private Set<Event> getEventIdsWithPaymentsForCivilYear() {
Set<Event> result = new HashSet<Event>();
for (AccountingTransactionDetail atd : Bennu.getInstance().getAccountingTransactionDetailsSet()) {
if (atd.getWhenRegistered().getYear() == YEAR_TO_PROCESS && atd.getTransaction() != null) {
if (!atd.getEvent().isCancelled()
&& (atd.getEvent() instanceof AcademicEvent || atd.getEvent() instanceof ResidenceEvent)) {
result.add(atd.getEvent());
}
}
}
return result;
}
private void createDeclarationData(Event event, Map<Person, ExportIRSDeclaration> result) {
ExportIRSDeclaration exportIRSDeclaration = result.get(event.getPerson());
if (exportIRSDeclaration != null) {
exportIRSDeclaration.getDeclarationDTO().addAmount(event, YEAR_TO_PROCESS);
return;
}
final IRSDeclarationDTO declarationDTO = new IRSDeclarationDTO(YEAR_TO_PROCESS, event.getPerson());
if (event.getPerson().getStudent() != null) {
declarationDTO.setStudentNumber(event.getPerson().getStudent().getNumber());
}
declarationDTO.addAmount(event, YEAR_TO_PROCESS);
if (declarationDTO.getTotalAmount().isZero()) {
output.append("Trying to send zero value declaration\t").append(event.getPerson().getUsername()).append("\n");
return;
}
if (isToIgnore(declarationDTO)) {
output.append(String.format("'%s'\tDoc.Id.Number\t'%s'\tignored due to invalid document id\n",
declarationDTO.getPersonName(), declarationDTO.getDocumentIdNumber()));
return;
}
exportIRSDeclaration = new ExportIRSDeclaration(declarationDTO, event.getPerson());
result.put(event.getPerson(), exportIRSDeclaration);
exportedDeclarationsCounter++;
}
private boolean isToIgnore(final IRSDeclarationDTO declaration) {
return Strings.isNullOrEmpty(declaration.getDocumentIdNumber().trim());
}
//Creates temp file due to Jasper Reports limitations
private File getSignatureFile() throws IOException {
File createTempFile = File.createTempFile("tmp", null);
InputStream resourceAsStream = getClass().getResourceAsStream("/signatures/academic_responsible.jpg");
if (resourceAsStream == null) {
throw new Error("Can't load signature file");
}
FileOutputStream to = new FileOutputStream(createTempFile);
ByteStreams.copy(resourceAsStream, to);
to.flush();
to.close();
taskLog("Created temp signature file %s\n", createTempFile.getAbsolutePath());
return createTempFile;
}
private void addUnitCoordinatorSignature(final IRSCustomDeclaration customDeclaration) {
customDeclaration.addParameter("signature", academicSignature);
}
private static class ExportIRSDeclaration implements Serializable {
private static final long serialVersionUID = 1L;
private IRSDeclarationDTO declarationDTO;
private Person person;
public ExportIRSDeclaration(IRSDeclarationDTO declarationDTO, Person person) {
setDeclarationDTO(declarationDTO);
setPerson(person);
}
public IRSDeclarationDTO getDeclarationDTO() {
return declarationDTO;
}
public void setDeclarationDTO(IRSDeclarationDTO declarationDTO) {
this.declarationDTO = declarationDTO;
}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
}
}