/**
* 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 static org.fenixedu.academic.predicate.AccessControl.check;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.fenixedu.academic.FenixEduAcademicConfiguration;
import org.fenixedu.academic.domain.Person;
import org.fenixedu.academic.domain.administrativeOffice.AdministrativeOffice;
import org.fenixedu.academic.domain.exceptions.DomainException;
import org.fenixedu.academic.domain.organizationalStructure.Unit;
import org.fenixedu.academic.dto.accounting.CreditNoteEntryDTO;
import org.fenixedu.academic.predicate.AcademicPredicates;
import org.fenixedu.academic.util.Bundle;
import org.fenixedu.academic.util.Money;
import org.fenixedu.bennu.core.domain.Bennu;
import org.fenixedu.bennu.core.i18n.BundleUtil;
import org.joda.time.DateTime;
import org.joda.time.YearMonthDay;
public class Receipt extends Receipt_Base {
public static final String GENERIC_CONTRIBUTOR_PARTY_NUMBER = " ";
private static final Map<Integer, String> NUMBER_SERIES_BY_YEAR = new HashMap<Integer, String>();
static {
final String[] parts = FenixEduAcademicConfiguration.getConfiguration().getReceiptNumberSeriesForYears().split(",");
for (final String part : parts) {
if (!StringUtils.isEmpty(part)) {
final String[] yearAndSeries = part.split(":");
NUMBER_SERIES_BY_YEAR.put(Integer.valueOf(yearAndSeries[0]), yearAndSeries[1]);
}
}
}
public static Comparator<Receipt> COMPARATOR_BY_NUMBER = new Comparator<Receipt>() {
@Override
public int compare(Receipt leftReceipt, Receipt rightReceipt) {
int comparationResult = leftReceipt.getReceiptNumber().compareTo(rightReceipt.getReceiptNumber());
return (comparationResult == 0) ? leftReceipt.getExternalId().compareTo(rightReceipt.getExternalId()) : comparationResult;
}
};
protected Receipt() {
super();
}
static public Receipt create(Person responsible, Person person, String contributorName, String contributorNumber,
String contributorAddress, Integer year, List<Entry> entries) {
final Receipt result = new Receipt();
result.init(responsible, person, contributorName, contributorNumber, contributorAddress, year, entries,
NUMBER_SERIES_BY_YEAR.get(year));
return result;
}
private void init(Person responsible, Person person, String contributorName, String contributorNumber,
String contributorAddress, Integer year, List<Entry> entries, String numberSeries) {
if (person == null) {
throw new DomainException("error.accouting.Receipt.person.cannot.be.null");
}
if (StringUtils.isEmpty(contributorName)) {
throw new DomainException("error.accounting.Receipt.contributor.or.contributorName.must.be.not.null");
}
if (year == null) {
throw new DomainException("error.accounting.Receipt.year.cannot.be.null");
}
if (entries == null) {
throw new DomainException("error.accounting.Receipt.entries.cannot.be.null");
}
if (entries.isEmpty()) {
throw new DomainException("error.accounting.Receipt.entries.cannot.be.empty");
}
checkRulesToCreate(person, year, entries);
super.setNumber(generateReceiptNumber(year));
super.setRootDomainObject(Bennu.getInstance());
super.setYear(year);
super.setNumberSeries(numberSeries);
super.setPerson(person);
setContributorName(contributorName);
setContributorNumber(contributorNumber);
setContributorAddress(contributorAddress);
super.setWhenCreated(new DateTime());
changeState(responsible, ReceiptState.ACTIVE);
super.setReceiptDate(new YearMonthDay().getYear() != year.intValue() ? getLastDayOfYear(year) : new YearMonthDay());
for (final Entry entry : entries) {
entry.setActiveReceipt(this);
}
}
private YearMonthDay getLastDayOfYear(Integer year) {
return new YearMonthDay(year, 12, 31);
}
private void checkRulesToCreate(final Person person, final Integer year, final List<Entry> entries) {
final YearMonthDay today = new YearMonthDay();
if (year < FenixEduAcademicConfiguration.getConfiguration().getReceiptMinYearToCreate().intValue()) {
throw new DomainException("error.accounting.Receipt.invalid.receipt.year", FenixEduAcademicConfiguration
.getConfiguration().getReceiptMinYearToCreate().toString());
}
for (final Entry entry : entries) {
if (entry.getWhenRegistered().getYear() != year) {
throw new DomainException("error.accounting.Receipt.entries.must.belong.to.receipt.civil.year");
}
if (!entry.getAccountingTransaction().isSourceAccountFromParty(person)) {
throw new DomainException("error.accounting.Receipt.entries.must.belong.to.person");
}
}
}
private void changeState(final Person responsible, final ReceiptState state) {
markChange(responsible);
super.setState(state);
}
private void markChange(final Person responsible) {
super.setWhenUpdated(new DateTime());
super.setResponsible(responsible);
}
@Override
public void addEntries(Entry entries) {
throw new DomainException("error.accounting.Receipt.cannot.add.new.entries");
}
@Override
public Set<Entry> getEntriesSet() {
return Collections.unmodifiableSet(super.getEntriesSet());
}
@Override
public void removeEntries(Entry entries) {
throw new DomainException("error.accounting.Receipt.cannot.remove.entries");
}
@Override
public void setNumber(Integer number) {
throw new DomainException("error.accounting.Receipt.cannot.modify.number");
}
@Override
public void removeReceiptsVersions(ReceiptPrintVersion receiptsVersions) {
throw new DomainException("error.accounting.Receipt.cannot.remove.receiptVersions");
}
@Override
public void setYear(Integer year) {
throw new DomainException("error.accounting.Receipt.cannot.modify.year");
}
@Override
public void setWhenCreated(DateTime whenCreated) {
throw new DomainException("error.accounting.Receipt.cannot.modify.creation.date");
}
@Override
public void addCreditNotes(CreditNote creditNote) {
throw new DomainException("error.accounting.Receipt.cannot.add.creditNote");
}
@Override
public void setResponsible(Person responsible) {
throw new DomainException("error.accounting.Receipt.cannot.modify.responsible");
}
@Override
public Set<CreditNote> getCreditNotesSet() {
return Collections.unmodifiableSet(super.getCreditNotesSet());
}
@Override
public void removeCreditNotes(CreditNote creditNote) {
throw new DomainException("error.accounting.Receipt.cannot.remove.creditNote");
}
@Override
public void setState(ReceiptState state) {
throw new DomainException("error.accounting.Receipt.cannot.modify.state");
}
@Override
public void setOwnerUnit(Unit ownerUnit) {
throw new UnsupportedOperationException();
}
private Integer generateReceiptNumber(int year) {
final List<Receipt> receipts = getReceiptsFor(year);
return receipts.isEmpty() ? 1 : Collections.max(receipts, Receipt.COMPARATOR_BY_NUMBER).getReceiptNumber() + 1;
}
public static List<Receipt> getReceiptsFor(int year) {
final List<Receipt> result = new ArrayList<Receipt>();
for (final Receipt receipt : Bennu.getInstance().getReceiptsSet()) {
if (receipt.getYear().intValue() == year) {
result.add(receipt);
}
}
return result;
}
public void registerReceiptPrint(Person person) {
if (getState().isToRegisterPrint()) {
new ReceiptPrintVersion(this, person);
}
}
public ReceiptPrintVersion getMostRecentReceiptPrintVersion() {
ReceiptPrintVersion result = null;
for (final ReceiptPrintVersion receiptVersion : getReceiptsVersionsSet()) {
if (result == null || receiptVersion.getWhenCreated().isAfter(result.getWhenCreated())) {
result = receiptVersion;
}
}
return result;
}
public Money getTotalAmount() {
Money result = Money.ZERO;
for (final Entry entry : getEntriesSet()) {
result = result.add(entry.getOriginalAmount());
}
return result;
}
public boolean isFromAdministrativeOffice(AdministrativeOffice administrativeOffice) {
for (final Entry entry : getEntriesSet()) {
if (!entry.getAccountingTransaction().getEvent().isPayableOnAdministrativeOffice(administrativeOffice)) {
return false;
}
}
return true;
}
public CreditNote createCreditNote(final Person responsible, final PaymentMode paymentMode,
final List<CreditNoteEntryDTO> creditNoteEntryDTOs) {
return CreditNote.create(this, responsible, creditNoteEntryDTOs);
}
public void annul(final Person responsible) {
checkRulesToAnnul();
changeState(responsible, ReceiptState.ANNULLED);
}
private void checkRulesToAnnul() {
if (hasAnyActiveCreditNotes()) {
throw new DomainException("error.accounting.Receipt.cannot.annul.receipts.with.credit.notes");
}
}
private boolean hasAnyActiveCreditNotes() {
Collection<CreditNote> creditNotes = getCreditNotesSet();
for (CreditNote creditNote : creditNotes) {
if (!creditNote.isAnnulled()) {
return true;
}
}
return false;
}
public boolean isActive() {
return getState() == ReceiptState.ACTIVE;
}
public boolean isAnnulled() {
return getState() == ReceiptState.ANNULLED;
}
public Set<Entry> getReimbursableEntries() {
final Set<Entry> result = new HashSet<Entry>();
for (final Entry entry : getEntriesSet()) {
if (entry.isReimbursementAppliable()) {
result.add(entry);
}
}
return result;
}
public void deleteReceiptPrintVersions() {
for (; !getReceiptsVersionsSet().isEmpty(); getReceiptsVersionsSet().iterator().next().delete()) {
;
}
}
public void delete() {
DomainException.throwWhenDeleteBlocked(getDeletionBlockers());
deleteReceiptPrintVersions();
super.setResponsible(null);
super.getEntriesSet().clear();
super.setPerson(null);
setRootDomainObject(null);
super.deleteDomainObject();
}
@Override
protected void checkForDeletionBlockers(Collection<String> blockers) {
super.checkForDeletionBlockers(blockers);
if (!getCreditNotesSet().isEmpty()) {
blockers.add(BundleUtil.getString(Bundle.APPLICATION, "error.accounting.Receipt.cannot.be.deleted"));
}
}
public boolean isNumberSeriesDefined() {
return !StringUtils.isEmpty(getNumberSeries());
}
public String getNumberWithSeries() {
return !isNumberSeriesDefined() ? String.valueOf(super.getNumber()) : super.getNumber() + getNumberSeries();
}
@Override
@Deprecated
public Integer getNumber() {
throw new DomainException("error.org.fenixedu.academic.domain.accounting.Receipt.use.getNumberWithSeries.instead");
}
private Integer getReceiptNumber() {
return super.getNumber();
}
public List<CreditNote> getEmittedCreditNotes() {
final List<CreditNote> result = new ArrayList<CreditNote>();
for (final CreditNote creditNote : super.getCreditNotesSet()) {
if (creditNote.isEmitted()) {
result.add(creditNote);
}
}
return result;
}
public void edit(final Person responsible, final String contributorName, final String contributorNumber,
final String contributorAddress) {
check(this, AcademicPredicates.MANAGE_STUDENT_PAYMENTS);
markChange(responsible);
if (StringUtils.isEmpty(contributorName)) {
throw new DomainException("error.accounting.Receipt.contributor.or.contributorName.must.be.not.null");
}
setContributorName(contributorName);
setContributorNumber(contributorNumber);
setContributorAddress(contributorAddress);
}
public void changeStateAndEntries(final ReceiptState state, final Set<Entry> newEntries) {
super.setState(state);
super.getEntriesSet().clear();
for (final Entry entry : newEntries) {
entry.setActiveReceipt(this);
}
}
public boolean isSecondPrintVersion() {
return getReceiptsVersionsSet().size() >= 1;
}
static public Receipt readByYearAndNumber(final Integer year, final Integer number, final String series) {
for (final Receipt receipt : Receipt.getReceiptsFor(year)) {
if (receipt.getReceiptNumber().equals(number)) {
if (series == null && receipt.getNumberSeries() == null) {
return receipt;
} else if (series != null && receipt.getNumberSeries() != null && series.equals(receipt.getNumberSeries())) {
return receipt;
}
}
}
return null;
}
}