/**
* 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.domain.accounting;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import org.fenixedu.academic.domain.DomainObjectUtil;
import org.fenixedu.academic.domain.Person;
import org.fenixedu.academic.domain.accounting.events.PenaltyExemption;
import org.fenixedu.academic.domain.accounting.paymentCodes.AccountingEventPaymentCode;
import org.fenixedu.academic.domain.administrativeOffice.AdministrativeOffice;
import org.fenixedu.academic.domain.exceptions.DomainException;
import org.fenixedu.academic.domain.organizationalStructure.Party;
import org.fenixedu.academic.domain.organizationalStructure.Unit;
import org.fenixedu.academic.dto.accounting.AccountingTransactionDetailDTO;
import org.fenixedu.academic.dto.accounting.EntryDTO;
import org.fenixedu.academic.dto.accounting.SibsTransactionDetailDTO;
import org.fenixedu.academic.predicate.AccessControl;
import org.fenixedu.academic.util.Bundle;
import org.fenixedu.academic.util.LabelFormatter;
import org.fenixedu.academic.util.Money;
import org.fenixedu.bennu.core.domain.Bennu;
import org.fenixedu.bennu.core.domain.User;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.joda.time.YearMonthDay;
public abstract class Event extends Event_Base {
public static final Comparator<Event> COMPARATOR_BY_DATE = new Comparator<Event>() {
@Override
public int compare(final Event e1, final Event e2) {
final int i = e1.getWhenOccured().compareTo(e2.getWhenOccured());
return i == 0 ? DomainObjectUtil.COMPARATOR_BY_ID.compare(e1, e2) : i;
}
};
protected Event() {
super();
super.setRootDomainObject(Bennu.getInstance());
super.setWhenOccured(new DateTime());
super.setCreatedBy(AccessControl.getPerson() != null ? AccessControl.getPerson().getUsername() : null);
changeState(EventState.OPEN, new DateTime());
}
protected void init(EventType eventType, Person person) {
checkParameters(eventType, person);
super.setEventType(eventType);
super.setParty(person);
}
protected void init(EventType eventType, Party party) {
checkParameters(eventType, party);
super.setEventType(eventType);
super.setParty(party);
}
private void checkParameters(EventType eventType, Party person) throws DomainException {
if (eventType == null) {
throw new DomainException("error.accounting.Event.invalid.eventType");
}
if (person == null) {
throw new DomainException("error.accounting.person.cannot.be.null");
}
}
public boolean isOpen() {
return (super.getEventState() == EventState.OPEN);
}
public boolean isInDebt() {
return isOpen();
}
public boolean isClosed() {
return (super.getEventState() == EventState.CLOSED);
}
public boolean isPayed() {
return isClosed();
}
public boolean isCancelled() {
return (super.getEventState() == EventState.CANCELLED);
}
@Override
public EventState getEventState() {
throw new DomainException(
"error.org.fenixedu.academic.domain.accounting.Event.dot.not.call.this.method.directly.use.isInState.instead");
}
protected EventState getCurrentEventState() {
return super.getEventState();
}
public boolean isInState(final EventState eventState) {
return super.getEventState() == eventState;
}
public final Set<Entry> process(final User responsibleUser, final Collection<EntryDTO> entryDTOs,
final AccountingTransactionDetailDTO transactionDetail) {
if (entryDTOs.isEmpty()) {
throw new DomainException("error.accounting.Event.process.requires.entries.to.be.processed");
}
checkConditionsToProcessEvent(transactionDetail);
final Set<Entry> result = internalProcess(responsibleUser, entryDTOs, transactionDetail);
recalculateState(transactionDetail.getWhenRegistered());
return result;
}
public final Set<Entry> process(final User responsibleUser, final AccountingEventPaymentCode paymentCode,
final Money amountToPay, final SibsTransactionDetailDTO transactionDetailDTO) {
checkConditionsToProcessEvent(transactionDetailDTO);
final Set<Entry> result = internalProcess(responsibleUser, paymentCode, amountToPay, transactionDetailDTO);
recalculateState(transactionDetailDTO.getWhenRegistered());
return result;
}
private void checkConditionsToProcessEvent(final AccountingTransactionDetailDTO transactionDetail) {
if (isClosed() && !isSibsTransaction(transactionDetail)) {
throw new DomainException("error.accounting.Event.is.already.closed");
}
}
private boolean isSibsTransaction(final AccountingTransactionDetailDTO transactionDetail) {
return transactionDetail instanceof SibsTransactionDetailDTO;
}
protected Set<Entry> internalProcess(User responsibleUser, AccountingEventPaymentCode paymentCode, Money amountToPay,
SibsTransactionDetailDTO transactionDetail) {
throw new UnsupportedOperationException("error.org.fenixedu.academic.domain.accounting.Event.operation.not.supported");
}
protected void closeEvent() {
changeState(EventState.CLOSED, hasEventCloseDate() ? getEventCloseDate() : new DateTime());
}
public AccountingTransaction getLastNonAdjustingAccountingTransaction() {
if (hasAnyNonAdjustingAccountingTransactions()) {
return Collections.max(getNonAdjustingTransactions(), AccountingTransaction.COMPARATOR_BY_WHEN_REGISTERED);
}
return null;
}
@Override
public void addAccountingTransactions(AccountingTransaction accountingTransactions) {
throw new DomainException("error.accounting.Event.cannot.add.accountingTransactions");
}
@Override
@Deprecated
public Set<AccountingTransaction> getAccountingTransactionsSet() {
// Not really deprecated, but be advised, this method should be returning the code below, but framework says no.
// Can`t throw DomainException because it is called from the Event_Base.class. Should not be called from anywhere else.
// throw new DomainException(
// "error.accounting.Event.this.method.should.not.be.used.directly.use.getNonAdjustingTransactions.method.instead");
return super.getAccountingTransactionsSet();
}
@Override
public void removeAccountingTransactions(AccountingTransaction accountingTransactions) {
throw new DomainException("error.accounting.Event.cannot.remove.accountingTransactions");
}
@Override
public void setEventType(EventType eventType) {
throw new DomainException("error.accounting.Event.cannot.modify.eventType");
}
@Override
public void setWhenOccured(DateTime whenOccured) {
throw new DomainException("error.accounting.Event.cannot.modify.occuredDateTime");
}
@Override
public void setCreatedBy(String createdBy) {
throw new DomainException("error.accounting.Event.cannot.modify.createdBy");
}
public void setPerson(Person person) {
throw new DomainException("error.accounting.Event.cannot.modify.person");
}
@Override
public void setEventState(EventState eventState) {
throw new DomainException("error.accounting.Event.cannot.modify.eventState");
}
@Override
public void setResponsibleForCancel(Person responsible) {
throw new DomainException("error.accounting.Event.cannot.modify.employeeResponsibleForCancel");
}
protected boolean canCloseEvent(DateTime whenRegistered) {
return calculateAmountToPay(whenRegistered).lessOrEqualThan(Money.ZERO);
}
public Set<Entry> getPositiveEntries() {
final Set<Entry> result = new HashSet<Entry>();
for (final AccountingTransaction transaction : getNonAdjustingTransactions()) {
if (transaction.getToAccountEntry().getAmountWithAdjustment().isPositive()) {
result.add(transaction.getToAccountEntry());
}
}
return result;
}
public Set<Entry> getEntriesWithoutReceipt() {
final Set<Entry> result = new HashSet<Entry>();
for (final AccountingTransaction transaction : getNonAdjustingTransactions()) {
if (!transaction.isSourceAccountFromParty(getPerson())) {
continue;
}
final Entry entry = transaction.getToAccountEntry();
if (!entry.isAssociatedToAnyActiveReceipt() && entry.isAmountWithAdjustmentPositive()) {
result.add(entry);
}
}
return result;
}
public List<AccountingTransaction> getNonAdjustingTransactions() {
final List<AccountingTransaction> result = new ArrayList<AccountingTransaction>();
for (final AccountingTransaction transaction : super.getAccountingTransactionsSet()) {
if (!transaction.isAdjustingTransaction() && transaction.getAmountWithAdjustment().isPositive()) {
result.add(transaction);
}
}
return result;
}
public List<AccountingTransaction> getAllAdjustedAccountingTransactions() {
final List<AccountingTransaction> result = new ArrayList<AccountingTransaction>();
for (final AccountingTransaction transaction : super.getAccountingTransactionsSet()) {
if (transaction.isAdjustingTransaction()) {
result.add(transaction);
}
}
return result;
}
public List<AccountingTransaction> getAdjustedTransactions() {
final List<AccountingTransaction> result = new ArrayList<AccountingTransaction>();
for (final AccountingTransaction transaction : super.getAccountingTransactionsSet()) {
if (!transaction.isAdjustingTransaction()) {
result.add(transaction);
}
}
return result;
}
public List<AccountingTransaction> getSortedNonAdjustingTransactions() {
final List<AccountingTransaction> result = getNonAdjustingTransactions();
Collections.sort(result, AccountingTransaction.COMPARATOR_BY_WHEN_REGISTERED);
return result;
}
public boolean hasNonAdjustingAccountingTransactions(final AccountingTransaction accountingTransactions) {
return getNonAdjustingTransactions().contains(accountingTransactions);
}
public boolean hasAnyNonAdjustingAccountingTransactions() {
return !getNonAdjustingTransactions().isEmpty();
}
public boolean hasAnyPayments() {
return hasAnyNonAdjustingAccountingTransactions();
}
public Money getPayedAmount() {
return getPayedAmount(null);
}
public Money getPayedAmountFor(EntryType entryType) {
if (isCancelled()) {
throw new DomainException("error.accounting.Event.cannot.calculatePayedAmount.on.invalid.events");
}
Money payedAmount = Money.ZERO;
for (final AccountingTransaction transaction : getNonAdjustingTransactions()) {
if (transaction.getToAccountEntry().getEntryType().equals(entryType)) {
payedAmount = payedAmount.add(transaction.getToAccountEntry().getAmountWithAdjustment());
}
}
return payedAmount;
}
public Money getPayedAmount(final DateTime until) {
if (isCancelled()) {
throw new DomainException("error.accounting.Event.cannot.calculatePayedAmount.on.invalid.events");
}
Money payedAmount = Money.ZERO;
for (final AccountingTransaction transaction : getNonAdjustingTransactions()) {
if (until == null || !transaction.getWhenRegistered().isAfter(until)) {
payedAmount = payedAmount.add(transaction.getToAccountEntry().getAmountWithAdjustment());
}
}
return payedAmount;
}
public Money getPayedAmountBetween(final DateTime startDate, final DateTime endDate) {
if (isCancelled()) {
throw new DomainException("error.accounting.Event.cannot.calculatePayedAmountBetween.on.invalid.events");
}
Money payedAmount = Money.ZERO;
for (final AccountingTransaction transaction : getNonAdjustingTransactions()) {
if (!transaction.getWhenRegistered().isBefore(startDate) && !transaction.getWhenRegistered().isAfter(endDate)) {
payedAmount = payedAmount.add(transaction.getToAccountEntry().getAmountWithAdjustment());
}
}
return payedAmount;
}
private Money getPayedAmountUntil(final int civilYear) {
if (isCancelled()) {
throw new DomainException("error.accounting.Event.cannot.calculatePayedAmountUntil.on.invalid.events");
}
Money result = Money.ZERO;
for (final AccountingTransaction transaction : getNonAdjustingTransactions()) {
if (transaction.getWhenRegistered().getYear() <= civilYear) {
result = result.add(transaction.getToAccountEntry().getAmountWithAdjustment());
}
}
return result;
}
public Money getPayedAmountFor(final int civilYear) {
if (isCancelled()) {
throw new DomainException("error.accounting.Event.cannot.calculatePayedAmount.on.invalid.events");
}
Money amountForCivilYear = Money.ZERO;
for (final AccountingTransaction accountingTransaction : getNonAdjustingTransactions()) {
if (accountingTransaction.isPayed(civilYear)) {
amountForCivilYear = amountForCivilYear.add(accountingTransaction.getToAccountEntry().getAmountWithAdjustment());
}
}
return amountForCivilYear;
}
public Money getMaxDeductableAmountForLegalTaxes(final int civilYear) {
if (isCancelled()) {
throw new DomainException(
"error.accounting.Event.cannot.calculate.max.deductable.amount.for.legal.taxes.on.invalid.events");
}
if (isOpen() || !hasEventCloseDate()) {
return calculatePayedAmountByPersonFor(civilYear);
}
final Money maxAmountForCivilYear =
calculateTotalAmountToPay(getEventCloseDate()).subtract(getPayedAmountUntil(civilYear - 1)).subtract(
calculatePayedAmountByOtherPartiesFor(civilYear));
if (maxAmountForCivilYear.isPositive()) {
final Money payedAmoutForPersonOnCivilYear = calculatePayedAmountByPersonFor(civilYear);
return payedAmoutForPersonOnCivilYear.lessOrEqualThan(maxAmountForCivilYear) ? payedAmoutForPersonOnCivilYear : maxAmountForCivilYear;
}
return Money.ZERO;
}
private Money calculatePayedAmountByPersonFor(final int civilYear) {
Money result = Money.ZERO;
for (final AccountingTransaction transaction : getNonAdjustingTransactions()) {
if (transaction.isPayed(civilYear) && transaction.isSourceAccountFromParty(getPerson())) {
result = result.add(transaction.getToAccountEntry().getAmountWithAdjustment());
}
}
return result;
}
public boolean hasPaymentsByPersonForCivilYear(final int civilYear) {
for (final AccountingTransaction transaction : getNonAdjustingTransactions()) {
if (transaction.isSourceAccountFromParty(getPerson()) && transaction.isPayed(civilYear)) {
return true;
}
}
return false;
}
private Money calculatePayedAmountByOtherPartiesFor(final int civilYear) {
Money result = Money.ZERO;
for (final AccountingTransaction transaction : getNonAdjustingTransactions()) {
if (transaction.isPayed(civilYear) && !transaction.isSourceAccountFromParty(getPerson())) {
result = result.add(transaction.getToAccountEntry().getAmountWithAdjustment());
}
}
return result;
}
public boolean hasPaymentsForCivilYear(final int civilYear) {
for (final AccountingTransaction accountingTransaction : getNonAdjustingTransactions()) {
if (accountingTransaction.isPayed(civilYear)) {
return true;
}
}
return false;
}
public final void recalculateState(final DateTime whenRegistered) {
if (isCancelled()) {
throw new DomainException(
"error.org.fenixedu.academic.domain.accounting.Event.cannot.recalculate.state.on.cancelled.events");
}
internalRecalculateState(whenRegistered);
}
protected void internalRecalculateState(final DateTime whenRegistered) {
final DateTime previousStateDate = getEventStateDate();
final EventState previousState = super.getEventState();
if (isOpen()) {
if (canCloseEvent(whenRegistered)) {
closeNonProcessedCodes();
closeEvent();
}
} else {
if (!canCloseEvent(hasEventCloseDate() ? getEventCloseDate() : whenRegistered)) {
changeState(EventState.OPEN, new DateTime());
reopenCancelledCodes();
}
}
// state does not change so keep previous date
if (previousState == super.getEventState()) {
super.setEventStateDate(previousStateDate);
}
}
protected void reopenCancelledCodes() {
for (final AccountingEventPaymentCode paymentCode : getCancelledPaymentCodes()) {
paymentCode.setState(PaymentCodeState.NEW);
}
}
protected void closeNonProcessedCodes() {
for (final AccountingEventPaymentCode paymentCode : getNonProcessedPaymentCodes()) {
paymentCode.setState(PaymentCodeState.CANCELLED);
}
}
/**
* Returns the total amount less the amount already paid. In other others
* returns the debt due to this event (if positive)
*
* @param whenRegistered
* @return
*/
public Money calculateAmountToPay(DateTime whenRegistered) {
final Money totalAmountToPay = calculateTotalAmountToPay(whenRegistered);
if (totalAmountToPay == null) {
return Money.ZERO;
}
final Money remainingAmount = totalAmountToPay.subtract(getPayedAmount(whenRegistered));
return remainingAmount.isPositive() ? remainingAmount : Money.ZERO;
}
private Money calculateTotalAmountToPay(DateTime whenRegistered) {
return getPostingRule().calculateTotalAmountToPay(this, whenRegistered);
}
public Money getAmountToPay() {
return calculateAmountToPay(new DateTime());
}
public Money getTotalAmountToPay(final DateTime whenRegistered) {
final Money totalAmountToPay = calculateTotalAmountToPay(whenRegistered);
return totalAmountToPay;
}
public Money getTotalAmountToPay() {
return getTotalAmountToPay(new DateTime());
}
public Money getOriginalAmountToPay() {
return getTotalAmountToPay(getWhenOccured().plusSeconds(1));
}
public List<EntryDTO> calculateEntries() {
return calculateEntries(new DateTime());
}
public List<EntryDTO> calculateEntries(DateTime when) {
return getPostingRule().calculateEntries(this, when);
}
public void open() {
changeState(EventState.OPEN, new DateTime());
super.setResponsibleForCancel(null);
super.setCancelJustification(null);
}
public void cancel(final Person responsible) {
cancel(responsible, null);
}
public void cancel(final Person responsible, final String cancelJustification) {
if (isCancelled()) {
return;
}
checkRulesToCancel(responsible);
changeState(EventState.CANCELLED, new DateTime());
super.setResponsibleForCancel(responsible);
super.setCancelJustification(cancelJustification);
closeNonProcessedCodes();
}
public void cancel(final String cancelJustification) {
if (isCancelled()) {
return;
}
if (getPayedAmount().isPositive()) {
throw new DomainException("error.accounting.Event.cannot.cancel.events.with.payed.amount.greater.than.zero");
}
changeState(EventState.CANCELLED, new DateTime());
super.setCancelJustification(cancelJustification);
closeNonProcessedCodes();
}
private void checkRulesToCancel(final Person responsible) {
if (!isOpen()) {
throw new DomainException("error.accounting.Event.only.open.events.can.be.cancelled");
}
if (getPayedAmount().isPositive()) {
throw new DomainException("error.accounting.Event.cannot.cancel.events.with.payed.amount.greater.than.zero");
}
}
protected Set<Entry> internalProcess(User responsibleUser, Collection<EntryDTO> entryDTOs,
AccountingTransactionDetailDTO transactionDetail) {
return getPostingRule().process(responsibleUser, entryDTOs, this, getFromAccount(), getToAccount(), transactionDetail);
}
public boolean hasInstallments() {
return false;
}
public List<AccountingEventPaymentCode> calculatePaymentCodes() {
return getAllPaymentCodes().isEmpty() ? createPaymentCodes() : updatePaymentCodes();
}
protected List<AccountingEventPaymentCode> updatePaymentCodes() {
return Collections.EMPTY_LIST;
}
protected List<AccountingEventPaymentCode> createPaymentCodes() {
return Collections.EMPTY_LIST;
}
public List<AccountingEventPaymentCode> getNonProcessedPaymentCodes() {
final List<AccountingEventPaymentCode> result = new ArrayList<AccountingEventPaymentCode>();
for (final AccountingEventPaymentCode paymentCode : super.getPaymentCodesSet()) {
if (paymentCode.isNew()) {
result.add(paymentCode);
}
}
return result;
}
public boolean hasNonProcessedPaymentCodes() {
for (final AccountingEventPaymentCode paymentCode : super.getPaymentCodesSet()) {
if (paymentCode.isNew()) {
return true;
}
}
return false;
}
public List<AccountingEventPaymentCode> getCancelledPaymentCodes() {
final List<AccountingEventPaymentCode> result = new ArrayList<AccountingEventPaymentCode>();
for (final AccountingEventPaymentCode paymentCode : super.getPaymentCodesSet()) {
if (paymentCode.isCancelled()) {
result.add(paymentCode);
}
}
return result;
}
public Set<AccountingEventPaymentCode> getAllPaymentCodes() {
return Collections.unmodifiableSet(super.getPaymentCodesSet());
}
@Override
public void addPaymentCodes(AccountingEventPaymentCode paymentCode) {
throw new DomainException("error.org.fenixedu.academic.domain.accounting.Event.cannot.add.paymentCode");
}
@Override
@Deprecated
public Set<AccountingEventPaymentCode> getPaymentCodesSet() {
// Not really deprecated, but be advised, this method should be returning the code below, but framework says no.
// Can`t throw DomainException because it is called from the Event_Base.class. Should not be called from anywhere else.
// throw new DomainException(
// "error.org.fenixedu.academic.domain.accounting.Event.paymentCodes.cannot.be.accessed.directly");
return super.getPaymentCodesSet();
}
@Override
public void removePaymentCodes(AccountingEventPaymentCode paymentCode) {
throw new DomainException("error.org.fenixedu.academic.domain.accounting.Event.cannot.remove.paymentCode");
}
public static List<Event> readNotCancelled() {
final List<Event> result = new ArrayList<Event>();
for (final Event event : Bennu.getInstance().getAccountingEventsSet()) {
if (!event.isCancelled()) {
result.add(event);
}
}
return result;
}
public PaymentCodeState getPaymentCodeStateFor(final PaymentMode paymentMode) {
return (paymentMode == PaymentMode.ATM) ? PaymentCodeState.PROCESSED : PaymentCodeState.CANCELLED;
}
public LabelFormatter getDescription() {
final LabelFormatter result = new LabelFormatter();
result.appendLabel(getEventType().getQualifiedName(), Bundle.ENUMERATION);
return result;
}
protected YearMonthDay calculateNextEndDate(final YearMonthDay yearMonthDay) {
final YearMonthDay nextMonth = yearMonthDay.plusMonths(1);
return new YearMonthDay(nextMonth.getYear(), nextMonth.getMonthOfYear(), 1).minusDays(1);
}
public Money getReimbursableAmount() {
if (!isClosed() || !hasEventCloseDate()) {
return Money.ZERO;
}
final Money extraPayedAmount = getPayedAmount().subtract(calculateTotalAmountToPay(getEventCloseDate()));
if (extraPayedAmount.isPositive()) {
final Money amountPayedByPerson = calculatePayedAmountByPerson();
return amountPayedByPerson.lessOrEqualThan(extraPayedAmount) ? amountPayedByPerson : extraPayedAmount;
}
return Money.ZERO;
}
protected boolean hasEventCloseDate() {
return getEventCloseDate() != null;
}
protected DateTime getEventCloseDate() {
for (final AccountingTransaction transaction : getSortedNonAdjustingTransactions()) {
if (canCloseEvent(transaction.getWhenRegistered())) {
return transaction.getWhenRegistered();
}
}
return null;
}
private Money calculatePayedAmountByPerson() {
Money result = Money.ZERO;
for (final AccountingTransaction transaction : getNonAdjustingTransactions()) {
if (transaction.isSourceAccountFromParty(getPerson())) {
result = result.add(transaction.getToAccountEntry().getAmountWithAdjustment());
}
}
return result;
}
public final void forceChangeState(EventState state, DateTime when) {
changeState(state, when);
}
protected void changeState(EventState state, DateTime when) {
super.setEventState(state);
super.setEventStateDate(when);
}
public boolean isOtherPartiesPaymentsSupported() {
return false;
}
public final void addOtherPartyAmount(User responsibleUser, Party party, Money amount,
AccountingTransactionDetailDTO transactionDetailDTO) {
getPostingRule().addOtherPartyAmount(responsibleUser, this, party.getAccountBy(AccountType.EXTERNAL), getToAccount(),
amount, transactionDetailDTO);
recalculateState(transactionDetailDTO.getWhenRegistered());
}
public final AccountingTransaction depositAmount(final User responsibleUser, final Money amount,
final AccountingTransactionDetailDTO transactionDetailDTO) {
final AccountingTransaction result =
getPostingRule().depositAmount(responsibleUser, this, getParty().getAccountBy(AccountType.EXTERNAL),
getToAccount(), amount, transactionDetailDTO);
recalculateState(transactionDetailDTO.getWhenRegistered());
return result;
}
public final AccountingTransaction depositAmount(final User responsibleUser, final Money amount, final EntryType entryType,
final AccountingTransactionDetailDTO transactionDetailDTO) {
final AccountingTransaction result =
getPostingRule().depositAmount(responsibleUser, this, getParty().getAccountBy(AccountType.EXTERNAL),
getToAccount(), amount, entryType, transactionDetailDTO);
recalculateState(transactionDetailDTO.getWhenRegistered());
return result;
}
public Money calculateOtherPartiesPayedAmount() {
Money result = Money.ZERO;
for (final AccountingTransaction accountingTransaction : getNonAdjustingTransactions()) {
if (!accountingTransaction.isSourceAccountFromParty(getParty())) {
result = result.add(accountingTransaction.getToAccountEntry().getAmountWithAdjustment());
}
}
return result;
}
public Set<Entry> getOtherPartyEntries() {
final Set<Entry> result = new HashSet<Entry>();
for (final AccountingTransaction transaction : getNonAdjustingTransactions()) {
if (!transaction.isSourceAccountFromParty(getPerson())) {
result.add(transaction.getToAccountEntry());
}
}
return result;
}
public void rollbackCompletly() {
while (!getNonAdjustingTransactions().isEmpty()) {
getNonAdjustingTransactions().iterator().next().delete();
}
changeState(EventState.OPEN, new DateTime());
for (final PaymentCode paymentCode : getExistingPaymentCodes()) {
paymentCode.setState(PaymentCodeState.NEW);
}
}
public Set<AccountingEventPaymentCode> getExistingPaymentCodes() {
return Collections.unmodifiableSet(super.getPaymentCodesSet());
}
protected abstract Account getFromAccount();
public abstract Account getToAccount();
public abstract LabelFormatter getDescriptionForEntryType(EntryType entryType);
abstract public PostingRule getPostingRule();
final public void delete() {
checkRulesToDelete();
disconnect();
}
protected void disconnect() {
while (!super.getPaymentCodesSet().isEmpty()) {
super.getPaymentCodesSet().iterator().next().delete();
}
while (!getExemptionsSet().isEmpty()) {
getExemptionsSet().iterator().next().delete(false);
}
super.setParty(null);
super.setResponsibleForCancel(null);
setRootDomainObject(null);
deleteDomainObject();
}
protected void checkRulesToDelete() {
if (isClosed() || !getNonAdjustingTransactions().isEmpty()) {
throw new DomainException(
"error.accounting.Event.cannot.delete.because.event.is.already.closed.or.has.transactions.associated");
}
}
public static List<Event> readBy(final EventType eventType) {
final List<Event> result = new ArrayList<Event>();
for (final Event event : Bennu.getInstance().getAccountingEventsSet()) {
if (event.getEventType() == eventType) {
result.add(event);
}
}
return result;
}
public static List<Event> readWithPaymentsByPersonForCivilYear(int civilYear) {
final List<Event> result = new ArrayList<Event>();
for (final Event event : readNotCancelled()) {
if (event.hasPaymentsByPersonForCivilYear(civilYear)) {
result.add(event);
}
}
return result;
}
@Override
public void addExemptions(Exemption exemption) {
throw new DomainException("error.org.fenixedu.academic.domain.accounting.Event.cannot.add.exemption");
}
@Override
public Set<Exemption> getExemptionsSet() {
return Collections.unmodifiableSet(super.getExemptionsSet());
}
@Override
public void removeExemptions(Exemption exemption) {
throw new DomainException("error.org.fenixedu.academic.domain.accounting.Event.cannot.remove.exemption");
}
public boolean isExemptionAppliable() {
return false;
}
public List<PenaltyExemption> getPenaltyExemptions() {
final List<PenaltyExemption> result = new ArrayList<PenaltyExemption>();
for (final Exemption exemption : getExemptionsSet()) {
if (exemption instanceof PenaltyExemption) {
result.add((PenaltyExemption) exemption);
}
}
return result;
}
public boolean hasAnyPenaltyExemptionsFor(Class type) {
for (final Exemption exemption : getExemptionsSet()) {
if (exemption.getClass().equals(type)) {
return true;
}
}
return false;
}
public List<PenaltyExemption> getPenaltyExemptionsFor(Class type) {
final List<PenaltyExemption> result = new ArrayList<PenaltyExemption>();
for (final Exemption exemption : getExemptionsSet()) {
if (exemption.getClass().equals(type)) {
result.add((PenaltyExemption) exemption);
}
}
return result;
}
public DateTime getLastPaymentDate() {
final AccountingTransaction transaction = getLastNonAdjustingAccountingTransaction();
return (transaction != null) ? transaction.getWhenRegistered() : null;
}
public boolean isLetterSent() {
return getWhenSentLetter() != null;
}
public void markLetterSent() {
setWhenSentLetter(new LocalDate());
}
public void transferPaymentsAndCancel(Person responsible, Event targetEvent, String justification) {
checkConditionsToTransferPaymentsAndCancel(targetEvent);
for (final Entry entryToTransfer : getPositiveEntries()) {
final AccountingTransactionDetailDTO transactionDetail =
createAccountingTransactionDetailForTransfer(entryToTransfer.getAccountingTransaction());
targetEvent.depositAmount(responsible.getUser(), entryToTransfer.getAmountWithAdjustment(), transactionDetail);
entryToTransfer.getAccountingTransaction().reimburseWithoutRules(responsible.getUser(), PaymentMode.CASH,
entryToTransfer.getAmountWithAdjustment());
}
cancel(responsible, justification);
}
protected void checkConditionsToTransferPaymentsAndCancel(Event targetEvent) {
if (getEventType() != targetEvent.getEventType()) {
throw new DomainException("error.accounting.Event.events.must.be.compatible");
}
if (isCancelled()) {
throw new DomainException("error.accounting.Event.cannot.transfer.payments.from.cancelled.events");
}
if (this == targetEvent) {
throw new DomainException(
"error.org.fenixedu.academic.domain.accounting.Event.target.event.must.be.different.from.source");
}
}
private AccountingTransactionDetailDTO createAccountingTransactionDetailForTransfer(final AccountingTransaction transaction) {
final String comments =
transaction.getEvent().getClass().getName() + ":" + transaction.getEvent().getExternalId() + ","
+ transaction.getClass().getName() + ":" + transaction.getExternalId();
return new AccountingTransactionDetailDTO(transaction.getTransactionDetail().getWhenRegistered(), PaymentMode.CASH,
comments);
}
public boolean isNotCancelled() {
return !isCancelled();
}
public boolean isAnnual() {
return false;
}
public boolean canApplyReimbursement(final Money amount) {
return getReimbursableAmount().greaterOrEqualThan(amount);
}
public boolean isPayableOnAdministrativeOffice(AdministrativeOffice administrativeOffice) {
return false;
}
public Set<EntryType> getPossibleEntryTypesForDeposit() {
return Collections.EMPTY_SET;
}
public boolean isDepositSupported() {
return !isCancelled() && !getPossibleEntryTypesForDeposit().isEmpty();
}
public void addDiscount(final Person responsible, final Money amount) {
addDiscounts(new Discount(responsible, amount));
}
public Money getTotalDiscount() {
Money result = Money.ZERO;
for (final Discount discount : getDiscountsSet()) {
result = result.add(discount.getAmount());
}
return result;
}
public boolean isPaymentPlanChangeAllowed() {
return false;
}
public boolean isGratuity() {
return false;
}
public boolean isAcademicServiceRequestEvent() {
return false;
}
public boolean isIndividualCandidacyEvent() {
return false;
}
public boolean isResidenceEvent() {
return false;
}
public boolean isPhdEvent() {
return false;
}
public boolean isDfaRegistrationEvent() {
return false;
}
public boolean isEnrolmentOutOfPeriod() {
return false;
}
public boolean isSpecializationDegreeRegistrationEvent() {
return false;
}
public boolean isFctScholarshipPhdGratuityContribuitionEvent() {
return false;
}
public boolean isTransferable() { return false; }
public abstract Unit getOwnerUnit();
public SortedSet<AccountingTransaction> getSortedTransactionsForPresentation() {
final SortedSet<AccountingTransaction> result =
new TreeSet<AccountingTransaction>(AccountingTransaction.COMPARATOR_BY_WHEN_REGISTERED);
result.addAll(getAdjustedTransactions());
return result;
}
public Person getPerson() {
return (Person) getParty();
}
}