View Javadoc
1   /*
2    * Copyright (c) 2002-2022, City of 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.io.IOException;
37  import java.time.LocalDate;
38  import java.time.LocalTime;
39  import java.util.ArrayList;
40  import java.util.HashMap;
41  import java.util.List;
42  import java.util.Map;
43  import java.util.concurrent.locks.Lock;
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.lang3.StringUtils;
50  
51  import com.fasterxml.jackson.core.type.TypeReference;
52  import com.fasterxml.jackson.databind.DeserializationFeature;
53  import com.fasterxml.jackson.databind.ObjectMapper;
54  import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
55  
56  import fr.paris.lutece.api.user.User;
57  import fr.paris.lutece.plugins.appointment.business.planning.TimeSlot;
58  import fr.paris.lutece.plugins.appointment.business.planning.WeekDefinition;
59  import fr.paris.lutece.plugins.appointment.business.planning.WorkingDay;
60  import fr.paris.lutece.plugins.appointment.business.rule.ReservationRule;
61  import fr.paris.lutece.plugins.appointment.business.rule.ReservationRuleHome;
62  import fr.paris.lutece.plugins.appointment.business.slot.Slot;
63  import fr.paris.lutece.plugins.appointment.business.slot.SlotHome;
64  import fr.paris.lutece.plugins.appointment.log.LogUtilities;
65  import fr.paris.lutece.plugins.appointment.service.AppointmentResourceIdService;
66  import fr.paris.lutece.plugins.appointment.service.AppointmentUtilities;
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.SlotSafeService;
70  import fr.paris.lutece.plugins.appointment.service.SlotService;
71  import fr.paris.lutece.plugins.appointment.service.TimeSlotService;
72  import fr.paris.lutece.plugins.appointment.service.WeekDefinitionService;
73  import fr.paris.lutece.plugins.appointment.service.WorkingDayService;
74  import fr.paris.lutece.plugins.appointment.service.listeners.WeekDefinitionManagerListener;
75  import fr.paris.lutece.plugins.appointment.web.dto.AppointmentFormDTO;
76  import fr.paris.lutece.portal.service.admin.AccessDeniedException;
77  import fr.paris.lutece.portal.service.message.AdminMessage;
78  import fr.paris.lutece.portal.service.message.AdminMessageService;
79  import fr.paris.lutece.portal.service.rbac.RBACService;
80  import fr.paris.lutece.portal.service.util.AppLogService;
81  import fr.paris.lutece.portal.util.mvc.admin.annotations.Controller;
82  import fr.paris.lutece.portal.util.mvc.commons.annotations.Action;
83  import fr.paris.lutece.portal.util.mvc.commons.annotations.View;
84  import fr.paris.lutece.util.url.UrlItem;
85  
86  /**
87   * JspBean to manage calendar slots
88   * 
89   * @author Laurent Payen
90   *
91   */
92  @Controller( controllerJsp = TypicalWeekJspBean.JSP_MANAGE_APPOINTMENT_SLOTS, controllerPath = "jsp/admin/plugins/appointment/", right = AppointmentFormJspBean.RIGHT_MANAGEAPPOINTMENTFORM )
93  public class TypicalWeekJspBean extends AbstractAppointmentFormAndSlotJspBean
94  {
95      /**
96       * JSP of this JSP Bean
97       */
98      public static final String JSP_MANAGE_APPOINTMENT_SLOTS = "ManageAppointmentSlots.jsp";
99  
100     /**
101      * Serial version UID
102      */
103     private static final long serialVersionUID = 2376721852596997810L;
104 
105     // Messages
106     private static final String MESSAGE_TYPICAL_WEEK_PAGE_TITLE = "appointment.typicalWeek.pageTitle";
107     private static final String MESSAGE_MODIFY_TIME_SLOT_PAGE_TITLE = "appointment.modifyCalendarSlots.pageTitle";
108     private static final String MESSAGE_WARNING_CHANGES_APPLY_TO_ALL = "appointment.modifyCalendarSlots.warningModifiyingEndingTime";
109     private static final String MESSAGE_ERROR_TIME_END_BEFORE_TIME_START = "appointment.modifyCalendarSlots.errorTimeEndBeforeTimeStart";
110     private static final String MESSAGE_SLOT_CAN_NOT_END_AFTER_DAY_OR_FORM = "appointment.message.error.slotCanNotEndAfterDayOrForm";
111     private static final String MESSAGE_ERROR_APPOINTMENT_ON_SLOT = "appointment.message.error.appointmentOnSlot";
112     private static final String MESSAGE_INFO_SLOT_UPDATED = "appointment.modifyCalendarSlots.messageSlotUpdated";
113     private static final String MESSAGE_INFO_VALIDATED_APPOINTMENTS_IMPACTED = "appointment.modifyCalendarSlots.messageValidatedAppointmentsImpacted";
114 
115     private static final String MESSAGE_ERROR_MODIFY_FORM_HAS_APPOINTMENTS_AFTER_DATE_OF_MODIFICATION = "appointment.message.error.refreshDays.modifyFormHasAppointments";
116     private static final String VALIDATION_ATTRIBUTES_PREFIX = "appointment.model.entity.appointmentform.attribute.";
117     private static final String MESSAGE_CONFIRM_REMOVE_WEEK_DEFINITION = "appointment.message.confirmRemoveWeekDefinition";
118     private static final String MESSAGE_ERROR_RULE_ASSIGNED = "appointment.message.error.rule.assigned";
119     private static final String MESSAGE_ERROR_MODIFICATION_WEEK_ASSIGNED_IN_PAST = "appointment.message.error.week.assigned.past";
120     private static final String MESSAGE_ERROR_PARSING_JSON = "appointment.message.error.parsing.json";
121 
122     // Parameters
123     private static final String PARAMETER_ID_FORM = "id_form";
124     private static final String PARAMETER_ID_TIME_SLOT = "id_time_slot";
125     private static final String PARAMETER_DAY_OF_WEEK = "dow";
126     private static final String PARAMETER_EVENTS = "events";
127     private static final String PARAMETER_MIN_DURATION = "min_duration";
128     private static final String PARAMETER_MIN_TIME = "min_time";
129     private static final String PARAMETER_MAX_TIME = "max_time";
130     private static final String PARAMETER_IS_OPEN = "is_open";
131     private static final String PARAMETER_ENDING_TIME = "ending_time";
132     private static final String PARAMETER_MAX_CAPACITY = "max_capacity";
133     private static final String PARAMETER_ID_RULE = "id_reservation_rule";
134     private static final String PARAMETER_TIME_SLOT_DATA = "timeSlotData";
135     private static final String PARAMETER_SHIFT_SLOT = "shift_slot";
136 
137     // Marks
138     private static final String MARK_TIME_SLOT = "timeSlot";
139     private static final String MARK_LIST_RESERVATION_RULE = "listReservationRule";
140     private static final String MARK_ID_RULE = "id_reservation_rule";
141     private static final String CAN_UPDATE_ADVANCED_PARAM = "canUpdateAdvancedParam";
142 
143     // Views
144     private static final String VIEW_MANAGE_TYPICAL_WEEK = "manageTypicalWeek";
145     private static final String VIEW_MODIFY_TIME_SLOT = "viewModifyTimeSlot";
146 
147     // Actions
148     private static final String ACTION_DO_MODIFY_TIME_SLOT = "doModifyTimeSlot";
149     private static final String ACTION_DO_MODIFY_LIST_TIME_SLOT = "doModifyListTimeSlot";
150     private static final String ACTION_MODIFY_ADVANCED_PARAMETERS = "modifyAdvancedParameters";
151     private static final String ACTION_MODIFY_GLOBAL_PARAM = "modifyGlobalParameters";
152     private static final String ACTION_CONFIRM_REMOVE_PARAMETER = "confirmRemoveParameter";
153     private static final String ACTION_REMOVE_PARAMETER = "doRemoveParameter";
154     private static final String ACTION_CREATE_TYPICAL_WEEK = "createTypicalWeek";
155     private static final String ACTION_DO_COPY_WEEK = "copyTypicalWeek";
156 
157     // Templates
158     private static final String TEMPLATE_MANAGE_TYPICAL_WEEK = "admin/plugins/appointment/slots/manage_typical_week.html";
159     private static final String TEMPLATE_MODIFY_TIME_SLOT = "admin/plugins/appointment/slots/modify_time_slot.html";
160 
161     // Porperties
162 
163     // Infos
164     private static final String INFO_ADVANCED_PARAMETERS_UPDATED = "appointment.info.advancedparameters.updated";
165     private static final String INFO_GLOBAL_PARAMETERS_UPDATED = "appointment.info.globalparameters.updated";
166 
167     private static final String INFO_PARAMETER_REMOVED = "appointment.info.advancedparameters.removed";
168 
169     private AppointmentFormDTO _appointmentForm;
170     private TimeSlot _timeSlot;
171 
172     /**
173      * Get the view of the typical week
174      * 
175      * @param request
176      *            the request
177      * @return the page
178      * @throws AccessDeniedException
179      */
180     @View( value = VIEW_MANAGE_TYPICAL_WEEK )
181     public String getViewManageTypicalWeek( HttpServletRequest request ) throws AccessDeniedException
182     {
183         _timeSlot = null;
184         boolean bCanUpdateAdvancedParam = true;
185         String strIdForm = request.getParameter( PARAMETER_ID_FORM );
186         int nIdForm = Integer.parseInt( strIdForm );
187         String strIdReservationRule = request.getParameter( PARAMETER_ID_RULE );
188         if ( !RBACService.isAuthorized( AppointmentFormDTO.RESOURCE_TYPE, strIdForm, AppointmentResourceIdService.PERMISSION_MODIFY_ADVANCED_SETTING_FORM,
189                 (User) getUser( ) ) )
190         {
191             throw new AccessDeniedException( AppointmentResourceIdService.PERMISSION_MODIFY_ADVANCED_SETTING_FORM );
192         }
193         int nIdReservationRule = 0;
194         if ( StringUtils.isNotEmpty( strIdReservationRule ) )
195         {
196             nIdReservationRule = Integer.parseInt( strIdReservationRule );
197         }
198         LocalDate dateOfApply = LocalDate.now( );
199         ReservationRule reservationRule;
200         if ( nIdReservationRule != 0 )
201         {
202             reservationRule = ReservationRuleService.findReservationRuleById( nIdReservationRule );
203             LocalDate dateNow = LocalDate.now( );
204             List<WeekDefinition> listWeekDefinition = WeekDefinitionService.findByReservationRule( nIdReservationRule );
205             if ( listWeekDefinition.stream( ).anyMatch( week -> week.getDateOfApply( ).isBefore( dateNow ) ) )
206             {
207 
208                 bCanUpdateAdvancedParam = false;
209             }
210         }
211         else
212         {
213             reservationRule = new ReservationRule( );
214         }
215 
216         Map<String, Object> model = getModel( );
217         List<String> listDayOfWeek = new ArrayList<>( );
218         List<TimeSlot> listTimeSlot = new ArrayList<>( );
219         LocalTime minStartingTime = LocalTime.MIN;
220         LocalTime maxEndingTime = LocalTime.MAX;
221 
222         if ( nIdReservationRule == 0
223                 && ( _appointmentForm == null || _appointmentForm.getIdReservationRule( ) != 0 || _appointmentForm.getIdForm( ) != nIdForm ) )
224         {
225             _appointmentForm = FormService.buildAppointmentFormWithoutReservationRule( nIdForm );
226 
227         }
228         else
229             if ( nIdReservationRule != 0 )
230             {
231                 _appointmentForm = FormService.buildAppointmentForm( nIdForm, nIdReservationRule );
232                 List<WorkingDay> listWorkingDay = reservationRule.getListWorkingDay( );
233                 listDayOfWeek = new ArrayList<>( WorkingDayService.getSetDaysOfWeekOfAListOfWorkingDayForFullCalendar( listWorkingDay ) );
234                 listTimeSlot = TimeSlotService.getListTimeSlotOfAListOfWorkingDay( listWorkingDay, dateOfApply );
235                 minStartingTime = WorkingDayService.getMinStartingTimeOfAListOfWorkingDay( listWorkingDay );
236                 maxEndingTime = WorkingDayService.getMaxEndingTimeOfAListOfWorkingDay( listWorkingDay );
237             }
238 
239         model.put( CAN_UPDATE_ADVANCED_PARAM, bCanUpdateAdvancedParam );
240         model.put( PARAMETER_DAY_OF_WEEK, listDayOfWeek );
241         model.put( PARAMETER_EVENTS, listTimeSlot );
242         model.put( PARAMETER_MIN_TIME, minStartingTime );
243         model.put( PARAMETER_MAX_TIME, maxEndingTime );
244         model.put( PARAMETER_MIN_DURATION, LocalTime.MIN.plusMinutes( AppointmentUtilities.THIRTY_MINUTES ) );
245         model.put( MARK_ID_RULE, nIdReservationRule );
246         model.put( MARK_LIST_RESERVATION_RULE, ReservationRuleService.findListReservationRule( nIdForm ) );
247         addElementsToModel( _appointmentForm, getUser( ), getLocale( ), model );
248         return getPage( MESSAGE_TYPICAL_WEEK_PAGE_TITLE, TEMPLATE_MANAGE_TYPICAL_WEEK, model );
249     }
250 
251     /**
252      * Create typical week
253      * 
254      * @param request
255      *            the request
256      * @return Html Page
257      * @throws AccessDeniedException
258      */
259     @Action( ACTION_CREATE_TYPICAL_WEEK )
260     public String doCreateTypicalWeek( HttpServletRequest request ) throws AccessDeniedException
261     {
262         String strIdForm = request.getParameter( PARAMETER_ID_FORM );
263         int nIdForm = Integer.parseInt( strIdForm );
264         if ( !RBACService.isAuthorized( AppointmentFormDTO.RESOURCE_TYPE, strIdForm, AppointmentResourceIdService.PERMISSION_MODIFY_ADVANCED_SETTING_FORM,
265                 (User) getUser( ) ) )
266         {
267             throw new AccessDeniedException( AppointmentResourceIdService.PERMISSION_MODIFY_ADVANCED_SETTING_FORM );
268         }
269         populate( _appointmentForm, request );
270         _appointmentForm.setCalendarTemplateId( 1 );
271         if ( !validateBean( _appointmentForm, VALIDATION_ATTRIBUTES_PREFIX ) || !validateReservationRuleBean( request, VALIDATION_ATTRIBUTES_PREFIX )
272                 || !checkConstraints( _appointmentForm ) )
273         {
274             addError( PARAMETER_ERROR_MODIFICATION );
275             return redirect( request, VIEW_MANAGE_TYPICAL_WEEK, PARAMETER_ID_FORM, nIdForm, PARAMETER_ID_RULE, _appointmentForm.getIdReservationRule( ) );
276         }
277 
278         int nIdreservationRule = ReservationRuleService.createTypicalWeek( _appointmentForm );
279         AppLogService.info( LogUtilities.buildLog( ACTION_MODIFY_ADVANCED_PARAMETERS, strIdForm, getUser( ) ) );
280         addInfo( INFO_ADVANCED_PARAMETERS_UPDATED, getLocale( ) );
281         return redirect( request, VIEW_MANAGE_TYPICAL_WEEK, PARAMETER_ID_FORM, nIdForm, PARAMETER_ID_RULE, nIdreservationRule );
282     }
283 
284     /**
285      * Modify typical week
286      * 
287      * @param request
288      *            the request
289      * @return the page
290      * @throws AccessDeniedException
291      */
292     @Action( ACTION_MODIFY_ADVANCED_PARAMETERS )
293     public String doModifyAdvancedParameters( HttpServletRequest request ) throws AccessDeniedException
294     {
295 
296         if ( !RBACService.isAuthorized( AppointmentFormDTO.RESOURCE_TYPE, String.valueOf( _appointmentForm.getIdForm( ) ),
297                 AppointmentResourceIdService.PERMISSION_MODIFY_ADVANCED_SETTING_FORM, (User) getUser( ) ) )
298         {
299             throw new AccessDeniedException( AppointmentResourceIdService.PERMISSION_MODIFY_ADVANCED_SETTING_FORM );
300         }
301         populate( _appointmentForm, request );
302         List<Slot> listSlotsImpacted = new ArrayList<>( );
303         List<Slot> listSlotsImpactedWithAppointment = new ArrayList<>( );
304 
305         LocalDate dateNow = LocalDate.now( );
306         List<WeekDefinition> listWeekDefinition = WeekDefinitionService.findByReservationRule( _appointmentForm.getIdReservationRule( ) );
307         if ( listWeekDefinition.stream( ).anyMatch( week -> week.getDateOfApply( ).isBefore( dateNow ) ) )
308         {
309 
310             return redirect( request, AdminMessageService.getMessageUrl( request, MESSAGE_ERROR_MODIFICATION_WEEK_ASSIGNED_IN_PAST, AdminMessage.TYPE_STOP ) );
311         }
312         if ( AppointmentUtilities.weekIsOpenInFO( _appointmentForm, listWeekDefinition, getLocale( ) ) )
313         {
314 
315             addError( ERROR_MESSAGE_WEEK_IS_OPEN_FO, getLocale( ) );
316             return redirect( request, VIEW_MANAGE_TYPICAL_WEEK, PARAMETER_ID_FORM, _appointmentForm.getIdForm( ), PARAMETER_ID_RULE,
317                     _appointmentForm.getIdReservationRule( ) );
318 
319         }
320         if ( !validateReservationRuleBean( _appointmentForm, VALIDATION_ATTRIBUTES_PREFIX ) || !validateBean( _appointmentForm, VALIDATION_ATTRIBUTES_PREFIX )
321                 || !checkConstraints( _appointmentForm ) )
322         {
323             addError( PARAMETER_ERROR_MODIFICATION );
324             return redirect( request, VIEW_MANAGE_TYPICAL_WEEK, PARAMETER_ID_FORM, _appointmentForm.getIdForm( ), PARAMETER_ID_RULE,
325                     _appointmentForm.getIdReservationRule( ) );
326         }
327 
328         for ( WeekDefinition week : listWeekDefinition )
329         {
330 
331             listSlotsImpacted.addAll( SlotService.findSlotsByIdFormAndDateRange( _appointmentForm.getIdForm( ), week.getDateOfApply( ).atStartOfDay( ),
332                     week.getEndingDateOfApply( ).atTime( LocalTime.MAX ) ) );
333             listSlotsImpactedWithAppointment.addAll( SlotService.findSlotWithAppointmentByDateRange( _appointmentForm.getIdForm( ),
334                     week.getDateOfApply( ).atStartOfDay( ), week.getEndingDateOfApply( ).atTime( LocalTime.MAX ) ) );
335 
336         }
337 
338         // if there are slots impacted
339         if ( CollectionUtils.isNotEmpty( listSlotsImpacted ) )
340         {
341             // if there are appointments impacted
342             if ( CollectionUtils.isNotEmpty( listSlotsImpactedWithAppointment ) )
343             {
344 
345                 if ( !AppointmentUtilities.checkNoAppointmentsImpacted( listSlotsImpactedWithAppointment, _appointmentForm ) )
346                 {
347                     addError( MESSAGE_ERROR_MODIFY_FORM_HAS_APPOINTMENTS_AFTER_DATE_OF_MODIFICATION, getLocale( ) );
348                     return redirect( request, VIEW_MANAGE_TYPICAL_WEEK, PARAMETER_ID_FORM, _appointmentForm.getIdForm( ), PARAMETER_ID_RULE,
349                             _appointmentForm.getIdReservationRule( ) );
350                 }
351                 manageTheSlotsAndAppointmentsImpacted( listSlotsImpactedWithAppointment, listSlotsImpacted, Boolean.TRUE,
352                         _appointmentForm.getMaxCapacityPerSlot( ), Boolean.FALSE, Boolean.FALSE );
353             }
354             else
355             {
356                 // No check, delete all the slots
357                 SlotService.deleteListSlots( listSlotsImpacted );
358             }
359         }
360         ReservationRuleService.updateAdvancedParameters( _appointmentForm );
361 
362         AppLogService.info( LogUtilities.buildLog( ACTION_MODIFY_ADVANCED_PARAMETERS, String.valueOf( _appointmentForm.getIdForm( ) ), getUser( ) ) );
363         addInfo( INFO_ADVANCED_PARAMETERS_UPDATED, getLocale( ) );
364         return redirect( request, VIEW_MANAGE_TYPICAL_WEEK, PARAMETER_ID_FORM, _appointmentForm.getIdForm( ), PARAMETER_ID_RULE,
365                 _appointmentForm.getIdReservationRule( ) );
366     }
367 
368     /**
369      * Modify global param of thr typical week
370      * 
371      * @param request
372      *            the request
373      * @return the page
374      * @throws AccessDeniedException
375      */
376     @Action( ACTION_MODIFY_GLOBAL_PARAM )
377     public String doModifyGlobalParameters( HttpServletRequest request ) throws AccessDeniedException
378     {
379         if ( !RBACService.isAuthorized( AppointmentFormDTO.RESOURCE_TYPE, String.valueOf( _appointmentForm.getIdForm( ) ),
380                 AppointmentResourceIdService.PERMISSION_MODIFY_ADVANCED_SETTING_FORM, (User) getUser( ) ) )
381         {
382             throw new AccessDeniedException( AppointmentResourceIdService.PERMISSION_MODIFY_ADVANCED_SETTING_FORM );
383         }
384         ReservationRuleness/rule/ReservationRule.html#ReservationRule">ReservationRule reservationRule = new ReservationRule( );
385         reservationRule.setIdReservationRule( _appointmentForm.getIdReservationRule( ) );
386         populate( _appointmentForm, request );
387         ReservationRuleService.fillInReservationRule( reservationRule, _appointmentForm, _appointmentForm.getIdForm( ) );
388 
389         if ( !validateReservationRuleBean( _appointmentForm, VALIDATION_ATTRIBUTES_PREFIX ) || !checkMultiSlotFormTypeBookablePlaces( _appointmentForm )
390                 || !checkSlotCapacityAndPeoplePerAppointment( _appointmentForm ) )
391         {
392             addError( PARAMETER_ERROR_MODIFICATION );
393             return redirect( request, VIEW_MANAGE_TYPICAL_WEEK, PARAMETER_ID_FORM, _appointmentForm.getIdForm( ), PARAMETER_ID_RULE,
394                     _appointmentForm.getIdReservationRule( ) );
395         }
396 
397         ReservationRuleHome.update( reservationRule );
398         addInfo( INFO_GLOBAL_PARAMETERS_UPDATED, getLocale( ) );
399         return redirect( request, VIEW_MANAGE_TYPICAL_WEEK, PARAMETER_ID_FORM, _appointmentForm.getIdForm( ), PARAMETER_ID_RULE,
400                 _appointmentForm.getIdReservationRule( ) );
401     }
402 
403     /**
404      * Manages the removal form of a appointment whose identifier is in the HTTP request
405      * 
406      * @param request
407      *            The HTTP request
408      * @return the HTML code to confirm
409      */
410     @Action( ACTION_CONFIRM_REMOVE_PARAMETER )
411     public String getConfirmRemoveParameter( HttpServletRequest request )
412     {
413         UrlItem url = new UrlItem( getActionUrl( ACTION_REMOVE_PARAMETER ) );
414         url.addParameter( PARAMETER_ID_RULE, request.getParameter( PARAMETER_ID_RULE ) );
415         url.addParameter( PARAMETER_ID_FORM, request.getParameter( PARAMETER_ID_FORM ) );
416 
417         String strMessageUrl = AdminMessageService.getMessageUrl( request, MESSAGE_CONFIRM_REMOVE_WEEK_DEFINITION, url.getUrl( ),
418                 AdminMessage.TYPE_CONFIRMATION );
419         return redirect( request, strMessageUrl );
420     }
421 
422     /**
423      * Handles the removal form of a week rul
424      * 
425      * @param request
426      *            The HTTP request
427      * @throws AccessDeniedException
428      *             If the user is not authorized to access this feature
429      */
430     @Action( ACTION_REMOVE_PARAMETER )
431     public String doRemoveParameter( HttpServletRequest request ) throws AccessDeniedException
432     {
433         String strIdForm = request.getParameter( PARAMETER_ID_FORM );
434         int nIdReservationRule = Integer.parseInt( request.getParameter( PARAMETER_ID_RULE ) );
435         List<WeekDefinition> listWeekDefinition = WeekDefinitionService.findByReservationRule( nIdReservationRule );
436         if ( CollectionUtils.isNotEmpty( listWeekDefinition ) )
437         {
438 
439             return redirect( request, AdminMessageService.getMessageUrl( request, MESSAGE_ERROR_RULE_ASSIGNED, AdminMessage.TYPE_STOP ) );
440         }
441 
442         if ( !RBACService.isAuthorized( AppointmentFormDTO.RESOURCE_TYPE, strIdForm, AppointmentResourceIdService.PERMISSION_MODIFY_ADVANCED_SETTING_FORM,
443                 (User) getUser( ) ) )
444         {
445             throw new AccessDeniedException( AppointmentResourceIdService.PERMISSION_MODIFY_ADVANCED_SETTING_FORM );
446         }
447         if ( AppointmentUtilities.weekIsOpenInFO( _appointmentForm, listWeekDefinition, getLocale( ) ) )
448         {
449 
450             return redirect( request, AdminMessageService.getMessageUrl( request, ERROR_MESSAGE_WEEK_IS_OPEN_FO, AdminMessage.TYPE_STOP ) );
451 
452         }
453         ReservationRuleService.removeReservationRule( nIdReservationRule );
454         addInfo( INFO_PARAMETER_REMOVED, getLocale( ) );
455         return redirect( request, VIEW_MANAGE_TYPICAL_WEEK, PARAMETER_ID_FORM, Integer.parseInt( strIdForm ) );
456     }
457 
458     /**
459      * Copy typical week
460      * 
461      * @param request
462      *            the request
463      * @return Html Page
464      * @throws AccessDeniedException
465      */
466     @Action( ACTION_DO_COPY_WEEK )
467     public String doCopyWeek( HttpServletRequest request ) throws AccessDeniedException
468     {
469         String strIdForm = request.getParameter( PARAMETER_ID_FORM );
470         int nIdForm = Integer.parseInt( strIdForm );
471         int nIdReservationRule = Integer.parseInt( request.getParameter( PARAMETER_ID_RULE ) );
472 
473         if ( !RBACService.isAuthorized( AppointmentFormDTO.RESOURCE_TYPE, strIdForm, AppointmentResourceIdService.PERMISSION_MODIFY_ADVANCED_SETTING_FORM,
474                 (User) getUser( ) ) )
475         {
476             throw new AccessDeniedException( AppointmentResourceIdService.PERMISSION_MODIFY_ADVANCED_SETTING_FORM );
477         }
478         ReservationRuleService.copyReservationRule( nIdReservationRule );
479         return redirect( request, VIEW_MANAGE_TYPICAL_WEEK, PARAMETER_ID_FORM, nIdForm, PARAMETER_ID_RULE, nIdReservationRule );
480 
481     }
482 
483     /**
484      * Get the view to modify a time slot
485      * 
486      * @param request
487      *            the request
488      * @return the page
489      */
490     @View( VIEW_MODIFY_TIME_SLOT )
491     public String getViewModifyTimeSlot( HttpServletRequest request )
492     {
493         int nIdTimeSlot = Integer.parseInt( request.getParameter( PARAMETER_ID_TIME_SLOT ) );
494         if ( ( _timeSlot == null ) || ( nIdTimeSlot != _timeSlot.getIdTimeSlot( ) ) )
495         {
496             _timeSlot = TimeSlotService.findTimeSlotById( nIdTimeSlot );
497         }
498         addInfo( MESSAGE_WARNING_CHANGES_APPLY_TO_ALL, getLocale( ) );
499         Map<String, Object> model = getModel( );
500         model.put( PARAMETER_ID_FORM, request.getParameter( PARAMETER_ID_FORM ) );
501         model.put( MARK_ID_RULE, request.getParameter( PARAMETER_ID_RULE ) );
502         model.put( MARK_TIME_SLOT, _timeSlot );
503         return getPage( MESSAGE_MODIFY_TIME_SLOT_PAGE_TITLE, TEMPLATE_MODIFY_TIME_SLOT, model );
504     }
505 
506     /**
507      * Do modify a time slot
508      * 
509      * @param request
510      *            the request
511      * @return to the page of the typical week
512      * @throws AccessDeniedException
513      */
514     @Action( ACTION_DO_MODIFY_TIME_SLOT )
515     public String doModifyTimeSlot( HttpServletRequest request ) throws AccessDeniedException
516     {
517         String strIdForm = request.getParameter( PARAMETER_ID_FORM );
518         int nIdForm = Integer.parseInt( strIdForm );
519         String strIdReservationRule = request.getParameter( PARAMETER_ID_RULE );
520         int nIdReservationRule = Integer.parseInt( strIdReservationRule );
521         if ( !RBACService.isAuthorized( AppointmentFormDTO.RESOURCE_TYPE, strIdForm, AppointmentResourceIdService.PERMISSION_MODIFY_ADVANCED_SETTING_FORM,
522                 (User) getUser( ) ) )
523         {
524             throw new AccessDeniedException( AppointmentResourceIdService.PERMISSION_MODIFY_ADVANCED_SETTING_FORM );
525         }
526         String strIdTimeSlot = request.getParameter( PARAMETER_ID_TIME_SLOT );
527         int nIdTimeSlot = Integer.parseInt( strIdTimeSlot );
528         TimeSlot oldTimeSlot = TimeSlotService.findTimeSlotById( nIdTimeSlot );
529 
530         if ( _timeSlot == null || nIdTimeSlot != _timeSlot.getIdTimeSlot( ) )
531         {
532             _timeSlot = oldTimeSlot;
533         }
534         boolean bIsOpen = Boolean.parseBoolean( request.getParameter( PARAMETER_IS_OPEN ) );
535         boolean bOpeningHasChanged = false;
536         int nMaxCapacity = Integer.parseInt( request.getParameter( PARAMETER_MAX_CAPACITY ) );
537         LocalTime endingTime = LocalTime.parse( request.getParameter( PARAMETER_ENDING_TIME ) );
538         boolean bShiftSlot = Boolean.parseBoolean( request.getParameter( PARAMETER_SHIFT_SLOT ) );
539         boolean bEndingTimeHasChanged = false;
540         boolean bMaxCapacityHasChanged = false;
541         LocalDate dateNow = LocalDate.now( );
542         List<WeekDefinition> listWeekDefinition = WeekDefinitionService.findByReservationRule( nIdReservationRule );
543 
544         if ( listWeekDefinition.stream( ).anyMatch( week -> week.getDateOfApply( ).isBefore( dateNow ) ) )
545         {
546 
547             addError( MESSAGE_ERROR_MODIFICATION_WEEK_ASSIGNED_IN_PAST, getLocale( ) );
548             return redirect( request, VIEW_MANAGE_TYPICAL_WEEK, PARAMETER_ID_FORM, nIdForm, PARAMETER_ID_RULE, nIdReservationRule );
549         }
550         if ( AppointmentUtilities.weekIsOpenInFO( _appointmentForm, listWeekDefinition, getLocale( ) ) )
551         {
552 
553             addError( ERROR_MESSAGE_WEEK_IS_OPEN_FO, getLocale( ) );
554             return redirect( request, VIEW_MANAGE_TYPICAL_WEEK, PARAMETER_ID_FORM, nIdForm, PARAMETER_ID_RULE, nIdReservationRule );
555 
556         }
557         if ( bIsOpen != _timeSlot.getIsOpen( ) )
558         {
559             _timeSlot.setIsOpen( bIsOpen );
560             bOpeningHasChanged = true;
561         }
562         if ( nMaxCapacity != oldTimeSlot.getMaxCapacity( ) )
563         {
564             _timeSlot.setMaxCapacity( nMaxCapacity );
565             bMaxCapacityHasChanged = true;
566         }
567         LocalTime previousEndingTime = oldTimeSlot.getEndingTime( );
568         if ( !endingTime.equals( previousEndingTime ) )
569         {
570             _timeSlot.setEndingTime( endingTime );
571             if ( !checkEndingTimeOfTimeSlot( endingTime, _timeSlot ) )
572             {
573                 Map<String, String> additionalParameters = new HashMap<>( );
574                 additionalParameters.put( PARAMETER_ID_FORM, strIdForm );
575                 additionalParameters.put( PARAMETER_ID_RULE, strIdReservationRule );
576                 return redirect( request, VIEW_MANAGE_TYPICAL_WEEK, additionalParameters );
577             }
578             bEndingTimeHasChanged = true;
579         }
580         List<Slot> listSlotsImpacted = new ArrayList<>( );
581         List<Slot> listSlotsImpactedByDate = new ArrayList<>( );
582 
583         for ( WeekDefinition week : listWeekDefinition )
584         {
585 
586             listSlotsImpacted.addAll( AppointmentUtilities.findSlotsImpactedByThisTimeSlot( _timeSlot, nIdForm, week.getIdWeekDefinition( ), bShiftSlot ) );
587             listSlotsImpactedByDate.addAll( SlotService.findSlotWithAppointmentByDateRange( nIdForm, week.getDateOfApply( ).atStartOfDay( ),
588                     week.getEndingDateOfApply( ).atTime( LocalTime.MAX ) ) );
589 
590         }
591 
592         // If there are slots impacted
593         if ( CollectionUtils.isNotEmpty( listSlotsImpacted ) )
594         {
595             List<Integer> listIdSlotsImpacted = listSlotsImpacted.stream( ).map( Slot::getIdSlot ).collect( Collectors.toList( ) );
596             List<Slot> listSlotsImpactedWithAppointment = listSlotsImpactedByDate.stream( ).filter( slot -> listIdSlotsImpacted.contains( slot.getIdSlot( ) ) )
597                     .collect( Collectors.toList( ) );
598             // if there are appointments impacted
599             if ( CollectionUtils.isNotEmpty( listSlotsImpactedWithAppointment ) )
600             {
601                 // If the ending time of the time slot has changed or if the max
602                 // capacity has decreased
603                 if ( bEndingTimeHasChanged || nMaxCapacity < oldTimeSlot.getMaxCapacity( ) )
604                 {
605                     // Error, the time slot can't be changed
606                     addError( MESSAGE_ERROR_APPOINTMENT_ON_SLOT, getLocale( ) );
607                     addError( listSlotsImpactedWithAppointment.size( ) + " slot impacté(s)" );
608                     Map<String, String> additionalParameters = new HashMap<>( );
609                     additionalParameters.put( PARAMETER_ID_FORM, strIdForm );
610                     additionalParameters.put( PARAMETER_ID_RULE, strIdReservationRule );
611                     return redirect( request, VIEW_MANAGE_TYPICAL_WEEK, additionalParameters );
612                 }
613                 // Get the slot whith appointment (the appointments that are not
614                 // cancelled)
615                 List<Slot> listSlotsWithAppointmentNotCancelled = listSlotsImpactedWithAppointment.stream( ).filter( slot -> slot.getNbPlacesTaken( ) > 0 )
616                         .collect( Collectors.toList( ) );
617                 if ( bOpeningHasChanged && CollectionUtils.isNotEmpty( listSlotsWithAppointmentNotCancelled ) )
618                 {
619                     addInfo( MESSAGE_INFO_VALIDATED_APPOINTMENTS_IMPACTED, getLocale( ) );
620                 }
621                 manageTheSlotsAndAppointmentsImpacted( listSlotsImpactedWithAppointment, listSlotsImpacted, bMaxCapacityHasChanged, nMaxCapacity,
622                         bOpeningHasChanged, bIsOpen );
623             }
624             else
625             {
626                 // no need to check appointments, delete all the slots
627                 SlotService.deleteListSlots( listSlotsImpacted );
628             }
629         }
630         TimeSlotService.updateTimeSlot( _timeSlot, bEndingTimeHasChanged, previousEndingTime, bShiftSlot );
631 
632         AppLogService.info( LogUtilities.buildLog( ACTION_DO_MODIFY_TIME_SLOT, strIdTimeSlot, getUser( ) ) );
633         addInfo( MESSAGE_INFO_SLOT_UPDATED, getLocale( ) );
634         return redirect( request, VIEW_MANAGE_TYPICAL_WEEK, PARAMETER_ID_FORM, nIdForm, PARAMETER_ID_RULE, nIdReservationRule );
635     }
636 
637     /**
638      * Do modify a time slot
639      * 
640      * @param request
641      *            the request
642      * @return to the page of the typical week
643      * @throws AccessDeniedException
644      */
645     @Action( ACTION_DO_MODIFY_LIST_TIME_SLOT )
646     public String doModifyListTimeSlot( HttpServletRequest request ) throws AccessDeniedException
647     {
648         String strIdForm = request.getParameter( PARAMETER_ID_FORM );
649         String strCap = request.getParameter( PARAMETER_CAPACITY_MOD );
650         int nIdForm = Integer.parseInt( strIdForm );
651         String strIdReservationRule = request.getParameter( PARAMETER_ID_RULE );
652         int nIdReservationRule = Integer.parseInt( strIdReservationRule );
653         int nVarMaxCapacity = 0;
654         int nMaxCapacity = -1;
655         if ( !RBACService.isAuthorized( AppointmentFormDTO.RESOURCE_TYPE, strIdForm, AppointmentResourceIdService.PERMISSION_MODIFY_ADVANCED_SETTING_FORM,
656                 (User) getUser( ) ) )
657         {
658             throw new AccessDeniedException( AppointmentResourceIdService.PERMISSION_MODIFY_ADVANCED_SETTING_FORM );
659         }
660         String strJson = request.getParameter( PARAMETER_TIME_SLOT_DATA );
661         AppLogService.debug( "slot - Received strJson : " + strJson );
662         ObjectMapper mapper = new ObjectMapper( );
663         mapper.registerModule( new JavaTimeModule( ) );
664         mapper.configure( DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false );
665 
666         List<TimeSlot> listTimeSlot = new ArrayList<>( );
667         List<TimeSlot> listTimeSlotJson = new ArrayList<>( );
668 
669         try
670         {
671 
672             listTimeSlotJson = mapper.readValue( strJson, new TypeReference<List<TimeSlot>>( )
673             {
674             } );
675 
676         }
677         catch( IOException e )
678         {
679 
680             AppLogService.error( MESSAGE_ERROR_PARSING_JSON + e.getMessage( ), e );
681             addError( MESSAGE_ERROR_PARSING_JSON, getLocale( ) );
682             return redirect( request, VIEW_MANAGE_TYPICAL_WEEK, PARAMETER_ID_FORM, nIdForm, PARAMETER_ID_RULE, nIdReservationRule );
683 
684         }
685         boolean bStateHasChanged = false;
686         boolean bIsOpen = false;
687         String strIsOpen = request.getParameter( PARAMETER_IS_OPEN );
688 
689         if ( strIsOpen.equalsIgnoreCase( "true" ) || strIsOpen.equalsIgnoreCase( "false" ) )
690         {
691 
692             bStateHasChanged = true;
693             bIsOpen = Boolean.parseBoolean( strIsOpen );
694         }
695         boolean bMaxCapacityIsLower = false;
696         LocalDate dateNow = LocalDate.now( );
697 
698         if ( strCap.equals( VAR_CAP ) )
699         {
700 
701             nVarMaxCapacity = Integer.parseInt( request.getParameter( PARAMETER_MAX_CAPACITY ) );
702 
703         }
704         else
705             if ( strCap.equals( NEW_CAP ) )
706             {
707 
708                 nMaxCapacity = Integer.parseInt( request.getParameter( PARAMETER_MAX_CAPACITY ) );
709 
710             }
711 
712         List<WeekDefinition> listWeekDefinition = WeekDefinitionService.findByReservationRule( nIdReservationRule );
713 
714         if ( listWeekDefinition.stream( ).anyMatch( week -> week.getDateOfApply( ).isBefore( dateNow ) ) )
715         {
716 
717             addError( MESSAGE_ERROR_MODIFICATION_WEEK_ASSIGNED_IN_PAST, getLocale( ) );
718             return redirect( request, VIEW_MANAGE_TYPICAL_WEEK, PARAMETER_ID_FORM, nIdForm, PARAMETER_ID_RULE, nIdReservationRule );
719         }
720         if ( AppointmentUtilities.weekIsOpenInFO( _appointmentForm, listWeekDefinition, getLocale( ) ) )
721         {
722 
723             addError( ERROR_MESSAGE_WEEK_IS_OPEN_FO, getLocale( ) );
724             return redirect( request, VIEW_MANAGE_TYPICAL_WEEK, PARAMETER_ID_FORM, nIdForm, PARAMETER_ID_RULE, nIdReservationRule );
725 
726         }
727         for ( TimeSlot time : listTimeSlotJson )
728         {
729 
730             TimeSlot timeSlot = TimeSlotService.findTimeSlotById( time.getIdTimeSlot( ) );
731             if ( bStateHasChanged && bIsOpen != timeSlot.getIsOpen( ) )
732             {
733                 timeSlot.setIsOpen( bIsOpen );
734             }
735             if ( nMaxCapacity >= 0 && nMaxCapacity != timeSlot.getMaxCapacity( ) )
736             {
737                 timeSlot.setMaxCapacity( nMaxCapacity );
738                 if ( nMaxCapacity < timeSlot.getMaxCapacity( ) )
739                 {
740 
741                     bMaxCapacityIsLower = true;
742                 }
743             }
744             else
745             {
746                 if ( timeSlot.getMaxCapacity( ) + nVarMaxCapacity > 0 )
747                 {
748 
749                     timeSlot.setMaxCapacity( timeSlot.getMaxCapacity( ) + nVarMaxCapacity );
750 
751                 }
752                 else
753                 {
754 
755                     timeSlot.setMaxCapacity( 0 );
756                 }
757                 if ( nVarMaxCapacity < 0 )
758                 {
759 
760                     bMaxCapacityIsLower = true;
761                 }
762             }
763             listTimeSlot.add( timeSlot );
764         }
765 
766         List<Slot> listSlotsImpacted = new ArrayList<>( );
767         List<Slot> listSlotsImpactedByDate = new ArrayList<>( );
768 
769         for ( WeekDefinition week : listWeekDefinition )
770         {
771             for ( TimeSlot timeSlot : listTimeSlot )
772             {
773                 listSlotsImpacted.addAll( AppointmentUtilities.findSlotsImpactedByThisTimeSlot( timeSlot, nIdForm, week.getIdWeekDefinition( ), false ) );
774             }
775             listSlotsImpactedByDate.addAll( SlotService.findSlotWithAppointmentByDateRange( nIdForm, week.getDateOfApply( ).atStartOfDay( ),
776                     week.getEndingDateOfApply( ).atTime( LocalTime.MAX ) ) );
777         }
778 
779         // If there are slots impacted
780         if ( CollectionUtils.isNotEmpty( listSlotsImpacted ) )
781         {
782             List<Integer> listIdSlotsImpacted = listSlotsImpacted.stream( ).map( Slot::getIdSlot ).collect( Collectors.toList( ) );
783             List<Slot> listSlotsImpactedWithAppointment = listSlotsImpactedByDate.stream( ).filter( slot -> listIdSlotsImpacted.contains( slot.getIdSlot( ) ) )
784                     .collect( Collectors.toList( ) );
785 
786             // if there are appointments impacted
787             if ( CollectionUtils.isNotEmpty( listSlotsImpactedWithAppointment ) )
788             {
789                 // if the max capacity has decreased
790                 if ( bMaxCapacityIsLower )
791                 {
792                     // Error, the time slot can't be changed
793                     addError( MESSAGE_ERROR_APPOINTMENT_ON_SLOT, getLocale( ) );
794                     addError( listSlotsImpactedWithAppointment.size( ) + " slot impacté(s)" );
795                     Map<String, String> additionalParameters = new HashMap<>( );
796                     additionalParameters.put( PARAMETER_ID_FORM, strIdForm );
797                     additionalParameters.put( PARAMETER_ID_RULE, strIdReservationRule );
798                     return redirect( request, VIEW_MANAGE_TYPICAL_WEEK, additionalParameters );
799                 }
800 
801                 manageTheSlotsAndAppointmentsImpacted( listSlotsImpactedWithAppointment, listSlotsImpacted, nMaxCapacity, nVarMaxCapacity, bIsOpen,
802                         bStateHasChanged );
803             }
804             else
805             {
806                 // no need to check appointments, delete all the slots
807                 SlotService.deleteListSlots( listSlotsImpacted );
808             }
809         }
810 
811         TimeSlotService.updateListTimeSlot( listTimeSlot );
812         if ( CollectionUtils.isNotEmpty( listTimeSlot ) && CollectionUtils.isNotEmpty( listWeekDefinition ) )
813         {
814 
815             WeekDefinitionManagerListener.notifyListenersListWeekDefinitionChanged( nIdForm, listWeekDefinition );
816         }
817 
818         addInfo( MESSAGE_INFO_SLOT_UPDATED, getLocale( ) );
819         return redirect( request, VIEW_MANAGE_TYPICAL_WEEK, PARAMETER_ID_FORM, nIdForm, PARAMETER_ID_RULE, nIdReservationRule );
820     }
821 
822     /**
823      * Check the ending time of a time slot
824      * 
825      * @param endingTime
826      *            the new ending time
827      * @param timeSlot
828      *            the time slot
829      * @return false if there is an error
830      */
831     private boolean checkEndingTimeOfTimeSlot( LocalTime endingTime, TimeSlot timeSlot )
832     {
833         boolean bReturn = true;
834         WorkingDay workingDay = WorkingDayService.findWorkingDayById( timeSlot.getIdWorkingDay( ) );
835         if ( endingTime.isAfter( WorkingDayService.getMaxEndingTimeOfAWorkingDay( workingDay ) ) )
836         {
837             bReturn = false;
838             addError( MESSAGE_SLOT_CAN_NOT_END_AFTER_DAY_OR_FORM, getLocale( ) );
839         }
840         if ( endingTime.isBefore( timeSlot.getStartingTime( ) ) || endingTime.equals( timeSlot.getStartingTime( ) ) )
841         {
842             bReturn = false;
843             addError( MESSAGE_ERROR_TIME_END_BEFORE_TIME_START, getLocale( ) );
844         }
845         return bReturn;
846     }
847 
848     /**
849      * 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
850      * 
851      * @param listAppointmentsImpacted
852      *            the appointments impacted
853      * @param listSlotsImpacted
854      *            the slots impacted
855      * @param bMaxCapacityHasChanged
856      *            True if the capacity has changed
857      * @param nMaxCapacity
858      *            the max capacity
859      * @param bOpeningHasChanged
860      *            true if the opening has changed
861      * @param bIsOpen
862      *            the new boolean opening value
863      */
864     private void manageTheSlotsAndAppointmentsImpacted( List<Slot> listSlotsImpactedWithAppointments, List<Slot> listSlotsImpacted,
865             boolean bMaxCapacityHasChanged, int nMaxCapacity, boolean bOpeningHasChanged, boolean bIsOpen )
866     {
867         // Need to delete the slots that are impacted but with no
868         // appointments
869         List<Integer> listIdSlotsImpactedWithAppointments = listSlotsImpactedWithAppointments.stream( ).map( Slot::getIdSlot ).collect( Collectors.toList( ) );
870         List<Slot> listslotImpactedWithoutAppointments = listSlotsImpacted.stream( )
871                 .filter( p -> !listIdSlotsImpactedWithAppointments.contains( p.getIdSlot( ) ) ).collect( Collectors.toList( ) );
872 
873         SlotService.deleteListSlots( listslotImpactedWithoutAppointments );
874         for ( Slot slotImpacted : listSlotsImpactedWithAppointments )
875         {
876             Lock lock = SlotSafeService.getLockOnSlot( slotImpacted.getIdSlot( ) );
877             lock.lock( );
878             try
879             {
880                 slotImpacted = updateRemainingPlaces( slotImpacted, bMaxCapacityHasChanged, nMaxCapacity, bOpeningHasChanged, bIsOpen );
881                 SlotSafeService.updateSlot( slotImpacted );
882             }
883             finally
884             {
885                 lock.unlock( );
886             }
887         }
888     }
889 
890     /**
891      * 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
892      * 
893      * @param listAppointmentsImpacted
894      *            the appointments impacted
895      * @param listSlotsImpacted
896      *            the slots impacted
897      * @param nMaxCapacity
898      *            the max capacity
899      * @param bIsOpen
900      *            the new boolean opening value
901      */
902     private void manageTheSlotsAndAppointmentsImpacted( List<Slot> listSlotsImpactedWithAppointments, List<Slot> listSlotsImpacted, int nMaxCapacity,
903             int nVarMaxCapacity, boolean bIsOpen, boolean bStateHasChanged )
904     {
905         boolean bOpeningHasChanged = false;
906         boolean binfoOpeningHasChanged = false;
907         boolean bMaxCapacityHasChanged = false;
908         // Need to delete the slots that are impacted but with no
909         // appointments
910         List<Integer> listIdSlotsImpactedWithAppointments = listSlotsImpactedWithAppointments.stream( ).map( Slot::getIdSlot ).collect( Collectors.toList( ) );
911         List<Slot> listslotImpactedWithoutAppointments = listSlotsImpacted.stream( )
912                 .filter( p -> !listIdSlotsImpactedWithAppointments.contains( p.getIdSlot( ) ) ).collect( Collectors.toList( ) );
913 
914         SlotService.deleteListSlots( listslotImpactedWithoutAppointments );
915         for ( Slot slotImpacted : listSlotsImpactedWithAppointments )
916         {
917             bOpeningHasChanged = false;
918             bMaxCapacityHasChanged = false;
919 
920             Lock lock = SlotSafeService.getLockOnSlot( slotImpacted.getIdSlot( ) );
921             lock.lock( );
922             try
923             {
924                 if ( ( nMaxCapacity != -1 && slotImpacted.getMaxCapacity( ) != nMaxCapacity ) )
925                 {
926 
927                     bMaxCapacityHasChanged = true;
928                 }
929                 else
930                     if ( nVarMaxCapacity != 0 )
931                     {
932 
933                         nMaxCapacity = ( slotImpacted.getMaxCapacity( ) + nVarMaxCapacity ) >= 0 ? ( slotImpacted.getMaxCapacity( ) + nVarMaxCapacity ) : 0;
934                         bMaxCapacityHasChanged = true;
935 
936                     }
937                 if ( bStateHasChanged && slotImpacted.getIsOpen( ) != bIsOpen )
938                 {
939 
940                     bOpeningHasChanged = true;
941                     binfoOpeningHasChanged = true;
942                 }
943                 slotImpacted = updateRemainingPlaces( slotImpacted, bMaxCapacityHasChanged, nMaxCapacity, bOpeningHasChanged, bIsOpen );
944                 SlotSafeService.updateSlot( slotImpacted );
945             }
946             finally
947             {
948                 lock.unlock( );
949             }
950         }
951         // Get the slot whith appointment (the appointments that are not
952         // cancelled)
953         List<Slot> listSlotsWithAppointmentNotCancelled = listSlotsImpactedWithAppointments.stream( ).filter( slot -> slot.getNbPlacesTaken( ) > 0 )
954                 .collect( Collectors.toList( ) );
955         if ( binfoOpeningHasChanged && CollectionUtils.isNotEmpty( listSlotsWithAppointmentNotCancelled ) )
956         {
957             addInfo( MESSAGE_INFO_VALIDATED_APPOINTMENTS_IMPACTED, getLocale( ) );
958         }
959     }
960 
961     /**
962      * Update the capacity of the slot
963      * 
964      * @param slot
965      *            the slot to update
966      * @param bMaxCapacityHasChanged
967      *            True if the capacity has changed
968      * @param nMaxCapacity
969      *            the max capacity
970      * @param bOpeningHasChanged
971      *            true if the opening has changed
972      * @param bIsOpen
973      *            the new boolean opening value Return the slot updated
974      */
975     private static Slot/../../fr/paris/lutece/plugins/appointment/business/slot/Slot.html#Slot">Slot updateRemainingPlaces( Slot slot, boolean bMaxCapacityHasChanged, int nNewNbMaxCapacity, boolean bOpeningHasChanged, boolean bIsOpen )
976     {
977         slot = SlotHome.findByPrimaryKey( slot.getIdSlot( ) );
978         // If the max capacity has been modified
979         if ( bMaxCapacityHasChanged )
980         {
981             int nOldBnMaxCapacity = slot.getMaxCapacity( );
982             nNewNbMaxCapacity = ( nNewNbMaxCapacity >= 0 ) ? nNewNbMaxCapacity : 0;
983             // Need to add the diff between the old value and the new value
984             // to the remaining places (if the new is higher)
985             if ( nNewNbMaxCapacity > nOldBnMaxCapacity )
986             {
987                 int nValueToAdd = nNewNbMaxCapacity - nOldBnMaxCapacity;
988                 slot.setNbPotentialRemainingPlaces( slot.getNbPotentialRemainingPlaces( ) + nValueToAdd );
989                 slot.setNbRemainingPlaces( slot.getNbRemainingPlaces( ) + nValueToAdd );
990             }
991             else
992             {
993                 // the new value is lower than the previous capacity
994                 // !!!! If there are appointments on this slot and if the
995                 // slot is already full, the slot will be surbooked !!!!
996                 int nValueToSubstract = nOldBnMaxCapacity - nNewNbMaxCapacity;
997                 slot.setNbPotentialRemainingPlaces( slot.getNbPotentialRemainingPlaces( ) - nValueToSubstract );
998                 slot.setNbRemainingPlaces( slot.getNbRemainingPlaces( ) - nValueToSubstract );
999             }
1000             slot.setMaxCapacity( nNewNbMaxCapacity );
1001         }
1002         if ( bOpeningHasChanged )
1003         {
1004             slot.setIsOpen( bIsOpen );
1005         }
1006         slot.setIsSpecific( false );
1007 
1008         return slot;
1009     }
1010 }