View Javadoc
1   /*
2    * Copyright (c) 2002-2018, Mairie de Paris
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met:
8    *
9    *  1. Redistributions of source code must retain the above copyright notice
10   *     and the following disclaimer.
11   *
12   *  2. Redistributions in binary form must reproduce the above copyright notice
13   *     and the following disclaimer in the documentation and/or other materials
14   *     provided with the distribution.
15   *
16   *  3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
17   *     contributors may be used to endorse or promote products derived from
18   *     this software without specific prior written permission.
19   *
20   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
24   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30   * POSSIBILITY OF SUCH DAMAGE.
31   *
32   * License 1.0
33   */
34  package fr.paris.lutece.plugins.appointment.web;
35  
36  import java.time.LocalDate;
37  import java.time.LocalDateTime;
38  import java.time.LocalTime;
39  import java.util.ArrayList;
40  import java.util.HashMap;
41  import java.util.HashSet;
42  import java.util.List;
43  import java.util.Map;
44  import java.util.stream.Collectors;
45  
46  import javax.servlet.http.HttpServletRequest;
47  
48  import org.apache.commons.collections.CollectionUtils;
49  import org.apache.commons.lang.StringUtils;
50  
51  import fr.paris.lutece.plugins.appointment.business.appointment.Appointment;
52  import fr.paris.lutece.plugins.appointment.business.display.Display;
53  import fr.paris.lutece.plugins.appointment.business.form.Form;
54  import fr.paris.lutece.plugins.appointment.business.planning.ClosingDay;
55  import fr.paris.lutece.plugins.appointment.business.planning.TimeSlot;
56  import fr.paris.lutece.plugins.appointment.business.planning.WeekDefinition;
57  import fr.paris.lutece.plugins.appointment.business.planning.WorkingDay;
58  import fr.paris.lutece.plugins.appointment.business.rule.ReservationRule;
59  import fr.paris.lutece.plugins.appointment.business.slot.Period;
60  import fr.paris.lutece.plugins.appointment.business.slot.Slot;
61  import fr.paris.lutece.plugins.appointment.log.LogUtilities;
62  import fr.paris.lutece.plugins.appointment.service.AppointmentResourceIdService;
63  import fr.paris.lutece.plugins.appointment.service.AppointmentService;
64  import fr.paris.lutece.plugins.appointment.service.AppointmentUtilities;
65  import fr.paris.lutece.plugins.appointment.service.ClosingDayService;
66  import fr.paris.lutece.plugins.appointment.service.DisplayService;
67  import fr.paris.lutece.plugins.appointment.service.FormService;
68  import fr.paris.lutece.plugins.appointment.service.ReservationRuleService;
69  import fr.paris.lutece.plugins.appointment.service.SlotService;
70  import fr.paris.lutece.plugins.appointment.service.TimeSlotService;
71  import fr.paris.lutece.plugins.appointment.service.WeekDefinitionService;
72  import fr.paris.lutece.plugins.appointment.service.WorkingDayService;
73  import fr.paris.lutece.plugins.appointment.web.dto.AppointmentFormDTO;
74  import fr.paris.lutece.portal.service.admin.AccessDeniedException;
75  import fr.paris.lutece.portal.service.message.AdminMessage;
76  import fr.paris.lutece.portal.service.message.AdminMessageService;
77  import fr.paris.lutece.portal.service.rbac.RBACService;
78  import fr.paris.lutece.portal.service.util.AppLogService;
79  import fr.paris.lutece.portal.service.util.AppPropertiesService;
80  import fr.paris.lutece.portal.util.mvc.admin.annotations.Controller;
81  import fr.paris.lutece.portal.util.mvc.commons.annotations.Action;
82  import fr.paris.lutece.portal.util.mvc.commons.annotations.View;
83  import fr.paris.lutece.util.url.UrlItem;
84  
85  /**
86   * JspBean to manage calendar slots
87   * 
88   * @author Laurent Payen
89   *
90   */
91  @Controller( controllerJsp = AppointmentSlotJspBean.JSP_MANAGE_APPOINTMENT_SLOTS, controllerPath = "jsp/admin/plugins/appointment/", right = AppointmentFormJspBean.RIGHT_MANAGEAPPOINTMENTFORM )
92  public class AppointmentSlotJspBean extends AbstractAppointmentFormAndSlotJspBean
93  {
94      /**
95       * JSP of this JSP Bean
96       */
97      public static final String JSP_MANAGE_APPOINTMENT_SLOTS = "ManageAppointmentSlots.jsp";
98  
99      /**
100      * Serial version UID
101      */
102     private static final long serialVersionUID = 2376721852596997810L;
103 
104     // Messages
105     private static final String MESSAGE_SPECIFIC_WEEK_PAGE_TITLE = "appointment.specificWeek.pageTitle";
106     private static final String MESSAGE_TYPICAL_WEEK_PAGE_TITLE = "appointment.typicalWeek.pageTitle";
107     private static final String MESSAGE_MODIFY_SLOT_PAGE_TITLE = "appointment.modifyCalendarSlots.pageTitle";
108     private static final String MESSAGE_MODIFY_TIME_SLOT_PAGE_TITLE = "appointment.modifyCalendarSlots.pageTitle";
109     private static final String MESSAGE_WARNING_CHANGES_APPLY_TO_ALL = "appointment.modifyCalendarSlots.warningModifiyingEndingTime";
110     private static final String MESSAGE_ERROR_TIME_END_BEFORE_TIME_START = "appointment.modifyCalendarSlots.errorTimeEndBeforeTimeStart";
111     private static final String MESSAGE_SLOT_CAN_NOT_END_AFTER_DAY_OR_FORM = "appointment.message.error.slotCanNotEndAfterDayOrForm";
112     private static final String MESSAGE_ERROR_APPOINTMENT_ON_SLOT = "appointment.message.error.appointmentOnSlot";
113     private static final String MESSAGE_ERROR_LAST_WEEK_DEFINITION = "appointment.message.error.lastWeekDefinition";
114     private static final String MESSAGE_INFO_SLOT_UPDATED = "appointment.modifyCalendarSlots.messageSlotUpdated";
115     private static final String MESSAGE_INFO_VALIDATED_APPOINTMENTS_IMPACTED = "appointment.modifyCalendarSlots.messageValidatedAppointmentsImpacted";
116     private static final String MESSAGE_INFO_SURBOOKING = "appointment.modifyCalendarSlots.messageSurbooking";
117     private static final String MESSAGE_INFO_OVERLOAD = "appointment.modifyCalendarSlots.messageOverload";
118     private static final String MESSAGE_ERROR_START_DATE_EMPTY = "appointment.message.error.startDateEmpty";
119     private static final String MESSAGE_ERROR_MODIFY_FORM_HAS_APPOINTMENTS_AFTER_DATE_OF_MODIFICATION = "appointment.message.error.refreshDays.modifyFormHasAppointments";
120     private static final String VALIDATION_ATTRIBUTES_PREFIX = "appointment.model.entity.appointmentform.attribute.";
121     private static final String MESSAGE_CONFIRM_REMOVE_WEEK_DEFINITION = "appointment.message.confirmRemoveWeekDefinition";
122 
123     // Parameters
124     private static final String PARAMETER_ENDING_DATE_OF_DISPLAY = "ending_date_of_display";
125     private static final String PARAMETER_DATE_OF_DISPLAY = "date_of_display";
126     private static final String PARAMETER_ERROR_MODIFICATION = "error_modification";
127     private static final String PARAMETER_ID_FORM = "id_form";
128     private static final String PARAMETER_ID_SLOT = "id_slot";
129     private static final String PARAMETER_STARTING_DATE_TIME = "starting_date_time";
130     private static final String PARAMETER_ENDING_DATE_TIME = "ending_date_time";
131     private static final String PARAMETER_ID_TIME_SLOT = "id_time_slot";
132     private static final String PARAMETER_DAY_OF_WEEK = "dow";
133     private static final String PARAMETER_EVENTS = "events";
134     private static final String PARAMETER_MIN_DURATION = "min_duration";
135     private static final String PARAMETER_MIN_TIME = "min_time";
136     private static final String PARAMETER_MAX_TIME = "max_time";
137     private static final String PARAMETER_IS_OPEN = "is_open";
138     private static final String PARAMETER_IS_SPECIFIC = "is_specific";
139     private static final String PARAMETER_ENDING_TIME = "ending_time";
140     private static final String PARAMETER_MAX_CAPACITY = "max_capacity";
141     private static final String PARAMETER_ID_WEEK_DEFINITION = "id_week_definition";
142     private static final String PARAMETER_SHIFT_SLOT = "shift_slot";
143 
144     // Marks
145     private static final String MARK_TIME_SLOT = "timeSlot";
146     private static final String MARK_SLOT = "slot";
147     private static final String MARK_LIST_DATE_OF_MODIFICATION = "listDateOfModification";
148 
149     // Views
150     private static final String VIEW_MANAGE_SPECIFIC_WEEK = "manageSpecificWeek";
151     private static final String VIEW_MANAGE_TYPICAL_WEEK = "manageTypicalWeek";
152     private static final String VIEW_MODIFY_TIME_SLOT = "viewModifyTimeSlot";
153     private static final String VIEW_MODIFY_SLOT = "viewModifySlot";
154 
155     // Actions
156     private static final String ACTION_DO_MODIFY_TIME_SLOT = "doModifyTimeSlot";
157     private static final String ACTION_DO_MODIFY_SLOT = "doModifySlot";
158     private static final String ACTION_MODIFY_ADVANCED_PARAMETERS = "modifyAdvancedParameters";
159     private static final String ACTION_CONFIRM_REMOVE_PARAMETER = "confirmRemoveParameter";
160     private static final String ACTION_REMOVE_PARAMETER = "doRemoveParameter";
161     // Templates
162     private static final String TEMPLATE_MANAGE_SPECIFIC_WEEK = "admin/plugins/appointment/slots/manage_specific_week.html";
163     private static final String TEMPLATE_MANAGE_TYPICAL_WEEK = "admin/plugins/appointment/slots/manage_typical_week.html";
164     private static final String TEMPLATE_MODIFY_TIME_SLOT = "admin/plugins/appointment/slots/modify_time_slot.html";
165     private static final String TEMPLATE_MODIFY_SLOT = "admin/plugins/appointment/slots/modify_slot.html";
166 
167     // Session variable to store working values
168     private static final String SESSION_ATTRIBUTE_TIME_SLOT = "appointment.session.timeSlot";
169     private static final String SESSION_ATTRIBUTE_SLOT = "appointment.session.slot";
170     private static final String SESSION_ATTRIBUTE_APPOINTMENT_FORM = "appointment.session.appointmentForm";
171 
172     // Porperties
173     private static final String PROPERTY_NB_WEEKS_TO_DISPLAY_IN_BO = "appointment.nbWeeksToDisplayInBO";
174 
175     // Infos
176     private static final String INFO_ADVANCED_PARAMETERS_UPDATED = "appointment.info.advancedparameters.updated";
177     private static final String INFO_PARAMETER_REMOVED = "appointment.info.advancedparameters.removed";
178 
179     /**
180      * Get the view of the typical week
181      * 
182      * @param request
183      *            the request
184      * @return the page
185      * @throws AccessDeniedException
186      */
187     @View( value = VIEW_MANAGE_TYPICAL_WEEK )
188     public String getViewManageTypicalWeek( HttpServletRequest request ) throws AccessDeniedException
189     {
190         request.getSession( ).removeAttribute( SESSION_ATTRIBUTE_TIME_SLOT );
191         int nIdForm = Integer.parseInt( request.getParameter( PARAMETER_ID_FORM ) );
192         String strIdWeekDefinition = request.getParameter( PARAMETER_ID_WEEK_DEFINITION );
193         int nIdWeekDefinition = 0;
194         if ( StringUtils.isNotEmpty( strIdWeekDefinition ) )
195         {
196             nIdWeekDefinition = Integer.parseInt( strIdWeekDefinition );
197         }
198         LocalDate dateOfApply = LocalDate.now( );
199         WeekDefinition weekDefinition;
200         if ( nIdWeekDefinition != 0 )
201         {
202             weekDefinition = WeekDefinitionService.findWeekDefinitionById( nIdWeekDefinition );
203         }
204         else
205         {
206             weekDefinition = WeekDefinitionService.findWeekDefinitionByIdFormAndClosestToDateOfApply( nIdForm, dateOfApply );
207             if ( weekDefinition != null )
208             {
209                 nIdWeekDefinition = weekDefinition.getIdWeekDefinition( );
210             }
211         }
212         Map<String, Object> model = getModel( );
213         AppointmentFormDTO appointmentForm = null;
214         if ( request.getParameter( PARAMETER_ERROR_MODIFICATION ) != null )
215         {
216             appointmentForm = (AppointmentFormDTO) request.getSession( ).getAttribute( SESSION_ATTRIBUTE_APPOINTMENT_FORM );
217             model.put( PARAMETER_ERROR_MODIFICATION, Boolean.TRUE );
218         }
219         List<String> listDayOfWeek = new ArrayList<>( );
220         List<TimeSlot> listTimeSlot = new ArrayList<>( );
221         LocalTime minStartingTime = LocalTime.MIN;
222         LocalTime maxEndingTime = LocalTime.MAX;
223         if ( weekDefinition == null )
224         {
225             appointmentForm = FormService.buildAppointmentForm( nIdForm, 0, 0 );
226 
227         }
228         else
229         {
230             if ( appointmentForm == null )
231             {
232                 int nIdReservationRule = 0;
233                 ReservationRule reservationRule = ReservationRuleService.findReservationRuleByIdFormAndDateOfApply( nIdForm, weekDefinition.getDateOfApply( ) );
234                 if ( reservationRule != null )
235                 {
236                     nIdReservationRule = reservationRule.getIdReservationRule( );
237                 }
238                 appointmentForm = FormService.buildAppointmentForm( nIdForm, nIdReservationRule, nIdWeekDefinition );
239             }
240             List<WorkingDay> listWorkingDay = weekDefinition.getListWorkingDay( );
241             listDayOfWeek = new ArrayList<>( WorkingDayService.getSetDaysOfWeekOfAListOfWorkingDayForFullCalendar( listWorkingDay ) );
242             listTimeSlot = TimeSlotService.getListTimeSlotOfAListOfWorkingDay( listWorkingDay, dateOfApply );
243             minStartingTime = WorkingDayService.getMinStartingTimeOfAListOfWorkingDay( listWorkingDay );
244             maxEndingTime = WorkingDayService.getMaxEndingTimeOfAListOfWorkingDay( listWorkingDay );
245         }
246         model.put( PARAMETER_DAY_OF_WEEK, listDayOfWeek );
247         model.put( PARAMETER_EVENTS, listTimeSlot );
248         model.put( PARAMETER_MIN_TIME, minStartingTime );
249         model.put( PARAMETER_MAX_TIME, maxEndingTime );
250         model.put( PARAMETER_MIN_DURATION, LocalTime.MIN.plusMinutes( AppointmentUtilities.THIRTY_MINUTES ) );
251         model.put( PARAMETER_ID_WEEK_DEFINITION, nIdWeekDefinition );
252         model.put( MARK_LIST_DATE_OF_MODIFICATION, WeekDefinitionService.findAllDateOfWeekDefinition( nIdForm ) );
253         AppointmentFormJspBean.addElementsToModel( request, appointmentForm, getUser( ), getLocale( ), model );
254         return getPage( MESSAGE_TYPICAL_WEEK_PAGE_TITLE, TEMPLATE_MANAGE_TYPICAL_WEEK, model );
255     }
256 
257     /**
258      * Do modify a form (advanced parameters part)
259      * 
260      * @param request
261      *            the request
262      * @return the JSP URL to display the form to modify appointment forms
263      * @throws AccessDeniedException
264      */
265     @Action( ACTION_MODIFY_ADVANCED_PARAMETERS )
266     public String doModifyAdvancedParameters( HttpServletRequest request ) throws AccessDeniedException
267     {
268         String strIdForm = request.getParameter( PARAMETER_ID_FORM );
269         int nIdForm = Integer.parseInt( strIdForm );
270         if ( !RBACService.isAuthorized( AppointmentFormDTO.RESOURCE_TYPE, strIdForm, AppointmentResourceIdService.PERMISSION_MODIFY_ADVANCED_SETTING_FORM,
271                 getUser( ) ) )
272         {
273             throw new AccessDeniedException( AppointmentResourceIdService.PERMISSION_MODIFY_ADVANCED_SETTING_FORM );
274         }
275         AppointmentFormDTO appointmentForm = (AppointmentFormDTO) request.getSession( ).getAttribute( SESSION_ATTRIBUTE_APPOINTMENT_FORM );
276         populate( appointmentForm, request );
277         if ( appointmentForm.getDateOfModification( ) == null )
278         {
279             request.getSession( ).setAttribute( SESSION_ATTRIBUTE_APPOINTMENT_FORM, appointmentForm );
280             addError( MESSAGE_ERROR_START_DATE_EMPTY, getLocale( ) );
281             return redirect( request, VIEW_MANAGE_TYPICAL_WEEK, PARAMETER_ID_FORM, nIdForm, PARAMETER_ERROR_MODIFICATION, 1 );
282         }
283         LocalDate dateOfModification = appointmentForm.getDateOfModification( ).toLocalDate( );
284         // Get all the appointments after this date and until the next week
285         // definition if it exists
286         WeekDefinition nextWeekDefinition = WeekDefinitionService.findNextWeekDefinition( nIdForm, dateOfModification );
287         // We can't use the LocalDateTime.MAX value because of the bug of the
288         // year 2038 for Timestamp
289         // (https://fr.wikipedia.org/wiki/Bug_de_l%27an_2038)
290         LocalDateTime endingDateTimeOfSearch = LocalDateTime.of( LocalDate.of( 9999, 12, 31 ), LocalTime.MAX );
291         if ( nextWeekDefinition != null )
292         {
293             endingDateTimeOfSearch = nextWeekDefinition.getDateOfApply( ).atTime( LocalTime.MIN );
294         }
295         if ( !validateBean( appointmentForm, VALIDATION_ATTRIBUTES_PREFIX ) || !checkConstraints( appointmentForm ) )
296         {
297             request.getSession( ).setAttribute( SESSION_ATTRIBUTE_APPOINTMENT_FORM, appointmentForm );
298             return redirect( request, VIEW_MANAGE_TYPICAL_WEEK, PARAMETER_ID_FORM, nIdForm, PARAMETER_ERROR_MODIFICATION, 1 );
299         }
300         List<Slot> listSlotsImpacted = SlotService.findSlotsByIdFormAndDateRange( nIdForm, dateOfModification.atStartOfDay( ), endingDateTimeOfSearch );
301         List<Appointment> listAppointmentsImpacted = AppointmentService.findListAppointmentByListSlot( listSlotsImpacted );
302         // if there are slots impacted
303         if ( CollectionUtils.isNotEmpty( listSlotsImpacted ) )
304         {
305             // if there are appointments impacted
306             if ( CollectionUtils.isNotEmpty( listAppointmentsImpacted ) )
307             {
308                 if ( !AppointmentUtilities.checkNoAppointmentsImpacted( listAppointmentsImpacted, nIdForm, dateOfModification, appointmentForm ) )
309                 {
310                     request.getSession( ).setAttribute( SESSION_ATTRIBUTE_APPOINTMENT_FORM, appointmentForm );
311                     addError( MESSAGE_ERROR_MODIFY_FORM_HAS_APPOINTMENTS_AFTER_DATE_OF_MODIFICATION, getLocale( ) );
312                     return redirect( request, VIEW_MANAGE_TYPICAL_WEEK, PARAMETER_ID_FORM, nIdForm, PARAMETER_ERROR_MODIFICATION, 1 );
313                 }
314                 manageTheSlotsAndAppointmentsImpacted( listAppointmentsImpacted, listSlotsImpacted, Boolean.TRUE, appointmentForm.getMaxCapacityPerSlot( ),
315                         Boolean.FALSE, Boolean.FALSE );
316             }
317             else
318             {
319                 // No check, delete all the slots
320                 SlotService.deleteListSlots( listSlotsImpacted );
321             }
322         }
323         FormService.updateAdvancedParameters( appointmentForm, dateOfModification );
324 
325         AppLogService.info( LogUtilities.buildLog( ACTION_MODIFY_ADVANCED_PARAMETERS, strIdForm, getUser( ) ) );
326         request.getSession( ).removeAttribute( SESSION_ATTRIBUTE_APPOINTMENT_FORM );
327         addInfo( INFO_ADVANCED_PARAMETERS_UPDATED, getLocale( ) );
328         ReservationRule reservationRule = ReservationRuleService.findReservationRuleByIdFormAndClosestToDateOfApply( nIdForm, dateOfModification );
329         WeekDefinition weekDefinition = WeekDefinitionService.findWeekDefinitionByIdFormAndDateOfApply( nIdForm, reservationRule.getDateOfApply( ) );
330         return redirect( request, VIEW_MANAGE_TYPICAL_WEEK, PARAMETER_ID_FORM, nIdForm, PARAMETER_ID_WEEK_DEFINITION, weekDefinition.getIdWeekDefinition( ) );
331     }
332 
333     /**
334      * Manages the removal form of a appointment whose identifier is in the HTTP request
335      * 
336      * @param request
337      *            The HTTP request
338      * @return the HTML code to confirm
339      */
340     @Action( ACTION_CONFIRM_REMOVE_PARAMETER )
341     public String getConfirmRemoveParameter( HttpServletRequest request )
342     {
343         UrlItem url = new UrlItem( getActionUrl( ACTION_REMOVE_PARAMETER ) );
344         url.addParameter( PARAMETER_ID_WEEK_DEFINITION, request.getParameter( PARAMETER_ID_WEEK_DEFINITION ) );
345         url.addParameter( PARAMETER_ID_FORM, request.getParameter( PARAMETER_ID_FORM ) );
346         String strMessageUrl = AdminMessageService.getMessageUrl( request, MESSAGE_CONFIRM_REMOVE_WEEK_DEFINITION, url.getUrl( ),
347                 AdminMessage.TYPE_CONFIRMATION );
348         return redirect( request, strMessageUrl );
349     }
350 
351     /**
352      * Handles the removal form of a appointment
353      * 
354      * @param request
355      *            The HTTP request
356      * @return the JSP URL to display the form to manage appointments
357      * @throws AccessDeniedException
358      *             If the user is not authorized to access this feature
359      */
360     @Action( ACTION_REMOVE_PARAMETER )
361     public String doRemoveParameter( HttpServletRequest request ) throws AccessDeniedException
362     {
363         String strIdForm = request.getParameter( PARAMETER_ID_FORM );
364         if ( !RBACService.isAuthorized( AppointmentFormDTO.RESOURCE_TYPE, strIdForm, AppointmentResourceIdService.PERMISSION_MODIFY_ADVANCED_SETTING_FORM,
365                 getUser( ) ) )
366         {
367             throw new AccessDeniedException( AppointmentResourceIdService.PERMISSION_MODIFY_ADVANCED_SETTING_FORM );
368         }
369         int nIdWeekDefinition = Integer.parseInt( request.getParameter( PARAMETER_ID_WEEK_DEFINITION ) );
370         int nIdForm = Integer.parseInt( request.getParameter( PARAMETER_ID_FORM ) );
371         WeekDefinition weekDefinitionToRemove = WeekDefinitionService.findWeekDefinitionById( nIdWeekDefinition );
372         // Check if there are other week definitions
373         List<WeekDefinition> listOfOtherWeekDefinitionOfTheForm = new ArrayList<>( );
374         listOfOtherWeekDefinitionOfTheForm.addAll( WeekDefinitionService.findAllWeekDefinition( nIdForm ).values( ) );
375         listOfOtherWeekDefinitionOfTheForm = listOfOtherWeekDefinitionOfTheForm.stream( ).filter( w -> w.getIdWeekDefinition( ) != nIdWeekDefinition )
376                 .collect( Collectors.toList( ) );
377         if ( CollectionUtils.isEmpty( listOfOtherWeekDefinitionOfTheForm ) )
378         {
379             addError( MESSAGE_ERROR_LAST_WEEK_DEFINITION, getLocale( ) );
380             return redirect( request, VIEW_MANAGE_TYPICAL_WEEK, PARAMETER_ID_FORM, nIdForm, PARAMETER_ID_WEEK_DEFINITION, nIdWeekDefinition );
381         }
382         // Check if there are appointments on this week definition
383         LocalDate beginDateOfApply = weekDefinitionToRemove.getDateOfApply( );
384         WeekDefinition nextWeekDefinition = WeekDefinitionService.findNextWeekDefinition( nIdForm, beginDateOfApply );
385         LocalDate endDateOfApply = LocalDate.MAX;
386         if ( nextWeekDefinition != null )
387         {
388             endDateOfApply = nextWeekDefinition.getDateOfApply( );
389         }
390         List<Slot> listSlotImpacted = SlotService.findSlotsByIdFormAndDateRange( nIdForm, beginDateOfApply.atTime( LocalTime.MIN ),
391                 endDateOfApply.atTime( LocalTime.MAX ) );
392         List<Appointment> listAppointment = AppointmentService.findListAppointmentByListSlot( listSlotImpacted );
393         if ( CollectionUtils.isNotEmpty( listAppointment ) )
394         {
395             addError( MESSAGE_ERROR_APPOINTMENT_ON_SLOT, getLocale( ) );
396             return redirect( request, VIEW_MANAGE_TYPICAL_WEEK, PARAMETER_ID_FORM, nIdForm, PARAMETER_ID_WEEK_DEFINITION, nIdWeekDefinition );
397         }
398         ReservationRule reservationRuleToRemove = ReservationRuleService.findReservationRuleByIdFormAndDateOfApply( nIdForm, beginDateOfApply );
399         ReservationRuleService.removeReservationRule( reservationRuleToRemove );
400         WeekDefinitionService.removeWeekDefinition( nIdWeekDefinition, nIdForm );
401         addInfo( INFO_PARAMETER_REMOVED, getLocale( ) );
402         return redirect( request, VIEW_MANAGE_TYPICAL_WEEK, PARAMETER_ID_FORM, nIdForm );
403     }
404 
405     /**
406      * Get the view to modify a time slot
407      * 
408      * @param request
409      *            the request
410      * @return the page
411      */
412     @View( VIEW_MODIFY_TIME_SLOT )
413     public String getViewModifyTimeSlot( HttpServletRequest request )
414     {
415         int nIdTimeSlot = Integer.parseInt( request.getParameter( PARAMETER_ID_TIME_SLOT ) );
416         TimeSlot timeSlot = (TimeSlot) request.getSession( ).getAttribute( SESSION_ATTRIBUTE_TIME_SLOT );
417         if ( ( timeSlot == null ) || ( nIdTimeSlot != timeSlot.getIdTimeSlot( ) ) )
418         {
419             timeSlot = TimeSlotService.findTimeSlotById( nIdTimeSlot );
420             request.getSession( ).setAttribute( SESSION_ATTRIBUTE_TIME_SLOT, timeSlot );
421         }
422         addInfo( MESSAGE_WARNING_CHANGES_APPLY_TO_ALL, getLocale( ) );
423         Map<String, Object> model = getModel( );
424         model.put( PARAMETER_ID_FORM, request.getParameter( PARAMETER_ID_FORM ) );
425         model.put( PARAMETER_ID_WEEK_DEFINITION, request.getParameter( PARAMETER_ID_WEEK_DEFINITION ) );
426         model.put( MARK_TIME_SLOT, timeSlot );
427         return getPage( MESSAGE_MODIFY_TIME_SLOT_PAGE_TITLE, TEMPLATE_MODIFY_TIME_SLOT, model );
428     }
429 
430     /**
431      * Do modify a time slot
432      * 
433      * @param request
434      *            the request
435      * @return to the page of the typical week
436      */
437     @Action( ACTION_DO_MODIFY_TIME_SLOT )
438     public String doModifyTimeSlot( HttpServletRequest request )
439     {
440         TimeSlot timeSlotFromSession = (TimeSlot) request.getSession( ).getAttribute( SESSION_ATTRIBUTE_TIME_SLOT );
441         String strIdForm = request.getParameter( PARAMETER_ID_FORM );
442         int nIdForm = Integer.parseInt( strIdForm );
443         String strIdWeekDefinition = request.getParameter( PARAMETER_ID_WEEK_DEFINITION );
444         int nIdWeekDefinition = Integer.parseInt( strIdWeekDefinition );
445         String strIdTimeSlot = request.getParameter( PARAMETER_ID_TIME_SLOT );
446         int nIdTimeSlot = Integer.parseInt( strIdTimeSlot );
447         if ( timeSlotFromSession == null || nIdTimeSlot != timeSlotFromSession.getIdTimeSlot( ) )
448         {
449             timeSlotFromSession = TimeSlotService.findTimeSlotById( nIdTimeSlot );
450         }
451         boolean bIsOpen = Boolean.parseBoolean( request.getParameter( PARAMETER_IS_OPEN ) );
452         boolean bOpeningHasChanged = false;
453         int nMaxCapacity = Integer.parseInt( request.getParameter( PARAMETER_MAX_CAPACITY ) );
454         LocalTime endingTime = LocalTime.parse( request.getParameter( PARAMETER_ENDING_TIME ) );
455         boolean bShiftSlot = Boolean.parseBoolean( request.getParameter( PARAMETER_SHIFT_SLOT ) );
456         boolean bEndingTimeHasChanged = false;
457         boolean bMaxCapacityHasChanged = false;
458         if ( bIsOpen != timeSlotFromSession.getIsOpen( ) )
459         {
460             timeSlotFromSession.setIsOpen( bIsOpen );
461             bOpeningHasChanged = true;
462         }
463         if ( nMaxCapacity != timeSlotFromSession.getMaxCapacity( ) )
464         {
465             timeSlotFromSession.setMaxCapacity( nMaxCapacity );
466             bMaxCapacityHasChanged = true;
467         }
468         LocalTime previousEndingTime = timeSlotFromSession.getEndingTime( );
469         if ( !endingTime.equals( previousEndingTime ) )
470         {
471             timeSlotFromSession.setEndingTime( endingTime );
472             if ( !checkEndingTimeOfTimeSlot( endingTime, timeSlotFromSession ) )
473             {
474                 Map<String, String> additionalParameters = new HashMap<>( );
475                 additionalParameters.put( PARAMETER_ID_FORM, strIdForm );
476                 additionalParameters.put( PARAMETER_ID_WEEK_DEFINITION, strIdWeekDefinition );
477                 additionalParameters.put( PARAMETER_ID_TIME_SLOT, strIdTimeSlot );
478                 request.getSession( ).setAttribute( SESSION_ATTRIBUTE_TIME_SLOT, timeSlotFromSession );
479                 return redirect( request, VIEW_MODIFY_TIME_SLOT, additionalParameters );
480             }
481             bEndingTimeHasChanged = true;
482         }
483         List<Slot> listSlotsImpacted = AppointmentUtilities.findSlotsImpactedByThisTimeSlot( timeSlotFromSession, nIdForm, nIdWeekDefinition, bShiftSlot );
484         List<Appointment> listAppointmentsImpacted = AppointmentService.findListAppointmentByListSlot( listSlotsImpacted );
485         // If there are slots impacted
486         if ( CollectionUtils.isNotEmpty( listSlotsImpacted ) )
487         {
488             // if there are appointments impacted
489             if ( CollectionUtils.isNotEmpty( listAppointmentsImpacted ) )
490             {
491                 // If the ending time of the time slot has changed or if the max
492                 // capacity has decreased
493                 if ( bEndingTimeHasChanged || nMaxCapacity < timeSlotFromSession.getMaxCapacity( ) )
494                 {
495                     // Error, the time slot can't be changed
496                     addError( MESSAGE_ERROR_APPOINTMENT_ON_SLOT, getLocale( ) );
497                     addError( listAppointmentsImpacted.size( ) + " rendez-vous impacté(s)" );
498                     addError( "dont un le " + SlotService.findSlotById( listAppointmentsImpacted.get( 0 ).getIdSlot( ) ).getStartingDateTime( ) );
499                     Map<String, String> additionalParameters = new HashMap<>( );
500                     additionalParameters.put( PARAMETER_ID_FORM, strIdForm );
501                     additionalParameters.put( PARAMETER_ID_WEEK_DEFINITION, strIdWeekDefinition );
502                     additionalParameters.put( PARAMETER_ID_TIME_SLOT, strIdTimeSlot );
503                     request.getSession( ).setAttribute( SESSION_ATTRIBUTE_TIME_SLOT, timeSlotFromSession );
504                     return redirect( request, VIEW_MODIFY_TIME_SLOT, additionalParameters );
505                 }
506                 // Get the validated appointment (the appointments that are not
507                 // cancelled)
508                 List<Appointment> listValidatedAppointments = listAppointmentsImpacted.stream( ).filter( appointment -> appointment.getIsCancelled( ) == false )
509                         .collect( Collectors.toList( ) );
510                 if ( bOpeningHasChanged && CollectionUtils.isNotEmpty( listValidatedAppointments ) )
511                 {
512                     addInfo( MESSAGE_INFO_VALIDATED_APPOINTMENTS_IMPACTED, getLocale( ) );
513                 }
514                 manageTheSlotsAndAppointmentsImpacted( listAppointmentsImpacted, listSlotsImpacted, bMaxCapacityHasChanged, nMaxCapacity, bOpeningHasChanged,
515                         bIsOpen );
516             }
517             else
518             {
519                 // no need to check appointments, delete all the slots
520                 SlotService.deleteListSlots( listSlotsImpacted );
521             }
522         }
523         TimeSlotService.updateTimeSlot( timeSlotFromSession, bEndingTimeHasChanged, previousEndingTime, bShiftSlot );
524 
525         AppLogService.info( LogUtilities.buildLog( ACTION_DO_MODIFY_TIME_SLOT, strIdTimeSlot, getUser( ) ) );
526         addInfo( MESSAGE_INFO_SLOT_UPDATED, getLocale( ) );
527         request.getSession( ).removeAttribute( SESSION_ATTRIBUTE_TIME_SLOT );
528         return redirect( request, VIEW_MANAGE_TYPICAL_WEEK, PARAMETER_ID_FORM, nIdForm, PARAMETER_ID_WEEK_DEFINITION, nIdWeekDefinition );
529     }
530 
531     /**
532      * Get the view of the specific week
533      * 
534      * @param request
535      *            the request
536      * @return the page
537      * @throws AccessDeniedException
538      */
539     @View( defaultView = true, value = VIEW_MANAGE_SPECIFIC_WEEK )
540     public String getViewManageSpecificWeek( HttpServletRequest request ) throws AccessDeniedException
541     {
542         request.getSession( ).removeAttribute( SESSION_ATTRIBUTE_SLOT );
543         int nIdForm = Integer.parseInt( request.getParameter( PARAMETER_ID_FORM ) );
544         Form form = FormService.findFormLightByPrimaryKey( nIdForm );
545         // Get the nb weeks to display
546         Display display = DisplayService.findDisplayWithFormId( nIdForm );
547         int nNbWeeksToDisplay = AppPropertiesService.getPropertyInt( PROPERTY_NB_WEEKS_TO_DISPLAY_IN_BO, display.getNbWeeksToDisplay( ) );
548         AppointmentFormDTO appointmentForm = (AppointmentFormDTO) request.getSession( ).getAttribute( SESSION_ATTRIBUTE_APPOINTMENT_FORM );
549         if ( ( appointmentForm == null ) || ( nIdForm != appointmentForm.getIdForm( ) ) )
550         {
551             appointmentForm = FormService.buildAppointmentForm( nIdForm, 0, 0 );
552         }
553         LocalDate dateOfDisplay = LocalDate.now( );
554         if ( appointmentForm.getDateStartValidity( ) != null && appointmentForm.getDateStartValidity( ).toLocalDate( ).isAfter( dateOfDisplay ) )
555         {
556             dateOfDisplay = appointmentForm.getDateStartValidity( ).toLocalDate( );
557         }
558         LocalDate endingDateOfDisplay = LocalDate.now( ).plusWeeks( nNbWeeksToDisplay );
559         LocalDate endingValidityDate = form.getEndingValidityDate( );
560         if ( endingValidityDate != null )
561         {
562             if ( endingDateOfDisplay.isAfter( endingValidityDate ) )
563             {
564                 endingDateOfDisplay = endingValidityDate;
565             }
566         }
567         // Get all the week definitions
568         HashMap<LocalDate, WeekDefinition> mapWeekDefinition = WeekDefinitionService.findAllWeekDefinition( nIdForm );
569         List<WeekDefinition> listWeekDefinition = new ArrayList<WeekDefinition>( mapWeekDefinition.values( ) );
570         // Get the min time of all the week definitions
571         LocalTime minStartingTime = WeekDefinitionService.getMinStartingTimeOfAListOfWeekDefinition( listWeekDefinition );
572         // Get the max time of all the week definitions
573         LocalTime maxEndingTime = WeekDefinitionService.getMaxEndingTimeOfAListOfWeekDefinition( listWeekDefinition );
574         // Get all the working days of all the week definitions
575         List<String> listDayOfWeek = new ArrayList<>( WeekDefinitionService.getSetDaysOfWeekOfAListOfWeekDefinitionForFullCalendar( listWeekDefinition ) );
576         // Build the slots
577         List<Slot> listSlot = SlotService.buildListSlot( nIdForm, mapWeekDefinition, dateOfDisplay, endingDateOfDisplay );
578         listSlot = listSlot.stream( ).filter( s -> s.getEndingDateTime( ).isAfter( LocalDateTime.now( ) ) ).collect( Collectors.toList( ) );
579         String strDateOfDisplay = request.getParameter( PARAMETER_DATE_OF_DISPLAY );
580         if ( StringUtils.isNotEmpty( strDateOfDisplay ) )
581         {
582             dateOfDisplay = LocalDate.parse( strDateOfDisplay );
583         }
584         addInfo( MESSAGE_INFO_OVERLOAD, getLocale( ) );
585         Map<String, Object> model = getModel( );
586         model.put( PARAMETER_DATE_OF_DISPLAY, dateOfDisplay );
587         model.put( PARAMETER_ENDING_DATE_OF_DISPLAY, endingDateOfDisplay );
588         model.put( PARAMETER_DAY_OF_WEEK, listDayOfWeek );
589         model.put( PARAMETER_EVENTS, listSlot );
590         model.put( PARAMETER_MIN_TIME, minStartingTime );
591         model.put( PARAMETER_MAX_TIME, maxEndingTime );
592         model.put( PARAMETER_MIN_DURATION, LocalTime.MIN.plusMinutes( AppointmentUtilities.THIRTY_MINUTES ) );
593         model.put( PARAMETER_ID_FORM, nIdForm );
594         AppointmentFormJspBean.addElementsToModel( request, appointmentForm, getUser( ), getLocale( ), model );
595         return getPage( MESSAGE_SPECIFIC_WEEK_PAGE_TITLE, TEMPLATE_MANAGE_SPECIFIC_WEEK, model );
596     }
597 
598     /**
599      * Get the view to modify a slot
600      * 
601      * @param request
602      *            the request
603      * @return the page
604      */
605     @View( VIEW_MODIFY_SLOT )
606     public String getViewModifySlot( HttpServletRequest request )
607     {
608         String strIdForm = request.getParameter( PARAMETER_ID_FORM );
609         int nIdForm = Integer.parseInt( strIdForm );
610         Slot slot = (Slot) request.getSession( ).getAttribute( SESSION_ATTRIBUTE_SLOT );
611         if ( slot == null )
612         {
613             int nIdSlot = Integer.parseInt( request.getParameter( PARAMETER_ID_SLOT ) );
614             // If nIdSlot == 0, the slot has not been created yet
615             if ( nIdSlot == 0 )
616             {
617                 // Need to get all the informations to create the slot
618                 LocalDateTime startingDateTime = LocalDateTime.parse( request.getParameter( PARAMETER_STARTING_DATE_TIME ) );
619                 LocalDateTime endingDateTime = LocalDateTime.parse( request.getParameter( PARAMETER_ENDING_DATE_TIME ) );
620                 boolean bIsOpen = Boolean.parseBoolean( request.getParameter( PARAMETER_IS_OPEN ) );
621                 boolean bIsSpecific = Boolean.parseBoolean( request.getParameter( PARAMETER_IS_SPECIFIC ) );
622                 int nMaxCapacity = Integer.parseInt( request.getParameter( PARAMETER_MAX_CAPACITY ) );
623                 slot = SlotService.buildSlot( nIdForm, new Period( startingDateTime, endingDateTime ), nMaxCapacity, nMaxCapacity, nMaxCapacity, 0, bIsOpen,
624                         bIsSpecific );
625             }
626             else
627             {
628                 slot = SlotService.findSlotById( nIdSlot );
629             }
630             request.getSession( ).setAttribute( SESSION_ATTRIBUTE_SLOT, slot );
631         }
632         Map<String, Object> model = getModel( );
633         model.put( PARAMETER_DATE_OF_DISPLAY, slot.getDate( ) );
634         model.put( MARK_SLOT, slot );
635         return getPage( MESSAGE_MODIFY_SLOT_PAGE_TITLE, TEMPLATE_MODIFY_SLOT, model );
636     }
637 
638     /**
639      * Do modify a slot
640      * 
641      * @param request
642      *            the request
643      * @return to the page of the specific week
644      */
645     @Action( ACTION_DO_MODIFY_SLOT )
646     public String doModifySlot( HttpServletRequest request )
647     {
648         Slot slotFromSessionOrFromDb = null;
649         String strIdSlot = request.getParameter( PARAMETER_ID_SLOT );
650         int nIdSlot = Integer.parseInt( strIdSlot );
651         if ( nIdSlot != 0 )
652         {
653             slotFromSessionOrFromDb = SlotService.findSlotById( nIdSlot );
654         }
655         else
656         {
657             slotFromSessionOrFromDb = (Slot) request.getSession( ).getAttribute( SESSION_ATTRIBUTE_SLOT );
658         }
659         LocalTime endingTime = LocalTime.parse( request.getParameter( PARAMETER_ENDING_TIME ) );
660         boolean bIsOpen = Boolean.parseBoolean( request.getParameter( PARAMETER_IS_OPEN ) );
661         int nMaxCapacity = Integer.parseInt( request.getParameter( PARAMETER_MAX_CAPACITY ) );
662         boolean bEndingTimeHasChanged = false;
663         boolean bOpeningHasChanged = false;
664         boolean bShiftSlot = Boolean.parseBoolean( request.getParameter( PARAMETER_SHIFT_SLOT ) );
665         if ( bIsOpen != slotFromSessionOrFromDb.getIsOpen( ) )
666         {
667             slotFromSessionOrFromDb.setIsOpen( bIsOpen );
668             bOpeningHasChanged = true;
669         }
670 
671         // If we edit the slot, we need to check if this slot is not a closing
672         // day
673         ClosingDay closingDay = ClosingDayService.findClosingDayByIdFormAndDateOfClosingDay( slotFromSessionOrFromDb.getIdForm( ),
674                 slotFromSessionOrFromDb.getDate( ) );
675         if ( closingDay != null )
676         {
677             // If the slot is a closing day, we need to remove it from the table
678             // closing day so that the slot is not in conflict with the
679             // definition of the closing days
680             ClosingDayService.removeClosingDay( closingDay );
681         }
682         if ( nMaxCapacity != slotFromSessionOrFromDb.getMaxCapacity( ) )
683         {
684             slotFromSessionOrFromDb.setMaxCapacity( nMaxCapacity );
685             // Need to set also the nb remaining places and the nb potential
686             // remaining places
687             // If the slot already exist, the good values will be set at the
688             // update of the slot with taking the old values
689             // If it is a new slot, the value set here will be good
690             slotFromSessionOrFromDb.setNbRemainingPlaces( nMaxCapacity );
691             slotFromSessionOrFromDb.setNbPotentialRemainingPlaces( nMaxCapacity );
692         }
693         LocalTime previousEndingTime = slotFromSessionOrFromDb.getEndingTime( );
694         if ( !endingTime.equals( previousEndingTime ) )
695         {
696             slotFromSessionOrFromDb.setEndingTime( endingTime );
697             slotFromSessionOrFromDb.setEndingDateTime( slotFromSessionOrFromDb.getDate( ).atTime( endingTime ) );
698             bEndingTimeHasChanged = true;
699         }
700         if ( bEndingTimeHasChanged && !checkNoAppointmentsOnThisSlotOrOnTheSlotsImpacted( slotFromSessionOrFromDb, bShiftSlot ) || bEndingTimeHasChanged
701                 && !checkEndingTimeOfSlot( endingTime, slotFromSessionOrFromDb ) )
702         {
703             request.getSession( ).setAttribute( SESSION_ATTRIBUTE_SLOT, slotFromSessionOrFromDb );
704             return redirect( request, VIEW_MODIFY_SLOT, PARAMETER_ID_FORM, slotFromSessionOrFromDb.getIdForm( ) );
705         }
706         SlotService.updateSlot( slotFromSessionOrFromDb, bEndingTimeHasChanged, previousEndingTime, bShiftSlot );
707         AppLogService.info( LogUtilities.buildLog( ACTION_DO_MODIFY_SLOT, strIdSlot, getUser( ) ) );
708         addInfo( MESSAGE_INFO_SLOT_UPDATED, getLocale( ) );
709         boolean appointmentsImpacted = !AppointmentUtilities.checkNoValidatedAppointmentsOnThisSlot( slotFromSessionOrFromDb );
710         if ( appointmentsImpacted && bOpeningHasChanged )
711         {
712             addInfo( MESSAGE_INFO_VALIDATED_APPOINTMENTS_IMPACTED, getLocale( ) );
713         }
714         if ( appointmentsImpacted && nMaxCapacity < slotFromSessionOrFromDb.getNbPlacesTaken( ) )
715         {
716             addInfo( MESSAGE_INFO_SURBOOKING, getLocale( ) );
717         }
718         request.getSession( ).removeAttribute( SESSION_ATTRIBUTE_SLOT );
719         Map<String, String> additionalParameters = new HashMap<>( );
720         additionalParameters.put( PARAMETER_ID_FORM, Integer.toString( slotFromSessionOrFromDb.getIdForm( ) ) );
721         additionalParameters.put( PARAMETER_DATE_OF_DISPLAY, slotFromSessionOrFromDb.getDate( ).toString( ) );
722         return redirect( request, VIEW_MANAGE_SPECIFIC_WEEK, additionalParameters );
723     }
724 
725     /**
726      * Check the ending time of a time slot
727      * 
728      * @param endingTime
729      *            the new ending time
730      * @param timeSlot
731      *            the time slot
732      * @return false if there is an error
733      */
734     private boolean checkEndingTimeOfTimeSlot( LocalTime endingTime, TimeSlot timeSlot )
735     {
736         boolean bReturn = true;
737         WorkingDay workingDay = WorkingDayService.findWorkingDayById( timeSlot.getIdWorkingDay( ) );
738         if ( endingTime.isAfter( WorkingDayService.getMaxEndingTimeOfAWorkingDay( workingDay ) ) )
739         {
740             bReturn = false;
741             addError( MESSAGE_SLOT_CAN_NOT_END_AFTER_DAY_OR_FORM, getLocale( ) );
742         }
743         if ( endingTime.isBefore( timeSlot.getStartingTime( ) ) || endingTime.equals( timeSlot.getStartingTime( ) ) )
744         {
745             bReturn = false;
746             addError( MESSAGE_ERROR_TIME_END_BEFORE_TIME_START, getLocale( ) );
747         }
748         return bReturn;
749     }
750 
751     /**
752      * Check the ending time of a slot
753      * 
754      * @param endingTime
755      *            the new ending time
756      * @param slot
757      *            the slot
758      * @return false if there is an error
759      */
760     private boolean checkEndingTimeOfSlot( LocalTime endingTime, Slot slot )
761     {
762         boolean bReturn = true;
763         LocalDate dateOfSlot = slot.getDate( );
764         WeekDefinition weekDefinition = WeekDefinitionService.findWeekDefinitionByIdFormAndClosestToDateOfApply( slot.getIdForm( ), dateOfSlot );
765         WorkingDay workingDay = WorkingDayService.getWorkingDayOfDayOfWeek( weekDefinition.getListWorkingDay( ), dateOfSlot.getDayOfWeek( ) );
766         LocalTime maxEndingTime = null;
767         if ( workingDay == null )
768         {
769             maxEndingTime = WorkingDayService.getMaxEndingTimeOfAListOfWorkingDay( weekDefinition.getListWorkingDay( ) );
770         }
771         else
772         {
773             maxEndingTime = WorkingDayService.getMaxEndingTimeOfAWorkingDay( workingDay );
774         }
775         if ( endingTime.isAfter( maxEndingTime ) )
776         {
777             bReturn = false;
778             addError( MESSAGE_SLOT_CAN_NOT_END_AFTER_DAY_OR_FORM, getLocale( ) );
779         }
780         if ( endingTime.isBefore( slot.getStartingTime( ) ) || endingTime.equals( slot.getStartingTime( ) ) )
781         {
782             bReturn = false;
783             addError( MESSAGE_ERROR_TIME_END_BEFORE_TIME_START, getLocale( ) );
784         }
785         return bReturn;
786     }
787 
788     /**
789      * Check that there is no appointment on a slot or on the impacted slots that will be modified
790      * 
791      * @param slot
792      *            the slot
793      * @param bShiftSLot
794      *            true if the next slots will be modified
795      * @return false if there is an error
796      */
797     private boolean checkNoAppointmentsOnThisSlotOrOnTheSlotsImpacted( Slot slot, boolean bShiftSLot )
798     {
799         boolean bReturn = true;
800         LocalDateTime endingDateTime = slot.getEndingDateTime( );
801         // If all the slot will be shifted,
802         // Need to check if there is no appointment until the end of the day
803         if ( bShiftSLot )
804         {
805             endingDateTime = slot.getDate( ).atTime( LocalTime.MAX );
806         }
807         List<Slot> listSlotImpacted = SlotService.findSlotsByIdFormAndDateRange( slot.getIdForm( ), slot.getStartingDateTime( ), endingDateTime );
808         List<Appointment> listAppointment = AppointmentService.findListAppointmentByListSlot( listSlotImpacted );
809         if ( CollectionUtils.isNotEmpty( listAppointment ) )
810         {
811             bReturn = false;
812             addError( MESSAGE_ERROR_APPOINTMENT_ON_SLOT, getLocale( ) );
813         }
814         return bReturn;
815     }
816 
817     /**
818      * Update the slots with appointments impacted by a modification of a typical week or a modification of a timeSlot Delete the slots with no appointments
819      * 
820      * @param listAppointmentsImpacted
821      *            the appointments impacted
822      * @param listSlotsImpacted
823      *            the slots impacted
824      * @param bMaxCapacityHasChanged
825      *            True if the capacity has changed
826      * @param nMaxCapacity
827      *            the max capacity
828      * @param bOpeningHasChanged
829      *            true if the opening has changed
830      * @param bIsOpen
831      *            the new boolean opening value
832      */
833     private void manageTheSlotsAndAppointmentsImpacted( List<Appointment> listAppointmentsImpacted, List<Slot> listSlotsImpacted,
834             boolean bMaxCapacityHasChanged, int nMaxCapacity, boolean bOpeningHasChanged, boolean bIsOpen )
835     {
836         // Need to delete the slots that are impacted but with no
837         // appointments
838         HashSet<Integer> setSlotsImpactedWithAppointments = new HashSet<>( );
839         for ( Appointment appointment : listAppointmentsImpacted )
840         {
841             setSlotsImpactedWithAppointments.add( appointment.getIdSlot( ) );
842         }
843         List<Slot> listSlotsImpactedWithoutAppointments = listSlotsImpacted.stream( )
844                 .filter( slot -> !setSlotsImpactedWithAppointments.contains( slot.getIdSlot( ) ) ).collect( Collectors.toList( ) );
845         List<Slot> listSlotsImpactedWithAppointments = listSlotsImpacted.stream( )
846                 .filter( slot -> setSlotsImpactedWithAppointments.contains( slot.getIdSlot( ) ) ).collect( Collectors.toList( ) );
847 
848         SlotService.deleteListSlots( listSlotsImpactedWithoutAppointments );
849 
850         for ( Slot slotImpacted : listSlotsImpactedWithAppointments )
851         {
852             // If the max capacity has changed,
853             // need to update it for all the slots that already have
854             // appointments
855             if ( bMaxCapacityHasChanged )
856             {
857                 slotImpacted.setMaxCapacity( nMaxCapacity );
858                 SlotService.updateRemainingPlaces( slotImpacted );
859             }
860             // if the opening of the timeslot has changed and there are
861             // appointments impacted,
862             // all the corresponding slots are marked as specific
863             if ( bOpeningHasChanged )
864             {
865                 slotImpacted.setIsSpecific( bIsOpen );
866             }
867             SlotService.updateSlot( slotImpacted );
868         }
869     }
870 
871 }