/* * 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); } } }