/**
* 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.candidacyProcess;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Formatter;
import java.util.List;
import java.util.Set;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
import org.apache.commons.lang.StringUtils;
import org.fenixedu.academic.domain.Degree;
import org.fenixedu.academic.domain.DegreeCurricularPlan;
import org.fenixedu.academic.domain.EntryPhase;
import org.fenixedu.academic.domain.ExecutionDegree;
import org.fenixedu.academic.domain.ExecutionInterval;
import org.fenixedu.academic.domain.ExecutionYear;
import org.fenixedu.academic.domain.Person;
import org.fenixedu.academic.domain.candidacy.DegreeCandidacy;
import org.fenixedu.academic.domain.candidacy.IMDCandidacy;
import org.fenixedu.academic.domain.candidacy.IngressionType;
import org.fenixedu.academic.domain.candidacy.MDCandidacy;
import org.fenixedu.academic.domain.candidacy.StudentCandidacy;
import org.fenixedu.academic.domain.degreeStructure.CycleType;
import org.fenixedu.academic.domain.exceptions.DomainException;
import org.fenixedu.academic.domain.student.PersonalIngressionData;
import org.fenixedu.academic.domain.student.PrecedentDegreeInformation;
import org.fenixedu.academic.domain.student.Registration;
import org.fenixedu.academic.domain.student.Student;
import org.fenixedu.academic.dto.person.ChoosePersonBean;
import org.fenixedu.academic.dto.person.PersonBean;
import org.fenixedu.academic.util.Bundle;
import org.fenixedu.bennu.core.domain.Bennu;
import org.fenixedu.bennu.core.i18n.BundleUtil;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.joda.time.YearMonthDay;
abstract public class IndividualCandidacy extends IndividualCandidacy_Base {
protected IndividualCandidacy() {
super();
super.setWhenCreated(new DateTime());
setRootDomainObject(Bennu.getInstance());
}
protected Person init(final IndividualCandidacyProcessBean bean, final IndividualCandidacyProcess process) {
/*
* 31/03/2009 - Now the person may be created inside init() method
*
* 06/04/2009 - All subclasses share the code below. So the
* checkParameters() is now abstract
*/
/*
* 08/05/2009 - Now all candidacies are external (even made in academic
* administrative office)
*/
/*
* 06/07/2009 - Due to payments the applications will be created with an
* associated person. This person might be created or associated with an
* existing.
*/
Person person = null;
if (bean.getInternalPersonCandidacy().booleanValue()) {
person = bean.getOrCreatePersonFromBean();
}
checkParameters(person, process, bean);
bean.getPersonBean().setPerson(person);
IndividualCandidacyPersonalDetails.createDetails(this, bean);
setCandidacyProcess(process);
setCandidacyDate(bean.getCandidacyDate());
setState(IndividualCandidacyState.STAND_BY);
editObservations(bean);
setUtlStudent(bean.getUtlStudent());
createPrecedentDegreeInformation(bean);
return person;
}
/**
* 06/04/2009 All subclasses of IndividualCandidacy call a checkParameters()
* in their constructor. The arguments of checkParameters varies from
* subclass to subclass but they come from Person,
* IndividualCandidacyProcess and IndividualCandidacyProcessBean
*
* @param person
* @param process
* @param bean
*/
protected abstract void checkParameters(final Person person, final IndividualCandidacyProcess process,
final IndividualCandidacyProcessBean bean);
protected void checkParameters(final Person person, final IndividualCandidacyProcess process, final LocalDate candidacyDate) {
/*
* 31/03/2009 - The candidacy will not be associated with a Person if it
* is submited externally (not in administrative office)
*
*
* if (person == null) { throw new
* DomainException("error.IndividualCandidacy.invalid.person"); }
*/
if (process == null) {
throw new DomainException("error.IndividualCandidacy.invalid.process");
}
if (candidacyDate == null || !process.hasOpenCandidacyPeriod(candidacyDate.toDateTimeAtCurrentTime())) {
throw new DomainException("error.IndividualCandidacy.invalid.candidacyDate", process.getCandidacyStart().toString(
"dd/MM/yyyy"), process.getCandidacyEnd().toString("dd/MM/yyyy"));
}
}
@Override
public void setWhenCreated(DateTime whenCreated) {
throw new DomainException("error.IndividualCandidacy.cannot.modify.when.created");
}
public boolean hasAnyPayment() {
return getEvent() != null && getEvent().hasAnyPayments();
}
public void editPersonalCandidacyInformation(final PersonBean personBean) {
getPersonalDetails().edit(personBean);
}
public void editPersonalCandidacyInformationPublic(final PersonBean personBean) {
getPersonalDetails().editPublic(personBean);
}
public void cancel(final Person person) {
checkRulesToCancel();
setState(IndividualCandidacyState.CANCELLED);
setResponsible(person.getUsername());
if (getEvent() != null) {
getEvent().cancel("IndividualCandidacy.canceled");
}
}
public void reject(final Person person) {
setState(IndividualCandidacyState.REJECTED);
setResponsible(person.getUsername());
}
public void revertToStandBy(final Person person) {
setState(IndividualCandidacyState.STAND_BY);
setResponsible(person.getUsername());
}
protected void checkRulesToCancel() {
if (getEvent() != null && hasAnyPayment()) {
throw new DomainException("error.IndividualCandidacy.cannot.cancel.candidacy.with.payments");
}
}
public Person getResponsiblePerson() {
return Person.readPersonByUsername(getResponsible());
}
public boolean isInStandBy() {
return getState() == IndividualCandidacyState.STAND_BY;
}
public boolean isAccepted() {
return getState() == IndividualCandidacyState.ACCEPTED;
}
public boolean isNotAccepted() {
return getState() == IndividualCandidacyState.NOT_ACCEPTED;
}
public boolean isCancelled() {
return getState() == IndividualCandidacyState.CANCELLED;
}
public boolean isRejected() {
return getState() == IndividualCandidacyState.REJECTED;
}
public boolean isDebtPayed() {
return getEvent() == null || (getEvent() != null && getEvent().isClosed());
}
public boolean isFor(final ExecutionInterval executionInterval) {
return getCandidacyProcess() != null && getCandidacyProcess().isFor(executionInterval);
}
protected boolean isCandidacyResultStateValid(final IndividualCandidacyState state) {
return state == IndividualCandidacyState.ACCEPTED || state == IndividualCandidacyState.REJECTED;
}
protected void createPrecedentDegreeInformation(final IndividualCandidacyProcessBean processBean) {
PrecedentDegreeInformationForIndividualCandidacyFactory.create(this, processBean);
}
public Registration createRegistration(final DegreeCurricularPlan degreeCurricularPlan, final CycleType cycleType,
final IngressionType ingressionType) {
if (getRegistration() != null) {
throw new DomainException("error.IndividualCandidacy.person.with.registration",
degreeCurricularPlan.getPresentationName());
}
if (hasActiveRegistration(degreeCurricularPlan)) {
final Registration registration = getStudent().getActiveRegistrationFor(degreeCurricularPlan);
setRegistration(registration);
return registration;
}
getPersonalDetails().ensurePersonInternalization();
return createRegistration(getPersonalDetails().getPerson(), degreeCurricularPlan, cycleType, ingressionType);
}
protected Registration createRegistration(final Person person, final DegreeCurricularPlan degreeCurricularPlan,
final CycleType cycleType, final IngressionType ingressionType) {
final Registration registration = new Registration(person, degreeCurricularPlan, cycleType);
registration.setEntryPhase(EntryPhase.FIRST_PHASE);
registration.setIngressionType(ingressionType);
registration.editStartDates(getStartDate(), registration.getHomologationDate(), registration.getStudiesStartDate());
createRaidesInformation(registration);
setRegistration(registration);
return registration;
}
protected void createRaidesInformation(Registration registration) {
Degree degree = registration.getDegree();
ExecutionYear startExecutionYear = registration.getStartExecutionYear();
ExecutionDegree executionDegree =
ExecutionDegree.getAllByDegreeAndExecutionYear(degree, startExecutionYear.getName()).iterator().next();
StudentCandidacy studentCandidacy = null;
if (registration.getDegree().getDegreeType().isIntegratedMasterDegree()) {
studentCandidacy = new IMDCandidacy(registration.getPerson(), executionDegree);
} else if (registration.getDegree().getDegreeType().isDegree()) {
studentCandidacy = new DegreeCandidacy(registration.getPerson(), executionDegree);
} else if (registration.getDegree().getDegreeType().isMasterDegree()) {
studentCandidacy = new MDCandidacy(registration.getPerson(), executionDegree);
}
studentCandidacy.getPrecedentDegreeInformation().delete();
PrecedentDegreeInformation refactoredPrecedentDegreeInformation = getRefactoredPrecedentDegreeInformation();
refactoredPrecedentDegreeInformation.setRegistration(registration);
studentCandidacy.setPrecedentDegreeInformation(refactoredPrecedentDegreeInformation);
studentCandidacy.setRegistration(registration);
PersonalIngressionData personalIngressionDataByExecutionYear =
registration.getStudent().getPersonalIngressionDataByExecutionYear(startExecutionYear);
if (personalIngressionDataByExecutionYear != null) {
personalIngressionDataByExecutionYear.addPrecedentDegreesInformations(refactoredPrecedentDegreeInformation);
} else {
new PersonalIngressionData(registration.getStudent(), startExecutionYear, refactoredPrecedentDegreeInformation);
}
}
protected boolean hasActiveRegistration(final DegreeCurricularPlan degreeCurricularPlan) {
return getPersonalDetails().hasStudent()
&& getPersonalDetails().getStudent().hasActiveRegistrationFor(degreeCurricularPlan);
}
protected YearMonthDay getStartDate() {
final ExecutionInterval interval = getCandidacyExecutionInterval();
final YearMonthDay today = new YearMonthDay();
return interval.isCurrent() && interval.getAcademicInterval().contains(today.toDateMidnight()) ? today : interval
.getBeginDateYearMonthDay();
}
public Student getStudent() {
return getPersonalDetails().getStudent();
}
public boolean hasStudent() {
return getStudent() != null;
}
protected ExecutionInterval getCandidacyExecutionInterval() {
return getCandidacyProcess() != null ? getCandidacyProcess().getCandidacyExecutionInterval() : null;
}
abstract public Collection<Degree> getAllDegrees();
protected boolean personHasDegree(final Person person, final Degree selectedDegree) {
if (person.getStudent() == null) {
return false;
}
List<Registration> registrationsFor = getNotCanceledRegistrationsFor(person, selectedDegree);
if (registrationsFor.isEmpty()) {
return false;
}
if ((registrationsFor.size() == 1) && registrationsFor.iterator().next() == getRegistration()) {
return false;
}
return true;
}
private List<Registration> getNotCanceledRegistrationsFor(final Person person, final Degree selectedDegree) {
List<Registration> registrationsFor = person.getStudent().getRegistrationsFor(selectedDegree);
List<Registration> notCanceledRegistrationsForDegree = new ArrayList<Registration>();
for (Registration registration : registrationsFor) {
if (registration.isCanceled()) {
continue;
}
notCanceledRegistrationsForDegree.add(registration);
}
return notCanceledRegistrationsForDegree;
}
protected boolean personHasOneOfDegrees(final Person person, final Set<Degree> selectedDegrees) {
if (person.getStudent() == null) {
return false;
}
for (Degree degree : selectedDegrees) {
if (person.getStudent().hasActiveRegistrationFor(degree)) {
return true;
}
}
return false;
}
public void editObservations(final IndividualCandidacyProcessBean bean) {
this.setObservations(bean.getObservations());
}
public Boolean isCandidacyInternal() {
return this.getPersonalDetails() instanceof IndividualCandidacyInternalPersonDetails;
}
protected abstract void createDebt(final Person person);
public void bindPerson(ChoosePersonBean bean) {
if (this.isCandidacyInternal()) {
throw new DomainException("error.bind.candidacy.internal");
}
Person selectedPerson = bean.getPerson();
if (selectedPerson != null) {
selectedPerson.edit(this.getPersonalDetails());
this.setPersonalDetails(new IndividualCandidacyInternalPersonDetails(this, selectedPerson));
} else {
selectedPerson = new Person(this.getPersonalDetails(), false);
this.setPersonalDetails(new IndividualCandidacyInternalPersonDetails(this, selectedPerson));
}
createDebt(this.getPersonalDetails().getPerson());
}
protected void createFormationEntries(List<FormationBean> formationConcludedBeanList,
List<FormationBean> formationNonConcludedBeanList) {
for (FormationBean formation : formationConcludedBeanList) {
this.addFormations(new Formation(this, formation));
}
for (FormationBean formation : formationNonConcludedBeanList) {
this.addFormations(new Formation(this, formation));
}
}
public List<Formation> getConcludedFormationList() {
return new ArrayList<Formation>(CollectionUtils.select(getFormationsSet(), new Predicate() {
@Override
public boolean evaluate(Object arg0) {
return ((Formation) arg0).getConcluded();
}
}));
}
public List<Formation> getNonConcludedFormationList() {
return new ArrayList<Formation>(CollectionUtils.select(getFormationsSet(), new Predicate() {
@Override
public boolean evaluate(Object arg0) {
return !((Formation) arg0).getConcluded();
}
}));
}
public void editFormationEntries(List<FormationBean> formationConcludedBeanList,
List<FormationBean> formationNonConcludedBeanList) {
List<Formation> formationsToBeRemovedList = new ArrayList<Formation>();
for (final Formation formation : this.getFormationsSet()) {
if (formation.getConcluded()) {
editFormationEntry(formationConcludedBeanList, formationsToBeRemovedList, formation);
}
}
for (final Formation formation : this.getFormationsSet()) {
if (!formation.getConcluded()) {
editFormationEntry(formationNonConcludedBeanList, formationsToBeRemovedList, formation);
}
}
for (Formation formation : formationsToBeRemovedList) {
this.getFormationsSet().remove(formation);
formation.delete();
}
for (FormationBean bean : formationConcludedBeanList) {
if (bean.getFormation() == null) {
this.addFormations(new Formation(this, bean));
}
}
for (FormationBean bean : formationNonConcludedBeanList) {
if (bean.getFormation() == null) {
this.addFormations(new Formation(this, bean));
}
}
}
private void editFormationEntry(List<FormationBean> formationConcludedBeanList, List<Formation> formationsToBeRemovedList,
final Formation formation) {
FormationBean bean = (FormationBean) CollectionUtils.find(formationConcludedBeanList, new Predicate() {
@Override
public boolean evaluate(Object arg0) {
FormationBean bean = (FormationBean) arg0;
return bean.getFormation() == formation;
}
});
if (bean == null) {
formationsToBeRemovedList.add(formation);
} else {
formation.edit(bean);
}
}
public void exportValues(final StringBuilder result) {
Formatter formatter = new Formatter(result);
formatter.format("%s: %s\n", BundleUtil.getString(Bundle.ACADEMIC, "label.IndividualCandidacy.candidacy"),
getCandidacyExecutionInterval().getName());
formatter.format("%s: %s\n", BundleUtil.getString(Bundle.ACADEMIC, "label.IndividualCandidacy.state"), getState()
.getLocalizedName());
formatter.format("%s: %s\n", BundleUtil.getString(Bundle.ACADEMIC, "label.IndividualCandidacy.whenCreated"),
getWhenCreated().toString("yyy-MM-dd"));
formatter.format("%s: %s\n", BundleUtil.getString(Bundle.ACADEMIC, "label.IndividualCandidacy.candidacyDate"),
getCandidacyDate().toString());
formatter.format("%s: %s\n", BundleUtil.getString(Bundle.ACADEMIC, "label.IndividualCandidacy.responsible"),
StringUtils.isEmpty(getResponsible()) ? StringUtils.EMPTY : getResponsible());
formatter.format("%s: %s\n", BundleUtil.getString(Bundle.ACADEMIC, "label.IndividualCandidacy.notes"),
StringUtils.isEmpty(getNotes()) ? StringUtils.EMPTY : getNotes());
formatter.format("%s: %s\n", BundleUtil.getString(Bundle.ACADEMIC, "label.IndividualCandidacy.observations"),
StringUtils.isEmpty(getObservations()) ? StringUtils.EMPTY : getObservations());
for (final Formation formation : getFormationsSet()) {
formation.exportValues(result);
}
formatter.close();
}
abstract public String getDescription();
public boolean isDegreeCandidacyForGraduatedPerson() {
return false;
}
public boolean isDegreeChange() {
return false;
}
public boolean isDegreeTransfer() {
return false;
}
public boolean isErasmus() {
return false;
}
public boolean isOver23() {
return false;
}
public boolean isSecondCycle() {
return false;
}
public boolean isStandalone() {
return false;
}
void editPrecedentDegreeInformation(IndividualCandidacyProcessBean bean) {
PrecedentDegreeInformationForIndividualCandidacyFactory.edit(bean);
}
}