package net.sourceforge.fenixedu.domain.accounting; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import net.sourceforge.fenixedu.dataTransferObject.accounting.AccountingTransactionDetailDTO; import net.sourceforge.fenixedu.dataTransferObject.accounting.EntryDTO; import net.sourceforge.fenixedu.dataTransferObject.accounting.SibsTransactionDetailDTO; import net.sourceforge.fenixedu.domain.Employee; import net.sourceforge.fenixedu.domain.Person; import net.sourceforge.fenixedu.domain.RootDomainObject; import net.sourceforge.fenixedu.domain.User; import net.sourceforge.fenixedu.domain.accounting.events.PenaltyExemption; import net.sourceforge.fenixedu.domain.accounting.paymentCodes.AccountingEventPaymentCode; import net.sourceforge.fenixedu.domain.administrativeOffice.AdministrativeOffice; import net.sourceforge.fenixedu.domain.exceptions.DomainException; import net.sourceforge.fenixedu.domain.organizationalStructure.Party; import net.sourceforge.fenixedu.domain.person.RoleType; import net.sourceforge.fenixedu.util.Money; import org.joda.time.DateTime; import org.joda.time.LocalDate; import org.joda.time.YearMonthDay; import pt.ist.fenixWebFramework.security.accessControl.Checked; import pt.utl.ist.fenix.tools.resources.LabelFormatter; import com.linkare.commons.metainfo.Linkare; public abstract class Event extends Event_Base { protected Event() { super(); super.setRootDomainObject(RootDomainObject.getInstance()); super.setWhenOccured(new DateTime()); changeState(EventState.OPEN, new DateTime()); } protected void init(EventType eventType, Person person) { checkParameters(eventType, person); super.setEventType(eventType); super.setPerson(person); } private void checkParameters(EventType eventType, Person 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.net.sourceforge.fenixedu.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 process(final User responsibleUser, final List entryDTOs, final AccountingTransactionDetailDTO transactionDetail) { if (entryDTOs.isEmpty()) { throw new DomainException("error.accounting.Event.process.requires.entries.to.be.processed"); } checkConditionsToProcessEvent(transactionDetail); final Set result = internalProcess(responsibleUser, entryDTOs, transactionDetail); recalculateState(transactionDetail.getWhenRegistered()); return result; } public final Set process(final User responsibleUser, final AccountingEventPaymentCode paymentCode, final Money amountToPay, final SibsTransactionDetailDTO transactionDetailDTO) { checkConditionsToProcessEvent(transactionDetailDTO); final Set 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 internalProcess(User responsibleUser, AccountingEventPaymentCode paymentCode, Money amountToPay, SibsTransactionDetailDTO transactionDetail) { throw new UnsupportedOperationException("error.net.sourceforge.fenixedu.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 public List getAccountingTransactions() { throw new DomainException( "error.accounting.Event.this.method.should.not.be.used.directly.use.getNonAdjustingTransactions.method.instead"); } @Override public Set getAccountingTransactionsSet() { throw new DomainException( "error.accounting.Event.this.method.should.not.be.used.directly.use.getNonAdjustingTransactions.method.instead"); } @Override public int getAccountingTransactionsCount() { throw new DomainException( "error.accounting.Event.this.method.should.not.be.used.directly.use.getNonAdjustingTransactions.method.instead"); } @Override public Iterator getAccountingTransactionsIterator() { return getAccountingTransactionsSet().iterator(); } @Override public boolean hasAccountingTransactions(AccountingTransaction accountingTransactions) { return !getAccountingTransactionsSet().isEmpty(); } @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 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 setEmployeeResponsibleForCancel(Employee employee) { throw new DomainException("error.accounting.Event.cannot.modify.employeeResponsibleForCancel"); } @Override @Checked("RolePredicates.MANAGER_PREDICATE") public void setEventStateDate(DateTime eventStateDate) { super.setEventStateDate(eventStateDate); } protected boolean canCloseEvent(DateTime whenRegistered) { return calculateAmountToPay(whenRegistered).lessOrEqualThan(Money.ZERO); } public Set getPositiveEntries() { final Set result = new HashSet(); for (final AccountingTransaction transaction : getNonAdjustingTransactions()) { if (transaction.getToAccountEntry().getAmountWithAdjustment().isPositive()) { result.add(transaction.getToAccountEntry()); } } return result; } public Set getEntriesWithoutReceipt() { final Set result = new HashSet(); 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 getNonAdjustingTransactions() { final List result = new ArrayList(); for (final AccountingTransaction transaction : super.getAccountingTransactionsSet()) { if (!transaction.isAdjustingTransaction() && transaction.getAmountWithAdjustment().isPositive()) { result.add(transaction); } } return result; } public List getSortedNonAdjustingTransactions() { final List 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 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.net.sourceforge.fenixedu.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); } } public Money calculateAmountToPay(DateTime whenRegistered) { final Money totalAmountToPay = calculateTotalAmountToPay(whenRegistered); return totalAmountToPay != null && totalAmountToPay.isPositive() ? totalAmountToPay .subtract(getPayedAmount(whenRegistered)) : Money.ZERO; } private Money calculateTotalAmountToPay(DateTime whenRegistered) { return getPostingRule().calculateTotalAmountToPay(this, whenRegistered); } public Money getAmountToPay() { return calculateAmountToPay(new DateTime()); } public List calculateEntries() { return calculateEntries(new DateTime()); } public List calculateEntries(DateTime when) { return getPostingRule().calculateEntries(this, when); } @Checked("RolePredicates.MANAGER_PREDICATE") public void open() { changeState(EventState.OPEN, new DateTime()); super.setEmployeeResponsibleForCancel(null); super.setCancelJustification(null); } public void cancel(final Employee responsibleEmployee) { cancel(responsibleEmployee, null); } public void cancel(final Employee responsibleEmployee, final String cancelJustification) { if (isCancelled()) { return; } checkRulesToCancel(responsibleEmployee); changeState(EventState.CANCELLED, new DateTime()); super.setEmployeeResponsibleForCancel(responsibleEmployee); 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 Employee employee) { if (!employee.getPerson().hasRole(RoleType.MANAGER) && !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 internalProcess(User responsibleUser, List entryDTOs, AccountingTransactionDetailDTO transactionDetail) { return getPostingRule().process(responsibleUser, entryDTOs, this, getFromAccount(), getToAccount(), transactionDetail); } public boolean hasInstallments() { return false; } public List calculatePaymentCodes() { return !hasAnyPaymentCodes() ? createPaymentCodes() : updatePaymentCodes(); } protected List updatePaymentCodes() { return Collections.EMPTY_LIST; } protected List createPaymentCodes() { return Collections.EMPTY_LIST; } public List getNonProcessedPaymentCodes() { final List result = new ArrayList(); for (final AccountingEventPaymentCode paymentCode : super.getPaymentCodesSet()) { if (paymentCode.isNew()) { result.add(paymentCode); } } return result; } public List getCancelledPaymentCodes() { final List result = new ArrayList(); for (final AccountingEventPaymentCode paymentCode : super.getPaymentCodesSet()) { if (paymentCode.isCancelled()) { result.add(paymentCode); } } return result; } public List getAllPaymentCodes() { return Collections.unmodifiableList(super.getPaymentCodes()); } @Override public void addPaymentCodes(AccountingEventPaymentCode paymentCode) { throw new DomainException("error.net.sourceforge.fenixedu.domain.accounting.Event.cannot.add.paymentCode"); } @Linkare(author = "Paulo Zenida", comments = "Removed the throwing of the domain exception") @Checked("RolePredicates.MANAGER_PREDICATE") @Override public List getPaymentCodes() { return super.getPaymentCodes(); } @Override public Set getPaymentCodesSet() { throw new DomainException( "error.net.sourceforge.fenixedu.domain.accounting.Event.paymentCodes.cannot.be.accessed.directly"); } @Override public Iterator getPaymentCodesIterator() { return getPaymentCodesSet().iterator(); } @Override public void removePaymentCodes(AccountingEventPaymentCode paymentCode) { throw new DomainException("error.net.sourceforge.fenixedu.domain.accounting.Event.cannot.remove.paymentCode"); } public static List readNotCancelled() { final List result = new ArrayList(); for (final Event event : RootDomainObject.getInstance().getAccountingEvents()) { 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(), "enum"); 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; } @Checked("RolePredicates.MANAGER_PREDICATE") 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()); } @Checked("RolePredicates.MANAGER_OR_ACADEMIC_ADMINISTRATIVE_OFFICE_PREDICATE") public final AccountingTransaction depositAmount(final User responsibleUser, final Money amount, final AccountingTransactionDetailDTO transactionDetailDTO) { final AccountingTransaction result = getPostingRule().depositAmount(responsibleUser, this, getPerson().getAccountBy(AccountType.EXTERNAL), getToAccount(), amount, transactionDetailDTO); recalculateState(transactionDetailDTO.getWhenRegistered()); return result; } @Checked("RolePredicates.MANAGER_OR_ACADEMIC_ADMINISTRATIVE_OFFICE_PREDICATE") public final AccountingTransaction depositAmount(final User responsibleUser, final Money amount, final EntryType entryType, final AccountingTransactionDetailDTO transactionDetailDTO) { final AccountingTransaction result = getPostingRule().depositAmount(responsibleUser, this, getPerson().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(getPerson())) { result = result.add(accountingTransaction.getToAccountEntry().getAmountWithAdjustment()); } } return result; } public Set getOtherPartyEntries() { final Set result = new HashSet(); for (final AccountingTransaction transaction : getNonAdjustingTransactions()) { if (!transaction.isSourceAccountFromParty(getPerson())) { result.add(transaction.getToAccountEntry()); } } return result; } @Checked("RolePredicates.MANAGER_PREDICATE") public void rollbackCompletly() { while (!getNonAdjustingTransactions().isEmpty()) { getNonAdjustingTransactions().get(0).delete(); } changeState(EventState.OPEN, new DateTime()); for (final PaymentCode paymentCode : getExistingPaymentCodes()) { paymentCode.setState(PaymentCodeState.NEW); } } @Checked("RolePredicates.MANAGER_PREDICATE") public List getExistingPaymentCodes() { return Collections.unmodifiableList(super.getPaymentCodes()); } protected abstract Account getFromAccount(); public abstract Account getToAccount(); public abstract LabelFormatter getDescriptionForEntryType(EntryType entryType); abstract public PostingRule getPostingRule(); @Linkare(author = "Paulo Zenida", comments = "Changed the modifiers order according to the JLS suggestions") public final void delete() { checkRulesToDelete(); disconnect(); } protected void disconnect() { while (!super.getPaymentCodes().isEmpty()) { super.getPaymentCodes().get(0).delete(); } while (!getExemptions().isEmpty()) { getExemptions().get(0).delete(false); } super.setPerson(null); super.setEmployeeResponsibleForCancel(null); removeRootDomainObject(); 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 readBy(final EventType eventType) { final List result = new ArrayList(); for (final Event event : RootDomainObject.getInstance().getAccountingEvents()) { if (event.getEventType() == eventType) { result.add(event); } } return result; } public static List readWithPaymentsByPersonForCivilYear(int civilYear) { final List result = new ArrayList(); for (final Event event : readNotCancelled()) { if (event.hasPaymentsByPersonForCivilYear(civilYear)) { result.add(event); } } return result; } @Override public void addExemptions(Exemption exemption) { throw new DomainException("error.net.sourceforge.fenixedu.domain.accounting.Event.cannot.add.exemption"); } @Override public List getExemptions() { return Collections.unmodifiableList(super.getExemptions()); } @Override public Set getExemptionsSet() { return Collections.unmodifiableSet(super.getExemptionsSet()); } @Override public Iterator getExemptionsIterator() { return getExemptionsSet().iterator(); } @Override public void removeExemptions(Exemption exemption) { throw new DomainException("error.net.sourceforge.fenixedu.domain.accounting.Event.cannot.remove.exemption"); } public boolean isExemptionAppliable() { return false; } public List getPenaltyExemptions() { final List result = new ArrayList(); 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 getPenaltyExemptionsFor(Class type) { final List result = new ArrayList(); 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()); } @Checked("RolePredicates.MANAGER_PREDICATE") public void transferPaymentsAndCancel(Employee employee, Event targetEvent, String justification) { checkConditionsToTransferPaymentsAndCancel(targetEvent); for (final Entry entryToTransfer : getPositiveEntries()) { final AccountingTransactionDetailDTO transactionDetail = createAccountingTransactionDetailForTransfer(entryToTransfer .getAccountingTransaction()); targetEvent.depositAmount(employee.getPerson().getUser(), entryToTransfer.getAmountWithAdjustment(), transactionDetail); entryToTransfer.getAccountingTransaction().reimburseWithoutRules(employee.getPerson().getUser(), PaymentMode.CASH, entryToTransfer.getAmountWithAdjustment()); } cancel(employee, 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.net.sourceforge.fenixedu.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().getIdInternal() + "," + transaction.getClass().getName() + ":" + transaction.getIdInternal(); 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 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 : getDiscounts()) { result = result.add(discount.getAmount()); } return result; } public boolean isPaymentPlanChangeAllowed() { return false; } }