/*
* This file is part of LibrePlan
*
* Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e
* Desenvolvemento Tecnolóxico de Galicia
* Copyright (C) 2010-2011 Igalia, S.L.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.libreplan.web.calendars;
import static org.libreplan.business.common.exceptions.ValidationException.invalidValue;
import static org.libreplan.web.I18nHelper._;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.lang3.Validate;
import org.joda.time.LocalDate;
import org.libreplan.business.calendars.daos.IBaseCalendarDAO;
import org.libreplan.business.calendars.daos.ICalendarExceptionTypeDAO;
import org.libreplan.business.calendars.entities.BaseCalendar;
import org.libreplan.business.calendars.entities.CalendarAvailability;
import org.libreplan.business.calendars.entities.CalendarData;
import org.libreplan.business.calendars.entities.CalendarData.Days;
import org.libreplan.business.calendars.entities.CalendarException;
import org.libreplan.business.calendars.entities.CalendarExceptionType;
import org.libreplan.business.calendars.entities.Capacity;
import org.libreplan.business.calendars.entities.ResourceCalendar;
import org.libreplan.business.common.IntegrationEntity;
import org.libreplan.business.common.daos.IConfigurationDAO;
import org.libreplan.business.common.entities.Configuration;
import org.libreplan.business.common.entities.EntityNameEnum;
import org.libreplan.business.common.exceptions.InstanceNotFoundException;
import org.libreplan.business.common.exceptions.ValidationException;
import org.libreplan.business.workingday.EffortDuration;
import org.libreplan.business.workingday.IntraDayDate.PartialDay;
import org.libreplan.web.common.IntegrationEntityModel;
import org.libreplan.web.common.concurrentdetection.OnConcurrentModification;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* Model for UI operations related to {@link BaseCalendar}.
* @author Manuel Rego Casasnovas <mrego@igalia.com>
* @author Diego Pino Garcia <dpino@igalia.com>
*/
@Service
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
@Qualifier("main")
@OnConcurrentModification(goToPage = "/calendars/calendars.zul")
public class BaseCalendarModel extends IntegrationEntityModel implements IBaseCalendarModel {
/**
* Conversation state
*/
protected BaseCalendar baseCalendar;
private LocalDate selectedDate = new LocalDate();
protected boolean editing = false;
@Autowired
private IBaseCalendarDAO baseCalendarDAO;
@Autowired
private IConfigurationDAO configurationDAO;
@Autowired
private ICalendarExceptionTypeDAO calendarExceptionTypeDAO;
/*
* Non conversational steps
*/
@Override
@Transactional(readOnly = true)
public List<BaseCalendar> getBaseCalendars() {
List<BaseCalendar> baseCalendars = baseCalendarDAO.getBaseCalendars();
for (BaseCalendar each : baseCalendars) {
forceLoad(each);
}
return baseCalendars;
}
/*
* Initial conversation steps
*/
@Override
@Transactional(readOnly = true)
public void initCreate() {
editing = false;
boolean codeGenerated = (configurationDAO.getConfiguration() != null) ? configurationDAO
.getConfiguration().getGenerateCodeForBaseCalendars()
: false;
this.baseCalendar = BaseCalendar.createBasicCalendar("");
if (codeGenerated) {
setDefaultCode();
}
baseCalendar.setCodeAutogenerated(codeGenerated);
}
@Override
@Transactional(readOnly = true)
public void initEdit(BaseCalendar baseCalendar) {
editing = true;
Validate.notNull(baseCalendar);
this.baseCalendar = getFromDB(baseCalendar);
forceLoad(this.baseCalendar);
initOldCodes();
}
@Override
@Transactional(readOnly = true)
public void initCreateDerived(BaseCalendar baseCalendar) {
editing = false;
Validate.notNull(baseCalendar);
this.baseCalendar = getFromDB(baseCalendar).newDerivedCalendar();
forceLoad(this.baseCalendar);
this.baseCalendar.setCode("");
boolean codeGenerated = (configurationDAO.getConfiguration() != null) ? configurationDAO
.getConfiguration().getGenerateCodeForBaseCalendars()
: false;
if (codeGenerated) {
setDefaultCode();
}
this.baseCalendar.setCodeAutogenerated(codeGenerated);
}
@Override
@Transactional(readOnly = true)
public void initCreateCopy(BaseCalendar baseCalendar) {
editing = false;
Validate.notNull(baseCalendar);
this.baseCalendar = getFromDB(baseCalendar).newCopy();
forceLoad(this.baseCalendar);
this.baseCalendar.setCode("");
if (this.baseCalendar.isCodeAutogenerated()) {
setDefaultCode();
}
}
@Override
public void initRemove(BaseCalendar baseCalendar) {
this.baseCalendar = baseCalendar;
}
protected void forceLoad(BaseCalendar baseCalendar) {
forceLoadBaseCalendar(baseCalendar);
forceLoadExceptionTypes();
}
public static void forceLoadBaseCalendar(BaseCalendar baseCalendar) {
for (CalendarData calendarData : baseCalendar.getCalendarDataVersions()) {
if (calendarData.getParent() != null) {
forceLoadBaseCalendar(calendarData.getParent());
}
}
loadingExceptionsWithTheirTypes(baseCalendar);
}
private static void loadingExceptionsWithTheirTypes(
BaseCalendar baseCalendar) {
Set<CalendarException> exceptions = baseCalendar.getExceptions();
for (CalendarException each : exceptions) {
each.getType().getName();
}
}
private void forceLoadExceptionTypes() {
for (CalendarExceptionType calendarExceptionType : getCalendarExceptionTypes()) {
calendarExceptionType.getName();
}
}
@Transactional(readOnly = true)
private BaseCalendar getFromDB(Long id) {
try {
BaseCalendar result = baseCalendarDAO.find(id);
return result;
} catch (InstanceNotFoundException e) {
throw new RuntimeException(e);
}
}
protected BaseCalendar getFromDB(BaseCalendar baseCalendar) {
return getFromDB(baseCalendar.getId());
}
/*
* Intermediate conversation steps
*/
@Override
public BaseCalendar getBaseCalendar() {
return baseCalendar;
}
@Override
@Transactional(readOnly = true)
public List<BaseCalendar> getPossibleParentCalendars() {
List<BaseCalendar> baseCalendars = getBaseCalendars();
if (getBaseCalendar() != null) {
for (BaseCalendar calendar : baseCalendars) {
if (calendar.getId().equals(getBaseCalendar().getId())) {
baseCalendars.remove(calendar);
break;
}
}
}
return baseCalendars;
}
@Override
public boolean isEditing() {
return this.editing;
}
@Override
public void setSelectedDay(LocalDate date) {
this.selectedDate = date != null ? date : new LocalDate();
}
@Override
public LocalDate getSelectedDay() {
return selectedDate;
}
@Override
public EffortDuration getWorkableTime() {
if (getBaseCalendar() == null) {
return null;
}
return getBaseCalendar().getCapacityOn(
PartialDay.wholeDay(selectedDate));
}
@Override
public Capacity getWorkableCapacity() {
if (getBaseCalendar() == null) {
return null;
}
return getBaseCalendar().getCapacityWithOvertime(selectedDate);
}
@Override
public void createException(CalendarExceptionType type,
LocalDate startDate, LocalDate endDate, Capacity capacity) {
for (LocalDate date = startDate; date.compareTo(endDate) <= 0; date = date
.plusDays(1)) {
if (baseCalendar.getOwnExceptionDay(date) != null) {
getBaseCalendar().updateExceptionDay(date, capacity, type);
} else {
CalendarException day = CalendarException.create("", date,
capacity, type);
getBaseCalendar().addExceptionDay(day);
}
}
}
@Override
public Capacity getCapacityAt(Days day) {
if (getBaseCalendar() == null) {
return Capacity.zero();
}
return getBaseCalendar().getCapacityConsideringCalendarDataOn(
selectedDate, day);
}
@Override
public Boolean isDefault(Days day) {
if (getBaseCalendar() == null) {
return false;
}
return getBaseCalendar().isDefault(day, selectedDate);
}
@Override
public void unsetDefault(Days day) {
if (getBaseCalendar() != null) {
Capacity previousCapacity = getBaseCalendar()
.getCapacityConsideringCalendarDataOn(selectedDate, day);
getBaseCalendar()
.setCapacityAt(day, previousCapacity, selectedDate);
}
}
@Override
public void setDefault(Days day) {
if (getBaseCalendar() != null) {
getBaseCalendar().setDefault(day, selectedDate);
}
}
@Override
public void setCapacityAt(Days day, Capacity value) {
if (getBaseCalendar() != null) {
getBaseCalendar().setCapacityAt(day, value, selectedDate);
}
}
@Override
public boolean isExceptional() {
if (getBaseCalendar() == null) {
return false;
}
CalendarException day = getBaseCalendar().getOwnExceptionDay(selectedDate);
return (day != null);
}
@Override
public void removeException() {
getBaseCalendar().removeExceptionDay(selectedDate);
}
@Override
public boolean isDerived() {
if (getBaseCalendar() == null) {
return false;
}
return getBaseCalendar().isDerived(selectedDate);
}
@Override
public BaseCalendar getParent() {
if (getBaseCalendar() == null) {
return null;
}
return getBaseCalendar().getParent(selectedDate);
}
@Override
public BaseCalendar getCurrentParent() {
if (getBaseCalendar() == null) {
return null;
}
CalendarData version = getCurrentVersion();
return version != null ? version.getParent() : null;
}
@Override
public Date getCurrentExpiringDate() {
CalendarData calendarData = getCurrentVersion();
if (calendarData != null) {
LocalDate startDate = calendarData.getExpiringDate();
return startDate != null ? startDate.minusDays(1)
.toDateTimeAtStartOfDay()
.toDate() : null;
}
return null;
}
@Override
public Date getCurrentStartDate() {
CalendarData calendarData = getCurrentVersion();
if (calendarData != null) {
LocalDate startDate = getValidFrom(calendarData);
return startDate != null ? startDate.toDateTimeAtStartOfDay()
.toDate() : null;
}
return null;
}
public CalendarData getCurrentVersion() {
return getBaseCalendar().getCalendarData(
LocalDate.fromDateFields(new Date()));
}
@Override
@Transactional(readOnly = true)
public void setParent(BaseCalendar parent) {
try {
parent = baseCalendarDAO.find(parent.getId());
} catch (InstanceNotFoundException e) {
throw new RuntimeException(e);
}
forceLoad(parent);
if (getBaseCalendar() != null) {
getBaseCalendar().setParent(parent, selectedDate);
}
}
@Override
@Transactional(readOnly = true)
public boolean isParent(BaseCalendar calendar) {
if (calendar == null) {
return false;
}
return !baseCalendarDAO.findByParent(calendar).isEmpty();
}
@Override
public LocalDate getDateValidFrom() {
if (getBaseCalendar() != null) {
LocalDate validFromDate = getBaseCalendar().getValidFrom(
selectedDate);
return validFromDate;
}
return null;
}
@Override
public void setDateValidFrom(LocalDate date) {
if (getBaseCalendar() != null) {
getBaseCalendar().setValidFrom(date, selectedDate);
}
}
@Override
public List<CalendarData> getHistoryVersions() {
if (getBaseCalendar() == null) {
return null;
}
return getBaseCalendar().getCalendarDataVersions();
}
@Override
public void createNewVersion(LocalDate startDate, LocalDate expiringDate,
BaseCalendar baseCalendar) {
if (getBaseCalendar() != null) {
if (expiringDate != null) {
expiringDate = expiringDate.plusDays(1);
}
getBaseCalendar().newVersion(startDate, expiringDate,
baseCalendar);
}
}
@Override
public boolean isLastVersion(LocalDate selectedDate) {
if (getBaseCalendar() != null) {
return getBaseCalendar().isLastVersion(selectedDate);
}
return false;
}
@Override
public boolean isFirstVersion(LocalDate selectedDate) {
if (getBaseCalendar() != null) {
return getBaseCalendar().isFirstVersion(selectedDate);
}
return false;
}
@Override
public void checkAndChangeStartDate(CalendarData version, Date date)
throws ValidationException {
if (date == null) {
if (version.equals(getBaseCalendar().getFirstCalendarData())) {
return;
} else {
throw new ValidationException(_("This date cannot be empty"));
}
}
LocalDate newStartDate = LocalDate.fromDateFields(date);
CalendarData prevVersion = getBaseCalendar().getPrevious(version);
if ((newStartDate != null) && (prevVersion != null)) {
if (getBaseCalendar().getPrevious(prevVersion) == null) {
return;
}
LocalDate prevStartDate = getBaseCalendar()
.getPrevious(prevVersion).getExpiringDate();
if ((prevStartDate == null)
|| ((newStartDate
.compareTo(prevStartDate) > 0))) {
prevVersion.setExpiringDate(newStartDate);
return;
}
}
throw new ValidationException(
_("This date can not include the whole previous work week"));
}
@Override
public void checkChangeExpiringDate(CalendarData version, Date date) {
Integer index = getBaseCalendar().getCalendarDataVersions().indexOf(
version);
if (date == null) {
if (version.equals(getBaseCalendar().getLastCalendarData())) {
return;
} else {
throw new ValidationException(_("This date cannot be empty"));
}
}
LocalDate newExpiringDate = LocalDate.fromDateFields(date);
if ((index < getBaseCalendar().getCalendarDataVersions().size() - 1)) {
LocalDate nextExpiringDate = getBaseCalendar()
.getCalendarDataVersions().get(index + 1).getExpiringDate();
if ((nextExpiringDate == null)
|| (newExpiringDate.compareTo(nextExpiringDate) < 0)) {
return;
}
}
throw new ValidationException(
_("Date cannot include the entire next work week"));
}
@Override
public String getName() {
if (getBaseCalendar() != null) {
return getBaseCalendar().getName();
}
return null;
}
@Override
public LocalDate getValidFrom(CalendarData calendarData) {
if (getBaseCalendar() != null) {
return getBaseCalendar().getValidFrom(calendarData);
}
return null;
}
/*
* Final conversation steps
*/
@Override
@Transactional(rollbackFor = ValidationException.class)
public void confirmSave() throws ValidationException {
confirmSave(getBaseCalendar());
}
@Transactional(rollbackFor = ValidationException.class)
private void confirmSave(BaseCalendar calendar) throws ValidationException {
checkInvalidValuesCalendar(calendar);
baseCalendarDAO.save(calendar);
}
@Override
@Transactional(rollbackFor = ValidationException.class)
public void confirmSaveAndContinue() throws ValidationException {
BaseCalendar baseCalendar = getBaseCalendar();
confirmSave(baseCalendar);
dontPoseAsTransientObjectAnymore(baseCalendar);
}
/**
* Don't pose as transient anymore calendar and all data hanging from
* calendar (data versions, availabilities and exceptions)
*
* @param calendar
*/
private void dontPoseAsTransientObjectAnymore(BaseCalendar calendar) {
calendar.dontPoseAsTransientObjectAnymore();
for (CalendarData each: calendar.getCalendarDataVersions()) {
each.dontPoseAsTransientObjectAnymore();
}
for (CalendarAvailability each : calendar.getCalendarAvailabilities()) {
each.dontPoseAsTransientObjectAnymore();
}
for (CalendarException each : calendar.getExceptions()) {
each.dontPoseAsTransientObjectAnymore();
}
}
@Override
public void checkInvalidValuesCalendar(BaseCalendar entity)
throws ValidationException {
if (baseCalendarDAO.thereIsOtherWithSameName(entity)) {
throw new ValidationException(_("Could not save the new calendar"),
invalidValue(_("{0} already exists", entity.getName()),
"name", entity.getName(), entity));
}
}
@Transactional
public void generateCalendarCodes() {
if (getBaseCalendar().isCodeAutogenerated()) {
baseCalendar
.generateCalendarExceptionCodes(getNumberOfDigitsCode());
}
}
@Override
@Transactional
public void confirmRemove(BaseCalendar calendar) {
try {
baseCalendarDAO.remove(calendar.getId());
} catch (InstanceNotFoundException e) {
throw new RuntimeException(e);
}
}
@Override
public void cancel() {
resetState();
}
private void resetState() {
baseCalendar = null;
}
@Override
@Transactional(readOnly = true)
public boolean isDefaultCalendar(BaseCalendar baseCalendar) {
Configuration configuration = configurationDAO.getConfiguration();
if (configuration == null) {
return false;
}
BaseCalendar defaultCalendar = configuration.getDefaultCalendar();
if (defaultCalendar == null) {
return false;
}
if (baseCalendar == null) {
return false;
}
return baseCalendar.getId().equals(
defaultCalendar
.getId());
}
@Override
@Transactional(readOnly = true)
public List<CalendarExceptionType> getCalendarExceptionTypes() {
return calendarExceptionTypeDAO.list(CalendarExceptionType.class);
}
@Override
public Set<CalendarException> getCalendarExceptions() {
if (getBaseCalendar() == null) {
return new HashSet<CalendarException>();
}
return getBaseCalendar().getExceptions();
}
@Override
public boolean isOwnException(CalendarException exception) {
if (getBaseCalendar() == null) {
return false;
}
return getBaseCalendar().getOwnExceptions().contains(exception);
}
@Override
public void removeException(LocalDate date) {
if (getBaseCalendar() != null) {
getBaseCalendar().removeExceptionDay(date);
}
}
@Override
public CalendarExceptionType getCalendarExceptionType() {
if (getBaseCalendar() == null) {
return null;
}
return getBaseCalendar().getExceptionType(selectedDate);
}
@Override
public CalendarExceptionType getCalendarExceptionType(LocalDate date) {
if (getBaseCalendar() == null) {
return null;
}
return getBaseCalendar().getExceptionType(date);
}
@Override
public void updateException(CalendarExceptionType type,
LocalDate startDate, LocalDate endDate, Capacity capacity) {
for (LocalDate date = new LocalDate(startDate); date
.compareTo(new LocalDate(endDate)) <= 0; date = date
.plusDays(1)) {
if (baseCalendar.getOwnExceptionDay(date) != null) {
if (type == null) {
getBaseCalendar().removeExceptionDay(date);
} else {
getBaseCalendar().updateExceptionDay(date, capacity, type);
}
} else {
if (type != null) {
CalendarException day = CalendarException.create(date,
capacity, type);
getBaseCalendar().addExceptionDay(day);
}
}
}
}
@Override
public void removeCalendarData(CalendarData calendarData) {
if (getBaseCalendar() != null) {
getBaseCalendar().removeCalendarData(calendarData);
}
}
@Override
public CalendarData getLastCalendarData() {
if (getBaseCalendar() == null) {
return null;
}
return getBaseCalendar().getLastCalendarData();
}
@Override
public CalendarData getCalendarData() {
if (getBaseCalendar() == null) {
return null;
}
LocalDate selectedDay = getSelectedDay();
if (selectedDay == null) {
return null;
}
return getBaseCalendar().getCalendarData(selectedDay);
}
@Override
public boolean isResourceCalendar() {
if (getBaseCalendar() == null) {
return false;
}
return getBaseCalendar() instanceof ResourceCalendar;
}
@Override
public List<CalendarAvailability> getCalendarAvailabilities() {
if (getBaseCalendar() == null) {
return null;
}
return getBaseCalendar().getCalendarAvailabilities();
}
@Override
public void removeCalendarAvailability(
CalendarAvailability calendarAvailability) {
if (getBaseCalendar() != null) {
getBaseCalendar().removeCalendarAvailability(calendarAvailability);
}
}
@Override
public void createCalendarAvailability() {
if (getBaseCalendar() != null) {
LocalDate startDate = new LocalDate();
CalendarAvailability lastCalendarAvailability = getBaseCalendar()
.getLastCalendarAvailability();
if (lastCalendarAvailability != null) {
if (lastCalendarAvailability.getEndDate() == null) {
startDate = lastCalendarAvailability.getStartDate();
} else {
startDate = lastCalendarAvailability.getEndDate();
}
startDate = startDate.plusDays(1);
}
CalendarAvailability calendarAvailability = CalendarAvailability
.create(startDate, null);
calendarAvailability.setCode("");
getBaseCalendar().addNewCalendarAvailability(calendarAvailability);
}
}
@Override
public void setStartDate(CalendarAvailability calendarAvailability,
LocalDate startDate) throws IllegalArgumentException {
if (getBaseCalendar() != null) {
getBaseCalendar().setStartDate(calendarAvailability, startDate);
}
}
@Override
public void setEndDate(CalendarAvailability calendarAvailability,
LocalDate endDate) throws IllegalArgumentException {
if (getBaseCalendar() != null) {
getBaseCalendar().setEndDate(calendarAvailability, endDate);
}
}
@Override
public EntityNameEnum getEntityName() {
return EntityNameEnum.CALENDAR;
}
@Override
public Set<IntegrationEntity> getChildren() {
Set<IntegrationEntity> children = new HashSet<IntegrationEntity>();
if (baseCalendar != null) {
children.addAll(baseCalendar.getExceptions());
children.addAll(baseCalendar.getCalendarDataVersions());
children.addAll(baseCalendar.getCalendarAvailabilities());
}
return children;
}
@Override
public IntegrationEntity getCurrentEntity() {
return this.baseCalendar;
}
@Override
public boolean isLastActivationPeriod(
CalendarAvailability calendarAvailability) {
if (getBaseCalendar() != null) {
return getBaseCalendar().isLastCalendarAvailability(
calendarAvailability);
}
return false;
}
@Override
@Transactional(readOnly = true)
public void checkIsReferencedByOtherEntities(BaseCalendar calendar) throws ValidationException {
baseCalendarDAO.checkIsReferencedByOtherEntities(calendar);
}
@Override
public boolean isOwnExceptionDay() {
if (baseCalendar != null) {
return (baseCalendar.getOwnExceptionDay(selectedDate) != null);
}
return false;
}
@Override
public boolean isVirtualWorker() {
if (baseCalendar == null) {
return false;
}
if (baseCalendar instanceof ResourceCalendar) {
ResourceCalendar resourceCalendar = (ResourceCalendar) baseCalendar;
return (resourceCalendar.getResource() != null)
&& resourceCalendar.getResource().isVirtual();
}
return false;
}
@Override
public Integer getCapacity() {
if (isVirtualWorker()) {
ResourceCalendar resourceCalendar = (ResourceCalendar) baseCalendar;
return resourceCalendar.getCapacity();
}
return 1;
}
@Override
public void setCapacity(Integer capacity) {
if (isVirtualWorker()) {
ResourceCalendar resourceCalendar = (ResourceCalendar) baseCalendar;
resourceCalendar.setCapacity(capacity);
}
}
}