/**
* 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.Collections;
import java.util.Comparator;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.fenixedu.academic.domain.exceptions.DomainException;
import org.fenixedu.academic.domain.exceptions.DomainExceptionWithLabelFormatter;
import org.fenixedu.academic.domain.organizationalStructure.Party;
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;
/**
* Two-ledged accounting transaction
*
* @author naat
*
*/
public class AccountingTransaction extends AccountingTransaction_Base {
public static Comparator<AccountingTransaction> COMPARATOR_BY_WHEN_REGISTERED = new Comparator<AccountingTransaction>() {
@Override
public int compare(AccountingTransaction leftAccountingTransaction, AccountingTransaction rightAccountingTransaction) {
int comparationResult =
leftAccountingTransaction.getWhenRegistered().compareTo(rightAccountingTransaction.getWhenRegistered());
return (comparationResult == 0) ? leftAccountingTransaction.getExternalId().compareTo(
rightAccountingTransaction.getExternalId()) : comparationResult;
}
};
protected AccountingTransaction() {
super();
super.setRootDomainObject(Bennu.getInstance());
}
public AccountingTransaction(User responsibleUser, Event event, Entry debit, Entry credit,
AccountingTransactionDetail transactionDetail) {
this();
init(responsibleUser, event, debit, credit, transactionDetail);
}
private AccountingTransaction(User responsibleUser, Entry debit, Entry credit, AccountingTransactionDetail transactionDetail,
AccountingTransaction transactionToAdjust) {
this();
init(responsibleUser, transactionToAdjust.getEvent(), debit, credit, transactionDetail, transactionToAdjust);
}
protected void init(User responsibleUser, Event event, Entry debit, Entry credit,
AccountingTransactionDetail transactionDetail) {
init(responsibleUser, event, debit, credit, transactionDetail, null);
}
protected void init(User responsibleUser, Event event, Entry debit, Entry credit,
AccountingTransactionDetail transactionDetail, AccountingTransaction transactionToAdjust) {
checkParameters(event, debit, credit);
super.setEvent(event);
super.setResponsibleUser(responsibleUser);
super.addEntries(debit);
super.addEntries(credit);
super.setAdjustedTransaction(transactionToAdjust);
super.setTransactionDetail(transactionDetail);
}
private void checkParameters(Event event, Entry debit, Entry credit) {
if (event == null) {
throw new DomainException("error.accounting.accountingTransaction.event.cannot.be.null");
}
if (debit == null) {
throw new DomainException("error.accounting.accountingTransaction.debit.cannot.be.null");
}
if (credit == null) {
throw new DomainException("error.accounting.accountingTransaction.credit.cannot.be.null");
}
}
@Override
public void addEntries(Entry entries) {
throw new DomainException("error.accounting.accountingTransaction.cannot.add.entries");
}
@Override
public Set<Entry> getEntriesSet() {
return Collections.unmodifiableSet(super.getEntriesSet());
}
@Override
public void removeEntries(Entry entries) {
throw new DomainException("error.accounting.accountingTransaction.cannot.remove.entries");
}
@Override
public void setEvent(Event event) {
super.setEvent(event);
}
@Override
public void setResponsibleUser(User responsibleUser) {
throw new DomainException("error.accounting.accountingTransaction.cannot.modify.responsibleUser");
}
@Override
public void setAdjustedTransaction(AccountingTransaction adjustedTransaction) {
throw new DomainException("error.accounting.accountingTransaction.cannot.modify.adjustedTransaction");
}
@Override
public void setTransactionDetail(AccountingTransactionDetail transactionDetail) {
throw new DomainException("error.accounting.AccountingTransaction.cannot.modify.transactionDetail");
}
@Override
public void addAdjustmentTransactions(AccountingTransaction accountingTransaction) {
throw new DomainException(
"error.org.fenixedu.academic.domain.accounting.AccountingTransaction.cannot.add.accountingTransaction");
}
@Override
public Set<AccountingTransaction> getAdjustmentTransactionsSet() {
return Collections.unmodifiableSet(super.getAdjustmentTransactionsSet());
}
@Override
public void removeAdjustmentTransactions(AccountingTransaction adjustmentTransactions) {
throw new DomainException(
"error.org.fenixedu.academic.domain.accounting.AccountingTransaction.cannot.remove.accountingTransaction");
}
public LabelFormatter getDescriptionForEntryType(EntryType entryType) {
return getEvent().getDescriptionForEntryType(entryType);
}
public Account getFromAccount() {
return getEntry(false).getAccount();
}
public Account getToAccount() {
return getEntry(true).getAccount();
}
public Entry getToAccountEntry() {
return getEntry(true);
}
public Entry getFromAccountEntry() {
return getEntry(false);
}
private Entry getEntry(boolean positive) {
for (final Entry entry : getEntriesSet()) {
if (entry.isPositiveAmount() == positive) {
return entry;
}
}
throw new DomainException("error.accounting.accountingTransaction.transaction.data.is.corrupted");
}
public AccountingTransaction reimburse(User responsibleUser, PaymentMode paymentMode, Money amountToReimburse) {
return reimburse(responsibleUser, paymentMode, amountToReimburse, null);
}
public AccountingTransaction reimburse(User responsibleUser, PaymentMode paymentMode, Money amountToReimburse, String comments) {
return reimburse(responsibleUser, paymentMode, amountToReimburse, comments, true);
}
public AccountingTransaction reimburse(User responsibleUser, PaymentMode paymentMode, Money amountToReimburse,
DateTime reimburseDate, String comments) {
return reimburse(responsibleUser, paymentMode, amountToReimburse, comments, true, reimburseDate);
}
public AccountingTransaction reimburseWithoutRules(User responsibleUser, PaymentMode paymentMode, Money amountToReimburse) {
return reimburseWithoutRules(responsibleUser, paymentMode, amountToReimburse, null);
}
public AccountingTransaction reimburseWithoutRules(User responsibleUser, PaymentMode paymentMode, Money amountToReimburse,
String comments) {
return reimburse(responsibleUser, paymentMode, amountToReimburse, comments, false);
}
public void annul(final User responsibleUser, final String reason) {
if (StringUtils.isEmpty(reason)) {
throw new DomainException(
"error.org.fenixedu.academic.domain.accounting.AccountingTransaction.cannot.annul.without.reason");
}
checkRulesToAnnul();
reimburseWithoutRules(responsibleUser, getTransactionDetail().getPaymentMode(), getAmountWithAdjustment(), reason);
}
private void checkRulesToAnnul() {
if (getToAccountEntry().isAssociatedToAnyActiveReceipt()) {
throw new DomainException("error.accounting.AccountingTransaction.cannot.annul.while.associated.to.active.receipt");
}
}
private AccountingTransaction reimburse(User responsibleUser, PaymentMode paymentMode, Money amountToReimburse,
String comments, boolean checkRules) {
return reimburse(responsibleUser, paymentMode, amountToReimburse, comments, checkRules, new DateTime());
}
private AccountingTransaction reimburse(User responsibleUser, PaymentMode paymentMode, Money amountToReimburse,
String comments, boolean checkRules, DateTime reimburseDate) {
if (checkRules && !canApplyReimbursement(amountToReimburse)) {
throw new DomainException("error.accounting.AccountingTransaction.cannot.reimburse.events.that.may.open");
}
if (!getToAccountEntry().canApplyReimbursement(amountToReimburse)) {
throw new DomainExceptionWithLabelFormatter(
"error.accounting.AccountingTransaction.amount.to.reimburse.exceeds.entry.amount", getToAccountEntry()
.getDescription());
}
final AccountingTransaction transaction =
new AccountingTransaction(responsibleUser, new Entry(EntryType.ADJUSTMENT, amountToReimburse.negate(),
getToAccount()), new Entry(EntryType.ADJUSTMENT, amountToReimburse, getFromAccount()),
new AccountingTransactionDetail(reimburseDate, paymentMode, comments), this);
getEvent().recalculateState(getWhenRegistered());
return transaction;
}
public DateTime getWhenRegistered() {
return getTransactionDetail().getWhenRegistered();
}
public DateTime getWhenProcessed() {
return getTransactionDetail().getWhenProcessed();
}
public String getComments() {
return getTransactionDetail().getComments();
}
public boolean isPayed(final int civilYear) {
return getWhenRegistered().getYear() == civilYear;
}
public boolean isAdjustingTransaction() {
return getAdjustedTransaction() != null;
}
public boolean hasBeenAdjusted() {
return !getAdjustmentTransactionsSet().isEmpty();
}
public Entry getEntryFor(final Account account) {
for (final Entry accountingEntry : getEntriesSet()) {
if (accountingEntry.getAccount() == account) {
return accountingEntry;
}
}
throw new DomainException(
"error.accounting.accountingTransaction.transaction.data.is.corrupted.because.no.entry.belongs.to.account");
}
private boolean canApplyReimbursement(final Money amount) {
return getEvent().canApplyReimbursement(amount);
}
public boolean isSourceAccountFromParty(Party party) {
return getFromAccount().getParty() == party;
}
public void delete() {
super.setAdjustedTransaction(null);
for (; !getAdjustmentTransactionsSet().isEmpty(); getAdjustmentTransactionsSet().iterator().next().delete()) {
;
}
if (getTransactionDetail() != null) {
getTransactionDetail().delete();
}
for (; !getEntriesSet().isEmpty(); getEntriesSet().iterator().next().delete()) {
;
}
super.setResponsibleUser(null);
super.setEvent(null);
setRootDomainObject(null);
super.deleteDomainObject();
}
public Money getAmountWithAdjustment() {
return getToAccountEntry().getAmountWithAdjustment();
}
public boolean isInsidePeriod(final YearMonthDay startDate, final YearMonthDay endDate) {
return isInsidePeriod(startDate.toLocalDate(), endDate.toLocalDate());
}
public boolean isInsidePeriod(final LocalDate startDate, final LocalDate endDate) {
return !getWhenRegistered().toLocalDate().isBefore(startDate) && !getWhenRegistered().toLocalDate().isAfter(endDate);
}
public boolean isInstallment() {
return false;
}
public PaymentMode getPaymentMode() {
return getTransactionDetail().getPaymentMode();
}
public Money getOriginalAmount() {
return getToAccountEntry().getOriginalAmount();
}
}