/**
* 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.domain.accounting.events.export;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
import org.fenixedu.academic.domain.ExecutionYear;
import org.fenixedu.academic.domain.Person;
import org.fenixedu.academic.domain.accounting.Event;
import org.fenixedu.academic.domain.accounting.PaymentCode;
import org.fenixedu.academic.domain.accounting.ResidenceEvent;
import org.fenixedu.academic.domain.accounting.events.AdministrativeOfficeFeeAndInsuranceEvent;
import org.fenixedu.academic.domain.accounting.events.gratuity.DfaGratuityEvent;
import org.fenixedu.academic.domain.accounting.events.gratuity.GratuityEventWithPaymentPlan;
import org.fenixedu.academic.domain.accounting.events.gratuity.StandaloneEnrolmentGratuityEvent;
import org.fenixedu.academic.domain.accounting.events.insurance.InsuranceEvent;
import org.fenixedu.academic.domain.accounting.paymentCodes.AccountingEventPaymentCode;
import org.fenixedu.academic.domain.accounting.paymentCodes.IndividualCandidacyPaymentCode;
import org.fenixedu.academic.domain.accounting.paymentCodes.rectorate.RectoratePaymentCode;
import org.fenixedu.academic.domain.candidacy.StudentCandidacy;
import org.fenixedu.academic.domain.exceptions.DomainException;
import org.fenixedu.academic.domain.person.RoleType;
import org.fenixedu.academic.util.Money;
import org.fenixedu.bennu.core.domain.Bennu;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.joda.time.YearMonthDay;
import pt.ist.fenix.domain.student.importation.DgesStudentImportationProcess;
import pt.ist.fenix.util.sibs.SibsOutgoingPaymentFile;
import pt.ist.fenixframework.Atomic;
import pt.ist.fenixframework.Atomic.TxMode;
import pt.ist.fenixframework.FenixFramework;
public class SIBSOutgoingPaymentFile extends SIBSOutgoingPaymentFile_Base {
private static final Comparator<SIBSOutgoingPaymentFile> SUCCESSFUL_SENT_DATE_TIME_COMPARATOR =
new Comparator<SIBSOutgoingPaymentFile>() {
@Override
public int compare(SIBSOutgoingPaymentFile o1, SIBSOutgoingPaymentFile o2) {
return o1.getSuccessfulSentDate().compareTo(o2.getSuccessfulSentDate());
}
};
private static final Comparator<SIBSOutgoingPaymentFile> CREATION_DATE_TIME_COMPARATOR =
new Comparator<SIBSOutgoingPaymentFile>() {
@Override
public int compare(SIBSOutgoingPaymentFile o1, SIBSOutgoingPaymentFile o2) {
if (o1.getUploadTime() == null && o2.getUploadTime() == null) {
return o1.getExternalId().compareTo(o2.getExternalId());
} else if (o1.getUploadTime() == null) {
return -1;
} else if (o2.getUploadTime() == null) {
return 1;
} else {
return o1.getUploadTime().compareTo(o2.getUploadTime());
}
}
};
public SIBSOutgoingPaymentFile(DateTime lastSuccessfulSentDateTime) {
super();
setExecutionYear(subjectExecutionYear());
try {
StringBuilder errorsBuilder = new StringBuilder();
byte[] paymentFileContents = createPaymentFile(lastSuccessfulSentDateTime, errorsBuilder).getBytes("ASCII");
setErrors(errorsBuilder.toString());
init(outgoingFilename(), outgoingFilename(), paymentFileContents, RoleType.MANAGER.actualGroup());
} catch (UnsupportedEncodingException e) {
throw new DomainException(e.getMessage(), e);
}
}
@Override
protected List<Class> getAcceptedEventClasses() {
return Arrays.asList(new Class[] { AdministrativeOfficeFeeAndInsuranceEvent.class, GratuityEventWithPaymentPlan.class,
DfaGratuityEvent.class, InsuranceEvent.class, ResidenceEvent.class, StandaloneEnrolmentGratuityEvent.class });
}
protected String createPaymentFile(DateTime lastSuccessfulSentDateTime, StringBuilder errorsBuilder) {
final ExecutionYear executionYear = subjectExecutionYear();
final SibsOutgoingPaymentFile sibsOutgoingPaymentFile =
new SibsOutgoingPaymentFile(SOURCE_INSTITUTION_ID, DESTINATION_INSTITUTION_ID, ENTITY_CODE,
lastSuccessfulSentDateTime);
for (final Entry<Person, List<Event>> entry : getNotPayedEventsGroupedByPerson(executionYear, errorsBuilder).entrySet()) {
for (final Event event : entry.getValue()) {
addCalculatedPaymentCodesFromEvent(sibsOutgoingPaymentFile, event, errorsBuilder);
}
}
exportDgesStudentCandidacyPaymentCodes(sibsOutgoingPaymentFile, errorsBuilder);
try {
final ExportThingy exportThingy = new ExportThingy(sibsOutgoingPaymentFile, errorsBuilder);
exportThingy.start();
exportThingy.join();
} catch (Throwable e) {
appendToErrors(errorsBuilder, "", e);
}
try {
final ExportAnotherThingy exportThingy = new ExportAnotherThingy(sibsOutgoingPaymentFile, errorsBuilder);
exportThingy.start();
exportThingy.join();
} catch (Throwable e) {
appendToErrors(errorsBuilder, "", e);
}
this.setPrintedPaymentCodes(sibsOutgoingPaymentFile.getAssociatedPaymentCodes());
invalidateOldPaymentCodes(sibsOutgoingPaymentFile, errorsBuilder);
return sibsOutgoingPaymentFile.render();
}
private void invalidateOldPaymentCodes(SibsOutgoingPaymentFile sibsOutgoingPaymentFile, StringBuilder errorsBuilder) {
SIBSOutgoingPaymentFile previous = readPreviousOfLastGeneratedPaymentFile();
PrintedPaymentCodes currentSet = this.getPrintedPaymentCodes();
PrintedPaymentCodes previousSet = previous == null ? null : previous.getPrintedPaymentCodes();
if (previousSet != null && previousSet.getPaymentCodes() != null) {
Collection<String> oldPaymentCodes =
CollectionUtils.subtract(previousSet.getPaymentCodes(), currentSet.getPaymentCodes());
for (String oldCode : oldPaymentCodes) {
sibsOutgoingPaymentFile.addLine(oldCode, new Money("0.01"), new Money("0.01"), new DateTime().minusDays(5)
.toYearMonthDay(), new DateTime().minusDays(5).toYearMonthDay());
}
}
}
private void exportDgesStudentCandidacyPaymentCodes(SibsOutgoingPaymentFile sibsOutgoingPaymentFile,
StringBuilder errorsBuilder) {
try {
CalculateStudentCandidacyPaymentCodes workThread =
new CalculateStudentCandidacyPaymentCodes(sibsOutgoingPaymentFile, errorsBuilder);
workThread.start();
workThread.join();
} catch (Throwable e) {
appendToErrors(errorsBuilder, "", e);
}
}
protected void exportIndividualCandidacyPaymentCodes(SibsOutgoingPaymentFile sibsFile, StringBuilder errorsBuilder) {
Set<? extends PaymentCode> individualCandidacyPaymentCodeList = Bennu.getInstance().getPaymentCodesSet();
LocalDate date = new LocalDate();
for (PaymentCode paymentCode : individualCandidacyPaymentCodeList) {
if (!(paymentCode instanceof IndividualCandidacyPaymentCode)) {
continue;
}
if (!paymentCode.getStartDate().isAfter(date) && !paymentCode.getEndDate().isBefore(date) && paymentCode.isNew()) {
addPaymentCode(sibsFile, paymentCode, errorsBuilder);
}
}
}
protected void exportRectoratePaymentCodes(SibsOutgoingPaymentFile sibsFile, StringBuilder errorsBuilder) {
List<RectoratePaymentCode> allRectoratePaymentCodes = RectoratePaymentCode.getAllRectoratePaymentCodes();
final LocalDate now = new LocalDate();
for (RectoratePaymentCode rectoratePaymentCode : allRectoratePaymentCodes) {
if (rectoratePaymentCode.getEndDate().isAfter(now)) {
addPaymentCode(sibsFile, rectoratePaymentCode, errorsBuilder);
}
}
}
protected void addPaymentCode(final SibsOutgoingPaymentFile file, final PaymentCode paymentCode, StringBuilder errorsBuilder) {
try {
file.addAssociatedPaymentCode(paymentCode);
file.addLine(paymentCode.getCode(), paymentCode.getMinAmount(), paymentCode.getMaxAmount(),
paymentCode.getStartDate(), paymentCode.getEndDate());
} catch (Throwable e) {
appendToErrors(errorsBuilder, paymentCode.getExternalId(), e);
}
}
protected void addCalculatedPaymentCodesFromEvent(final SibsOutgoingPaymentFile file, final Event event,
StringBuilder errorsBuilder) {
try {
CalculatePaymentCodes thread = new CalculatePaymentCodes(event.getExternalId(), errorsBuilder, file);
thread.start();
thread.join();
} catch (Throwable e) {
appendToErrors(errorsBuilder, event.getExternalId(), e);
}
}
private static ExecutionYear subjectExecutionYear() {
return ExecutionYear.readCurrentExecutionYear();
}
private String outgoingFilename() {
return String.format("SIBS-%s.txt", new DateTime().toString("dd-MM-yyyy_H_m_s"));
}
public static List<SIBSOutgoingPaymentFile> readSuccessfulSentPaymentFiles() {
List<SIBSOutgoingPaymentFile> files = new ArrayList<SIBSOutgoingPaymentFile>();
CollectionUtils.select(readGeneratedPaymentFiles(), new Predicate() {
@Override
public boolean evaluate(Object arg0) {
return ((SIBSOutgoingPaymentFile) arg0).getSuccessfulSentDate() != null;
}
}, files);
return files;
}
public static SIBSOutgoingPaymentFile readLastSuccessfulSentPaymentFile() {
List<SIBSOutgoingPaymentFile> files = readSuccessfulSentPaymentFiles();
if (files.isEmpty()) {
return null;
}
Collections.sort(files, Collections.reverseOrder(SUCCESSFUL_SENT_DATE_TIME_COMPARATOR));
return files.iterator().next();
}
public static SIBSOutgoingPaymentFile readLastGeneratedPaymentFile() {
List<SIBSOutgoingPaymentFile> files = readGeneratedPaymentFiles();
Collections.sort(files, Collections.reverseOrder(CREATION_DATE_TIME_COMPARATOR));
return files.iterator().next();
}
public static SIBSOutgoingPaymentFile readPreviousOfLastGeneratedPaymentFile() {
List<SIBSOutgoingPaymentFile> files = readGeneratedPaymentFiles();
Collections.sort(files, Collections.reverseOrder(CREATION_DATE_TIME_COMPARATOR));
if (files.size() <= 1) {
return null;
}
return files.get(1);
}
public static List<SIBSOutgoingPaymentFile> readGeneratedPaymentFiles() {
return new ArrayList<SIBSOutgoingPaymentFile>(subjectExecutionYear().getSIBSOutgoingPaymentFilesSet());
}
@Atomic
public void markAsSuccessfulSent(DateTime dateTime) {
setSuccessfulSentDate(dateTime);
}
private class CalculatePaymentCodes extends Thread {
private final String eventExternalId;
private final StringBuilder errorsBuilder;
private final SibsOutgoingPaymentFile sibsFile;
public CalculatePaymentCodes(String eventExternalId, StringBuilder errorsBuilder, SibsOutgoingPaymentFile sibsFile) {
this.eventExternalId = eventExternalId;
this.errorsBuilder = errorsBuilder;
this.sibsFile = sibsFile;
}
@Override
@Atomic(mode = TxMode.READ)
public void run() {
try {
txDo();
} catch (Throwable e) {
appendToErrors(errorsBuilder, eventExternalId, e);
}
}
@Atomic
private void txDo() {
Event event = FenixFramework.getDomainObject(eventExternalId);
for (final AccountingEventPaymentCode paymentCode : event.calculatePaymentCodes()) {
this.sibsFile.addAssociatedPaymentCode(paymentCode);
sibsFile.addLine(paymentCode.getCode(), paymentCode.getMinAmount(), paymentCode.getMaxAmount(),
paymentCode.getStartDate(), paymentCode.getEndDate());
}
}
}
private class ExportThingy extends Thread {
final SibsOutgoingPaymentFile sibsOutgoingPaymentFile;
final StringBuilder errorsBuilder;
public ExportThingy(final SibsOutgoingPaymentFile sibsOutgoingPaymentFile, final StringBuilder errorsBuilder) {
this.sibsOutgoingPaymentFile = sibsOutgoingPaymentFile;
this.errorsBuilder = errorsBuilder;
}
@Override
@Atomic(mode = TxMode.READ)
public void run() {
txDo();
}
private void txDo() {
exportIndividualCandidacyPaymentCodes(sibsOutgoingPaymentFile, errorsBuilder);
}
}
private class ExportAnotherThingy extends Thread {
final SibsOutgoingPaymentFile sibsOutgoingPaymentFile;
final StringBuilder errorsBuilder;
public ExportAnotherThingy(final SibsOutgoingPaymentFile sibsOutgoingPaymentFile, final StringBuilder errorsBuilder) {
this.sibsOutgoingPaymentFile = sibsOutgoingPaymentFile;
this.errorsBuilder = errorsBuilder;
}
@Override
@Atomic(mode = TxMode.READ)
public void run() {
txDo();
}
private void txDo() {
exportRectoratePaymentCodes(sibsOutgoingPaymentFile, errorsBuilder);
}
}
private class CalculateStudentCandidacyPaymentCodes extends Thread {
final SibsOutgoingPaymentFile file;
final StringBuilder errorsBuilder;
CalculateStudentCandidacyPaymentCodes(final SibsOutgoingPaymentFile file, final StringBuilder errorsBuilder) {
this.file = file;
this.errorsBuilder = errorsBuilder;
}
@Override
@Atomic(mode = TxMode.READ)
public void run() {
txDo();
}
private void txDo() {
List<DgesStudentImportationProcess> processList =
DgesStudentImportationProcess.readDoneJobs(ExecutionYear.readCurrentExecutionYear());
for (DgesStudentImportationProcess process : processList) {
int i = 0;
for (StudentCandidacy studentCandidacy : process.getStudentCandidacySet()) {
i++;
for (PaymentCode paymentCode : studentCandidacy.getAvailablePaymentCodesSet()) {
try {
if (paymentCode.isCancelled()) {
continue;
}
if (paymentCode.isInvalid()) {
continue;
}
if (!paymentCode.getEndDate().isAfter(new YearMonthDay())) {
continue;
}
if (((AccountingEventPaymentCode) paymentCode).getAccountingEvent() != null) {
continue;
}
this.file.addAssociatedPaymentCode(paymentCode);
this.file.addLine(paymentCode.getCode(), paymentCode.getMinAmount(), paymentCode.getMaxAmount(),
paymentCode.getStartDate(), paymentCode.getEndDate());
} catch (Throwable e) {
appendToErrors(errorsBuilder, paymentCode.getExternalId(), e);
}
}
}
}
}
}
}