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.service;
35  
36  import static java.lang.Math.toIntExact;
37  
38  import java.io.IOException;
39  import java.io.OutputStream;
40  import java.sql.Date;
41  import java.text.SimpleDateFormat;
42  import java.time.DayOfWeek;
43  import java.time.LocalDate;
44  import java.time.LocalDateTime;
45  import java.time.LocalTime;
46  import java.time.format.DateTimeFormatter;
47  import java.time.temporal.ChronoUnit;
48  import java.util.ArrayList;
49  import java.util.GregorianCalendar;
50  import java.util.HashMap;
51  import java.util.List;
52  import java.util.Locale;
53  import java.util.Map;
54  import java.util.Set;
55  import java.util.Timer;
56  import java.util.concurrent.TimeUnit;
57  import java.util.stream.Collectors;
58  
59  import javax.servlet.http.HttpServletRequest;
60  import javax.servlet.http.HttpServletResponse;
61  import javax.validation.ConstraintViolation;
62  
63  import org.apache.commons.collections.CollectionUtils;
64  import org.apache.commons.lang.StringUtils;
65  import org.apache.poi.ss.usermodel.Cell;
66  import org.apache.poi.ss.usermodel.Row;
67  import org.apache.poi.xssf.usermodel.XSSFSheet;
68  import org.apache.poi.xssf.usermodel.XSSFWorkbook;
69  
70  import fr.paris.lutece.plugins.appointment.business.appointment.Appointment;
71  import fr.paris.lutece.plugins.appointment.business.form.Form;
72  import fr.paris.lutece.plugins.appointment.business.planning.TimeSlot;
73  import fr.paris.lutece.plugins.appointment.business.planning.WeekDefinition;
74  import fr.paris.lutece.plugins.appointment.business.planning.WorkingDay;
75  import fr.paris.lutece.plugins.appointment.business.rule.ReservationRule;
76  import fr.paris.lutece.plugins.appointment.business.slot.Slot;
77  import fr.paris.lutece.plugins.appointment.business.user.User;
78  import fr.paris.lutece.plugins.appointment.service.lock.SlotEditTask;
79  import fr.paris.lutece.plugins.appointment.service.lock.TimerForLockOnSlot;
80  import fr.paris.lutece.plugins.appointment.web.dto.AppointmentDTO;
81  import fr.paris.lutece.plugins.appointment.web.dto.AppointmentFormDTO;
82  import fr.paris.lutece.plugins.appointment.web.dto.ResponseRecapDTO;
83  import fr.paris.lutece.plugins.genericattributes.business.Entry;
84  import fr.paris.lutece.plugins.genericattributes.business.EntryFilter;
85  import fr.paris.lutece.plugins.genericattributes.business.EntryHome;
86  import fr.paris.lutece.plugins.genericattributes.business.Field;
87  import fr.paris.lutece.plugins.genericattributes.business.FieldHome;
88  import fr.paris.lutece.plugins.genericattributes.business.GenericAttributeError;
89  import fr.paris.lutece.plugins.genericattributes.business.Response;
90  import fr.paris.lutece.plugins.genericattributes.business.ResponseHome;
91  import fr.paris.lutece.plugins.genericattributes.service.entrytype.EntryTypeServiceManager;
92  import fr.paris.lutece.plugins.genericattributes.service.entrytype.IEntryTypeService;
93  import fr.paris.lutece.plugins.workflowcore.business.state.State;
94  import fr.paris.lutece.plugins.workflowcore.service.state.StateService;
95  import fr.paris.lutece.portal.business.user.AdminUser;
96  import fr.paris.lutece.portal.service.i18n.I18nService;
97  import fr.paris.lutece.portal.service.rbac.RBACService;
98  import fr.paris.lutece.portal.service.util.AppLogService;
99  import fr.paris.lutece.portal.service.util.AppPropertiesService;
100 import fr.paris.lutece.util.beanvalidation.BeanValidationUtil;
101 
102 /**
103  * Utility class for Appointment Mutualize methods between MVCApplication and MVCAdminJspBean
104  * 
105  * @author Laurent Payen
106  *
107  */
108 public final class AppointmentUtilities
109 {
110 
111     private static final String ERROR_MESSAGE_EMPTY_CONFIRM_EMAIL = "appointment.validation.appointment.EmailConfirmation.email";
112     private static final String ERROR_MESSAGE_CONFIRM_EMAIL = "appointment.message.error.confirmEmail";
113     private static final String ERROR_MESSAGE_DATE_APPOINTMENT = "appointment.message.error.dateAppointment";
114     private static final String ERROR_MESSAGE_EMPTY_EMAIL = "appointment.validation.appointment.Email.notEmpty";
115     private static final String ERROR_MESSAGE_EMPTY_NB_BOOKED_SEAT = "appointment.validation.appointment.NbBookedSeat.notEmpty";
116     private static final String ERROR_MESSAGE_FORMAT_NB_BOOKED_SEAT = "appointment.validation.appointment.NbBookedSeat.notNumberFormat";
117     private static final String ERROR_MESSAGE_ERROR_NB_BOOKED_SEAT = "appointment.validation.appointment.NbBookedSeat.error";
118 
119     private static final String KEY_RESOURCE_TYPE = "appointment.appointment.name";
120     private static final String KEY_COLUMN_LAST_NAME = "appointment.manageAppointments.columnLastName";
121     private static final String KEY_COLUMN_FISRT_NAME = "appointment.manageAppointments.columnFirstName";
122     private static final String KEY_COLUMN_EMAIL = "appointment.manageAppointments.columnEmail";
123     private static final String KEY_COLUMN_DATE_APPOINTMENT = "appointment.dateAppointment.title";
124     private static final String KEY_TIME_START = "appointment.model.entity.appointmentform.attribute.timeStart";
125     private static final String KEY_TIME_END = "appointment.model.entity.appointmentform.attribute.timeEnd";
126     private static final String KEY_COLUMN_ADMIN = "appointment.manageAppointments.columnAdmin";
127     private static final String KEY_COLUMN_STATUS = "appointment.labelStatus";
128     private static final String KEY_COLUMN_STATE = "appointment.manageAppointments.columnState";
129     private static final String KEY_COLUMN_NB_BOOKED_SEATS = "appointment.manageAppointments.columnNumberOfBookedseatsPerAppointment";
130     private static final String KEY_HORAIRE_MAX_AM = "appointment.horaireLimiteMaxAM";
131 
132     private static final String CONSTANT_COMMA = ",";
133     private static final String EXCEL_FILE_EXTENSION = ".xlsx";
134     private static final String EXCEL_MIME_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
135 
136     public static final String SESSION_TIMER_SLOT = "appointment.session.timer.slot";
137 
138     public static final String PROPERTY_DEFAULT_EXPIRED_TIME_EDIT_APPOINTMENT = "appointment.edit.expired.time";
139 
140     public static final int THIRTY_MINUTES = 30;
141 
142     /**
143      * Private constructor - this class does not need to be instantiated
144      */
145     private AppointmentUtilities( )
146     {
147     }
148 
149     /**
150      * Check that the email is correct and matches the confirm email
151      * 
152      * @param strEmail
153      *            the email
154      * @param strConfirmEmail
155      *            the confirm email
156      * @param form
157      *            the form
158      * @param locale
159      *            the local
160      * @param listFormErrors
161      *            the list of errors that can be fill in with the errors found for the email
162      */
163     public static void checkEmail( String strEmail, String strConfirmEmail, AppointmentFormDTO form, Locale locale, List<GenericAttributeError> listFormErrors )
164     {
165         if ( form.getEnableMandatoryEmail( ) )
166         {
167             if ( StringUtils.isEmpty( strEmail ) )
168             {
169                 GenericAttributeError genAttError = new GenericAttributeError( );
170                 genAttError.setErrorMessage( I18nService.getLocalizedString( ERROR_MESSAGE_EMPTY_EMAIL, locale ) );
171                 listFormErrors.add( genAttError );
172             }
173             if ( StringUtils.isEmpty( strConfirmEmail ) )
174             {
175                 GenericAttributeError genAttError = new GenericAttributeError( );
176                 genAttError.setErrorMessage( I18nService.getLocalizedString( ERROR_MESSAGE_EMPTY_CONFIRM_EMAIL, locale ) );
177                 listFormErrors.add( genAttError );
178             }
179         }
180         if ( !StringUtils.equals( strEmail, strConfirmEmail ) )
181         {
182             GenericAttributeError genAttError = new GenericAttributeError( );
183             genAttError.setErrorMessage( I18nService.getLocalizedString( ERROR_MESSAGE_CONFIRM_EMAIL, locale ) );
184             listFormErrors.add( genAttError );
185         }
186     }
187 
188     /**
189      * Check that the date of the appointment we try to take is not in the past
190      * 
191      * @param appointmentDTO
192      *            the appointment
193      * @param locale
194      *            the local
195      * @param listFormErrors
196      *            the list of errors that can be fill in with the error found with the date
197      */
198     public static void checkDateOfTheAppointmentIsNotBeforeNow( AppointmentDTO appointmentDTO, Locale locale, List<GenericAttributeError> listFormErrors )
199     {
200         if ( appointmentDTO.getSlot( ).getStartingDateTime( ).toLocalDate( ).isBefore( LocalDate.now( ) ) )
201         {
202             GenericAttributeError genAttError = new GenericAttributeError( );
203             genAttError.setErrorMessage( I18nService.getLocalizedString( ERROR_MESSAGE_DATE_APPOINTMENT, locale ) );
204             listFormErrors.add( genAttError );
205         }
206     }
207 
208     /**
209      * Check that the delay between two appointments for the same use has been respected
210      * 
211      * @param appointmentDTO
212      *            the appointment
213      * @param strEmail
214      *            the email
215      * @param form
216      *            the form
217      * @return false if the delay is not respected
218      */
219     public static boolean checkNbDaysBetweenTwoAppointments( AppointmentDTO appointmentDTO, String strFirstName, String strLastName, String strEmail,
220             AppointmentFormDTO form )
221     {
222         boolean bCheckPassed = true;
223         int nbDaysBetweenTwoAppointments = form.getNbDaysBeforeNewAppointment( );
224         if ( nbDaysBetweenTwoAppointments != 0 )
225         {
226             List<Slot> listSlots = getSlotsByEmail( strEmail, appointmentDTO.getIdAppointment( ) );
227             if ( CollectionUtils.isNotEmpty( listSlots ) )
228             {
229                 // Get the last appointment date for this form
230                 listSlots = listSlots.stream( ).filter( s -> s.getIdForm( ) == form.getIdForm( ) ).collect( Collectors.toList( ) );
231                 if ( CollectionUtils.isNotEmpty( listSlots ) )
232                 {
233                     LocalDate dateOfTheLastAppointment = listSlots.stream( ).map( Slot::getStartingDateTime ).max( LocalDateTime::compareTo ).get( )
234                             .toLocalDate( );
235                     // Check the number of days between this appointment and
236                     // the last appointment the user has taken
237                     LocalDate dateOfTheAppointment = appointmentDTO.getSlot( ).getStartingDateTime( ).toLocalDate( );
238                     if ( Math.abs( dateOfTheLastAppointment.until( dateOfTheAppointment, ChronoUnit.DAYS ) ) <= nbDaysBetweenTwoAppointments )
239                     {
240                         bCheckPassed = false;
241                     }
242                 }
243             }
244         }
245         return bCheckPassed;
246     }
247 
248     /**
249      * Get the slot of a user appointment
250      * 
251      * @param strEmail
252      *            the user's email
253      * @param idAppointment
254      *            the id of the appointment
255      * @return the list of slots
256      */
257     private static List<Slot> getSlotsByEmail( String strEmail, int idAppointment )
258     {
259         List<Slot> listSlots = new ArrayList<>( );
260         if ( StringUtils.isNotEmpty( strEmail ) )
261         {
262             // Looking for existing users with this email
263             List<User> listUsers = UserService.findUsersByEmail( strEmail );
264             if ( listUsers != null )
265             {
266                 List<Appointment> listAppointment = new ArrayList<>( );
267                 // For each User
268                 for ( User user : listUsers )
269                 {
270                     // looking for its appointment
271                     listAppointment.addAll( AppointmentService.findListAppointmentByUserId( user.getIdUser( ) ) );
272                 }
273 
274                 // If we modify an appointment, we remove the
275                 // appointment that we currently edit
276                 if ( idAppointment != 0 )
277                 {
278                     listAppointment = listAppointment.stream( ).filter( a -> a.getIdAppointment( ) != idAppointment ).collect( Collectors.toList( ) );
279                 }
280                 if ( CollectionUtils.isNotEmpty( listAppointment ) )
281                 {
282                     // I know we could have a join sql query, but I don't
283                     // want to join the appointment table with the slot
284                     // table, it's too big and not efficient
285 
286                     for ( Appointment appointment : listAppointment )
287                     {
288                         if ( !appointment.getIsCancelled( ) )
289                         {
290                             listSlots.add( SlotService.findSlotById( appointment.getIdSlot( ) ) );
291                         }
292                     }
293 
294                 }
295             }
296         }
297         return listSlots;
298     }
299 
300     /**
301      * Check that the number of appointments on a defined period is not above the maximum authorized
302      * 
303      * @param appointmentDTO
304      *            the appointment
305      * @param strEmail
306      *            the email of the user
307      * @param form
308      *            the form
309      * @return false if the number of appointments is above the maximum authorized on the defined period
310      */
311     public static boolean checkNbMaxAppointmentsOnAGivenPeriod( AppointmentDTO appointmentDTO, String strEmail, AppointmentFormDTO form )
312     {
313         boolean bCheckPassed = true;
314         int nbMaxAppointmentsPerUser = form.getNbMaxAppointmentsPerUser( );
315         int nbDaysForMaxAppointmentsPerUser = form.getNbDaysForMaxAppointmentsPerUser( );
316         if ( nbMaxAppointmentsPerUser != 0 )
317         {
318             List<Slot> listSlots = getSlotsByEmail( strEmail, appointmentDTO.getIdAppointment( ) );
319             if ( CollectionUtils.isNotEmpty( listSlots ) )
320             {
321                 // Filter fot the good form
322                 listSlots = listSlots.stream( ).filter( s -> s.getIdForm( ) == form.getIdForm( ) ).collect( Collectors.toList( ) );
323                 if ( CollectionUtils.isNotEmpty( listSlots ) )
324                 {
325                     // Get the date of the future appointment
326                     LocalDate dateOfTheAppointment = appointmentDTO.getSlot( ).getStartingDateTime( ).toLocalDate( );
327                     // Min starting date of the period
328                     LocalDate minStartingDateOfThePeriod = dateOfTheAppointment.minusDays( nbDaysForMaxAppointmentsPerUser );
329                     // Max ending date of the period
330                     LocalDate maxEndingDateOfThePeriod = dateOfTheAppointment.plusDays( nbDaysForMaxAppointmentsPerUser );
331                     // Keep only the slots that are in the min-max period
332                     listSlots = listSlots
333                             .stream( )
334                             .filter(
335                                     s -> s.getStartingDateTime( ).toLocalDate( ).isEqual( minStartingDateOfThePeriod )
336                                             || s.getStartingDateTime( ).toLocalDate( ).isAfter( minStartingDateOfThePeriod ) )
337                             .filter(
338                                     s -> s.getStartingDateTime( ).toLocalDate( ).isEqual( maxEndingDateOfThePeriod )
339                                             || s.getStartingDateTime( ).toLocalDate( ).isBefore( maxEndingDateOfThePeriod ) ).collect( Collectors.toList( ) );
340                     LocalDate startingDateOfThePeriod = null;
341                     LocalDate endingDateOfThePeriod = null;
342                     // For each slot
343                     for ( Slot slot : listSlots )
344                     {
345                         if ( slot.getStartingDateTime( ).toLocalDate( ).isBefore( dateOfTheAppointment ) )
346                         {
347                             startingDateOfThePeriod = slot.getStartingDateTime( ).toLocalDate( );
348                             endingDateOfThePeriod = startingDateOfThePeriod.plusDays( nbDaysForMaxAppointmentsPerUser );
349                         }
350                         if ( slot.getStartingDateTime( ).toLocalDate( ).isAfter( dateOfTheAppointment ) )
351                         {
352                             endingDateOfThePeriod = slot.getStartingDateTime( ).toLocalDate( );
353                             startingDateOfThePeriod = endingDateOfThePeriod.minusDays( nbDaysForMaxAppointmentsPerUser );
354                         }
355                         if ( slot.getStartingDateTime( ).toLocalDate( ).isEqual( dateOfTheAppointment ) )
356                         {
357                             startingDateOfThePeriod = endingDateOfThePeriod = slot.getStartingDateTime( ).toLocalDate( );
358                         }
359                         // Check the number of slots on the period
360                         final LocalDate startingDateOfPeriodToSearch = startingDateOfThePeriod;
361                         final LocalDate endingDateOfPeriodToSearch = endingDateOfThePeriod;
362                         int nbSlots = toIntExact( listSlots
363                                 .stream( )
364                                 .filter(
365                                         s -> ( s.getStartingDateTime( ).toLocalDate( ).equals( startingDateOfPeriodToSearch ) || s.getStartingDateTime( )
366                                                 .toLocalDate( ).isAfter( startingDateOfPeriodToSearch ) )
367                                                 && ( s.getStartingDateTime( ).toLocalDate( ).equals( endingDateOfPeriodToSearch ) || s.getStartingDateTime( )
368                                                         .toLocalDate( ).isBefore( endingDateOfPeriodToSearch ) ) ).count( ) );
369                         if ( nbSlots >= nbMaxAppointmentsPerUser )
370                         {
371                             bCheckPassed = false;
372                             break;
373                         }
374                     }
375                 }
376             }
377         }
378         return bCheckPassed;
379     }
380 
381     /**
382      * Check and validate all the rules for the number of booked seats asked
383      * 
384      * @param strNbBookedSeats
385      *            the number of booked seats
386      * @param form
387      *            the form
388      * @param nbRemainingPlaces
389      *            the number of remaining places on the slot asked
390      * @param locale
391      *            the locale
392      * @param listFormErrors
393      *            the list of errors that can be fill in with the errors found for the number of booked seats
394      * @return
395      */
396     public static int checkAndReturnNbBookedSeats( String strNbBookedSeats, AppointmentFormDTO form, AppointmentDTO appointmentDTO, Locale locale,
397             List<GenericAttributeError> listFormErrors )
398     {
399         int nbBookedSeats = 1;
400         if ( StringUtils.isEmpty( strNbBookedSeats ) && form.getMaxPeoplePerAppointment( ) > 1 )
401         {
402             GenericAttributeError genAttError = new GenericAttributeError( );
403             genAttError.setErrorMessage( I18nService.getLocalizedString( ERROR_MESSAGE_EMPTY_NB_BOOKED_SEAT, locale ) );
404             listFormErrors.add( genAttError );
405         }
406         if ( StringUtils.isNotEmpty( strNbBookedSeats ) )
407         {
408             try
409             {
410                 nbBookedSeats = Integer.parseInt( strNbBookedSeats );
411             }
412             catch( NumberFormatException | NullPointerException e )
413             {
414                 GenericAttributeError genAttError = new GenericAttributeError( );
415                 genAttError.setErrorMessage( I18nService.getLocalizedString( ERROR_MESSAGE_FORMAT_NB_BOOKED_SEAT, locale ) );
416                 listFormErrors.add( genAttError );
417             }
418         }
419         // if it's a new appointment, need to check if the number of booked
420         // seats is under or equal to the number of remaining places
421         // if it's a modification, need to check if the new number of booked
422         // seats is under or equal to the number of the remaining places + the
423         // previous number of booked seats of the appointment
424         if ( nbBookedSeats > appointmentDTO.getNbMaxPotentialBookedSeats( ) )
425         {
426             GenericAttributeError genAttError = new GenericAttributeError( );
427             genAttError.setErrorMessage( I18nService.getLocalizedString( ERROR_MESSAGE_ERROR_NB_BOOKED_SEAT, locale ) );
428             listFormErrors.add( genAttError );
429         }
430 
431         if ( nbBookedSeats == 0 )
432         {
433             GenericAttributeError genAttError = new GenericAttributeError( );
434             genAttError.setErrorMessage( I18nService.getLocalizedString( ERROR_MESSAGE_EMPTY_NB_BOOKED_SEAT, locale ) );
435             listFormErrors.add( genAttError );
436         }
437         return nbBookedSeats;
438     }
439 
440     /**
441      * Fill the appoinmentFront DTO with the given parameters
442      * 
443      * @param appointmentDTO
444      *            the appointmentFront DTO
445      * @param nbBookedSeats
446      *            the number of booked seats
447      * @param strEmail
448      *            the email of the user
449      * @param strFirstName
450      *            the first name of the user
451      * @param strLastName
452      *            the last name of the user
453      */
454     public static void fillAppointmentDTO( AppointmentDTO appointmentDTO, int nbBookedSeats, String strEmail, String strFirstName, String strLastName )
455     {
456         appointmentDTO.setDateOfTheAppointment( appointmentDTO.getSlot( ).getDate( ).format( Utilities.getFormatter( ) ) );
457         appointmentDTO.setNbBookedSeats( nbBookedSeats );
458         appointmentDTO.setEmail( strEmail );
459         appointmentDTO.setFirstName( strFirstName );
460         appointmentDTO.setLastName( strLastName );
461     }
462 
463     /**
464      * Validate the form and the additional entries of the form
465      * 
466      * @param appointmentDTO
467      *            the appointmentFron DTo to validate
468      * @param request
469      *            the request
470      * @param listFormErrors
471      *            the list of errors that can be fill with the errors found at the validation
472      */
473     public static void validateFormAndEntries( AppointmentDTO appointmentDTO, HttpServletRequest request, List<GenericAttributeError> listFormErrors )
474     {
475         Set<ConstraintViolation<AppointmentDTO>> listErrors = BeanValidationUtil.validate( appointmentDTO );
476         if ( CollectionUtils.isNotEmpty( listErrors ) )
477         {
478             for ( ConstraintViolation<AppointmentDTO> constraintViolation : listErrors )
479             {
480                 GenericAttributeError genAttError = new GenericAttributeError( );
481                 genAttError.setErrorMessage( I18nService.getLocalizedString( constraintViolation.getMessageTemplate( ), request.getLocale( ) ) );
482                 listFormErrors.add( genAttError );
483             }
484         }
485         List<Entry> listEntryFirstLevel = EntryHome.getEntryList( EntryService.buildEntryFilter( appointmentDTO.getIdForm( ) ) );
486         for ( Entry entry : listEntryFirstLevel )
487         {
488             listFormErrors.addAll( EntryService.getResponseEntry( request, entry.getIdEntry( ), request.getLocale( ), appointmentDTO ) );
489         }
490     }
491 
492     public static void fillInListResponseWithMapResponse( AppointmentDTO appointmentDTO )
493     {
494         Map<Integer, List<Response>> mapResponses = appointmentDTO.getMapResponsesByIdEntry( );
495         if ( mapResponses != null && !mapResponses.isEmpty( ) )
496         {
497             List<Response> listResponse = new ArrayList<Response>( );
498             for ( List<Response> listResponseByEntry : mapResponses.values( ) )
499             {
500                 listResponse.addAll( listResponseByEntry );
501             }
502             // appointmentDTO.clearMapResponsesByIdEntry();
503             appointmentDTO.setListResponse( listResponse );
504         }
505     }
506 
507     /**
508      * Build a list of response of the appointment
509      * 
510      * @param appointment
511      *            the appointment
512      * @param request
513      *            the request
514      * @param locale
515      *            the local
516      * @return a list of response
517      */
518     public static List<ResponseRecapDTO> buildListResponse( AppointmentDTO appointment, HttpServletRequest request, Locale locale )
519     {
520         List<ResponseRecapDTO> listResponseRecapDTO = new ArrayList<ResponseRecapDTO>( );
521         HashMap<Integer, List<ResponseRecapDTO>> mapResponse = new HashMap<>( );
522         if ( CollectionUtils.isNotEmpty( appointment.getListResponse( ) ) )
523         {
524             listResponseRecapDTO = new ArrayList<ResponseRecapDTO>( appointment.getListResponse( ).size( ) );
525             for ( Response response : appointment.getListResponse( ) )
526             {
527                 int nIndex = response.getEntry( ).getPosition( );
528                 IEntryTypeService entryTypeService = EntryTypeServiceManager.getEntryTypeService( response.getEntry( ) );
529                 ResponseRecapDTO responseRecapDTO = new ResponseRecapDTO( response, entryTypeService.getResponseValueForRecap( response.getEntry( ), request,
530                         response, locale ) );
531                 List<ResponseRecapDTO> listResponse = mapResponse.get( nIndex );
532                 if ( listResponse == null )
533                 {
534                     listResponse = new ArrayList<>( );
535                     mapResponse.put( nIndex, listResponse );
536                 }
537                 listResponse.add( responseRecapDTO );
538             }
539         }
540         for ( List<ResponseRecapDTO> listResponse : mapResponse.values( ) )
541         {
542             listResponseRecapDTO.addAll( listResponse );
543         }
544         return listResponseRecapDTO;
545     }
546 
547     /**
548      * Build the excel fil of the list of the appointments found in the manage appointment viw by filter
549      * 
550      * @param strIdForm
551      *            the form id
552      * @param response
553      *            the response
554      * @param locale
555      *            the local
556      * @param listAppointmentsDTO
557      *            the list of the appointments to input in the excel file
558      * @param stateService
559      *            the state service
560      */
561     public static void buildExcelFileWithAppointments( String strIdForm, HttpServletResponse response, Locale locale, List<AppointmentDTO> listAppointmentsDTO,
562             StateService stateService )
563     {
564         AppointmentFormDTO tmpForm = FormService.buildAppointmentFormLight( Integer.parseInt( strIdForm ) );
565         XSSFWorkbook workbook = new XSSFWorkbook( );
566         XSSFSheet sheet = workbook.createSheet( I18nService.getLocalizedString( KEY_RESOURCE_TYPE, locale ) );
567         List<Object [ ]> tmpObj = new ArrayList<Object [ ]>( );
568         EntryFilter entryFilter = new EntryFilter( );
569         entryFilter.setIdResource( Integer.valueOf( strIdForm ) );
570         List<Entry> listEntry = EntryHome.getEntryList( entryFilter );
571         Map<Integer, String> mapDefaultValueGenAttBackOffice = new HashMap<Integer, String>( );
572         for ( Entry e : listEntry )
573         {
574             if ( e.isOnlyDisplayInBack( ) )
575             {
576                 e = EntryHome.findByPrimaryKey( e.getIdEntry( ) );
577                 if ( e.getFields( ) != null && e.getFields( ).size( ) == 1 && !StringUtils.isEmpty( e.getFields( ).get( 0 ).getValue( ) ) )
578                 {
579                     mapDefaultValueGenAttBackOffice.put( e.getIdEntry( ), e.getFields( ).get( 0 ).getValue( ) );
580                 }
581                 else
582                     if ( e.getFields( ) != null )
583                     {
584                         for ( Field field : e.getFields( ) )
585                         {
586                             if ( field.isDefaultValue( ) )
587                             {
588                                 mapDefaultValueGenAttBackOffice.put( e.getIdEntry( ), field.getValue( ) );
589                             }
590                         }
591                     }
592             }
593         }
594         int nTaille = 10 + ( listEntry.size( ) + 1 );
595         if ( tmpForm != null )
596         {
597             int nIndex = 0;
598             Object [ ] strWriter = new String [ 1];
599             strWriter [0] = tmpForm.getTitle( );
600             tmpObj.add( strWriter );
601             Object [ ] strInfos = new String [ nTaille];
602             strInfos [0] = I18nService.getLocalizedString( KEY_COLUMN_LAST_NAME, locale );
603             strInfos [1] = I18nService.getLocalizedString( KEY_COLUMN_FISRT_NAME, locale );
604             strInfos [2] = I18nService.getLocalizedString( KEY_COLUMN_EMAIL, locale );
605             strInfos [3] = I18nService.getLocalizedString( KEY_COLUMN_DATE_APPOINTMENT, locale );
606             strInfos [4] = I18nService.getLocalizedString( KEY_TIME_START, locale );
607             strInfos [5] = I18nService.getLocalizedString( KEY_TIME_END, locale );
608             strInfos [6] = I18nService.getLocalizedString( KEY_COLUMN_ADMIN, locale );
609             strInfos [7] = I18nService.getLocalizedString( KEY_COLUMN_STATUS, locale );
610             strInfos [8] = I18nService.getLocalizedString( KEY_COLUMN_STATE, locale );
611             strInfos [9] = I18nService.getLocalizedString( KEY_COLUMN_NB_BOOKED_SEATS, locale );
612             nIndex = 1;
613             if ( listEntry.size( ) > 0 )
614             {
615                 for ( Entry e : listEntry )
616                 {
617                     strInfos [10 + nIndex] = e.getTitle( );
618                     nIndex++;
619                 }
620             }
621             tmpObj.add( strInfos );
622         }
623         if ( listAppointmentsDTO != null )
624         {
625             for ( AppointmentDTO appointmentDTO : listAppointmentsDTO )
626             {
627                 int nIndex = 0;
628                 Object [ ] strWriter = new String [ nTaille];
629                 strWriter [0] = appointmentDTO.getLastName( );
630                 strWriter [1] = appointmentDTO.getFirstName( );
631                 strWriter [2] = appointmentDTO.getEmail( );
632                 strWriter [3] = appointmentDTO.getDateOfTheAppointment( );
633                 strWriter [4] = appointmentDTO.getStartingTime( ).toString( );
634                 strWriter [5] = appointmentDTO.getEndingTime( ).toString( );
635                 strWriter [6] = appointmentDTO.getAdminUser( );
636                 String status = I18nService.getLocalizedString( AppointmentDTO.PROPERTY_APPOINTMENT_STATUS_RESERVED, locale );
637                 if ( appointmentDTO.getIsCancelled( ) )
638                 {
639                     status = I18nService.getLocalizedString( AppointmentDTO.PROPERTY_APPOINTMENT_STATUS_UNRESERVED, locale );
640                 }
641                 strWriter [7] = status;
642                 State stateAppointment = stateService.findByResource( appointmentDTO.getIdAppointment( ), Appointment.APPOINTMENT_RESOURCE_TYPE,
643                         tmpForm.getIdWorkflow( ) );
644                 String strState = StringUtils.EMPTY;
645                 if ( stateAppointment != null )
646                 {
647                     appointmentDTO.setState( stateAppointment );
648                     strState = stateAppointment.getName( );
649                 }
650                 strWriter [8] = strState;
651                 nIndex = 1;
652                 strWriter [9] = Integer.toString( appointmentDTO.getNbBookedSeats( ) );
653                 List<Integer> listIdResponse = AppointmentResponseService.findListIdResponse( appointmentDTO.getIdAppointment( ) );
654                 List<Response> listResponses = new ArrayList<Response>( );
655                 for ( int nIdResponse : listIdResponse )
656                 {
657                     Response resp = ResponseHome.findByPrimaryKey( nIdResponse );
658                     if ( resp != null )
659                     {
660                         listResponses.add( resp );
661                     }
662                 }
663                 for ( Entry e : listEntry )
664                 {
665                     Integer key = e.getIdEntry( );
666                     StringBuffer strValue = new StringBuffer( StringUtils.EMPTY );
667                     String strPrefix = StringUtils.EMPTY;
668                     for ( Response resp : listResponses )
669                     {
670                         String strRes = StringUtils.EMPTY;
671                         if ( key.equals( resp.getEntry( ).getIdEntry( ) ) )
672                         {
673                             Field f = resp.getField( );
674                             int nfield = 0;
675                             if ( f != null )
676                             {
677                                 nfield = f.getIdField( );
678                                 Field field = FieldHome.findByPrimaryKey( nfield );
679                                 if ( field != null )
680                                 {
681                                     strRes = field.getTitle( );
682                                 }
683                             }
684                             else
685                             {
686                                 strRes = resp.getResponseValue( );
687                             }
688                         }
689                         if ( ( strRes != null ) && !strRes.isEmpty( ) )
690                         {
691                             strValue.append( strPrefix + strRes );
692                             strPrefix = CONSTANT_COMMA;
693                         }
694                     }
695                     if ( strValue.toString( ).isEmpty( ) && mapDefaultValueGenAttBackOffice.containsKey( key ) )
696                     {
697                         strValue.append( mapDefaultValueGenAttBackOffice.get( key ) );
698                     }
699                     if ( !strValue.toString( ).isEmpty( ) )
700                     {
701                         strWriter [10 + nIndex] = strValue.toString( );
702                     }
703                     nIndex++;
704                 }
705                 tmpObj.add( strWriter );
706             }
707         }
708         int nRownum = 0;
709         for ( Object [ ] myObj : tmpObj )
710         {
711             Row row = sheet.createRow( nRownum++ );
712             int nCellnum = 0;
713             for ( Object strLine : myObj )
714             {
715                 Cell cell = row.createCell( nCellnum++ );
716                 if ( strLine instanceof String )
717                 {
718                     cell.setCellValue( (String) strLine );
719                 }
720                 else
721                     if ( strLine instanceof Boolean )
722                     {
723                         cell.setCellValue( (Boolean) strLine );
724                     }
725                     else
726                         if ( strLine instanceof Date )
727                         {
728                             cell.setCellValue( (Date) strLine );
729                         }
730                         else
731                             if ( strLine instanceof Double )
732                             {
733                                 cell.setCellValue( (Double) strLine );
734                             }
735             }
736         }
737         try
738         {
739             String now = new SimpleDateFormat( "yyyyMMdd-hhmm" ).format( GregorianCalendar.getInstance( locale ).getTime( ) ) + "_"
740                     + I18nService.getLocalizedString( KEY_RESOURCE_TYPE, locale ) + EXCEL_FILE_EXTENSION;
741             response.setContentType( EXCEL_MIME_TYPE );
742             response.setHeader( "Content-Disposition", "attachment; filename=\"" + now + "\";" );
743             response.setHeader( "Pragma", "public" );
744             response.setHeader( "Expires", "0" );
745             response.setHeader( "Cache-Control", "must-revalidate,post-check=0,pre-check=0" );
746             OutputStream os = response.getOutputStream( );
747             workbook.write( os );
748             os.close( );
749             workbook.close( );
750         }
751         catch( IOException e )
752         {
753             AppLogService.error( e );
754         }
755     }
756 
757     /**
758      * Kill the lock timer on a slot
759      * 
760      * @param request
761      *            the request
762      */
763     public static void killTimer( HttpServletRequest request )
764     {
765         Timer timer = (Timer) request.getSession( ).getAttribute( SESSION_TIMER_SLOT );
766         if ( timer != null )
767         {
768             timer.cancel( );
769             request.getSession( ).removeAttribute( SESSION_TIMER_SLOT );
770         }
771     }
772 
773     /**
774      * Create a timer on a slot
775      * 
776      * @param slot
777      *            the slot
778      * @param appointmentDTO
779      *            the appointment
780      * @param maxPeoplePerAppointment
781      *            the max people per appointment
782      * @return the timer
783      */
784     public static Timer putTimerInSession( HttpServletRequest request, Slot slot, AppointmentDTO appointmentDTO, int maxPeoplePerAppointment )
785     {
786         SlotEditTask slotEditTask = new SlotEditTask( );
787         int nbPotentialRemainingPlaces = slot.getNbPotentialRemainingPlaces( );
788         int nbPotentialPlacesTaken = Math.min( nbPotentialRemainingPlaces, maxPeoplePerAppointment );
789         appointmentDTO.setNbMaxPotentialBookedSeats( nbPotentialPlacesTaken );
790         slot.setNbPotentialRemainingPlaces( nbPotentialRemainingPlaces - nbPotentialPlacesTaken );
791         SlotService.updateSlot( slot );
792         slotEditTask.setNbPlacesTaken( nbPotentialPlacesTaken );
793         slotEditTask.setIdSlot( slot.getIdSlot( ) );
794         TimerForLockOnSlot timer = new TimerForLockOnSlot( );
795         long delay = TimeUnit.MINUTES.toMillis( AppPropertiesService.getPropertyInt( PROPERTY_DEFAULT_EXPIRED_TIME_EDIT_APPOINTMENT, 1 ) );
796         timer.schedule( slotEditTask, delay );
797         request.getSession( ).setAttribute( AppointmentUtilities.SESSION_TIMER_SLOT, timer );
798         return timer;
799     }
800 
801     /**
802      * Get Form Permissions
803      * 
804      * @param listForms
805      * @param request
806      * @return
807      */
808     public static String [ ][ ] getPermissions( List<AppointmentFormDTO> listForms, AdminUser user )
809     {
810         String [ ][ ] retour = new String [ listForms.size( )] [ 6];
811         int nI = 0;
812 
813         for ( AppointmentFormDTO tmpForm : listForms )
814         {
815             String [ ] strRetour = new String [ 7];
816             strRetour [0] = String.valueOf( RBACService.isAuthorized( AppointmentFormDTO.RESOURCE_TYPE, String.valueOf( tmpForm.getIdForm( ) ),
817                     AppointmentResourceIdService.PERMISSION_VIEW_APPOINTMENT, user ) );
818             strRetour [1] = String.valueOf( RBACService.isAuthorized( AppointmentFormDTO.RESOURCE_TYPE, String.valueOf( tmpForm.getIdForm( ) ),
819                     AppointmentResourceIdService.PERMISSION_MODIFY_ADVANCED_SETTING_FORM, user ) );
820             strRetour [2] = String.valueOf( RBACService.isAuthorized( AppointmentFormDTO.RESOURCE_TYPE, String.valueOf( tmpForm.getIdForm( ) ),
821                     AppointmentResourceIdService.PERMISSION_MODIFY_FORM, user ) );
822             strRetour [3] = String.valueOf( RBACService.isAuthorized( AppointmentFormDTO.RESOURCE_TYPE, String.valueOf( tmpForm.getIdForm( ) ),
823                     AppointmentResourceIdService.PERMISSION_MODIFY_FORM, user ) );
824             strRetour [4] = String.valueOf( RBACService.isAuthorized( AppointmentFormDTO.RESOURCE_TYPE, String.valueOf( tmpForm.getIdForm( ) ),
825                     AppointmentResourceIdService.PERMISSION_CHANGE_STATE, user ) );
826             strRetour [5] = String.valueOf( RBACService.isAuthorized( AppointmentFormDTO.RESOURCE_TYPE, String.valueOf( tmpForm.getIdForm( ) ),
827                     AppointmentResourceIdService.PERMISSION_DELETE_FORM, user ) );
828             retour [nI++] = strRetour;
829         }
830 
831         return retour;
832     }
833 
834     /**
835      * Return the min starting time to display
836      * 
837      * @param minStartingTime
838      *            the min starting time
839      * @return 00 if the minstarting time is under 30, 30 otherwise
840      */
841     public static LocalTime getMinTimeToDisplay( LocalTime minStartingTime )
842     {
843         LocalTime minStartingTimeToDisplay;
844         if ( minStartingTime.getMinute( ) < THIRTY_MINUTES )
845         {
846             minStartingTimeToDisplay = LocalTime.of( minStartingTime.getHour( ), 0 );
847         }
848         else
849         {
850             minStartingTimeToDisplay = LocalTime.of( minStartingTime.getHour( ), THIRTY_MINUTES );
851         }
852         return minStartingTimeToDisplay;
853     }
854 
855     /**
856      * Return the max ending time to display
857      * 
858      * @param maxEndingTime
859      *            the max ending time
860      * @return 30 if the max ending time is under 30, otherwise the next hour
861      */
862     public static LocalTime getMaxTimeToDisplay( LocalTime maxEndingTime )
863     {
864         LocalTime maxEndingTimeToDisplay;
865         if ( maxEndingTime.getMinute( ) < THIRTY_MINUTES )
866         {
867             maxEndingTimeToDisplay = LocalTime.of( maxEndingTime.getHour( ), THIRTY_MINUTES );
868         }
869         else
870         {
871             maxEndingTimeToDisplay = LocalTime.of( maxEndingTime.getHour( ) + 1, 0 );
872         }
873         return maxEndingTimeToDisplay;
874     }
875 
876     /**
877      * Check if there are appointments impacted by the new week definition
878      * 
879      * @param listAppointment
880      *            the list of appointments
881      * @param nIdForm
882      *            the form Id
883      * @param dateOfModification
884      *            the date of modification (date of apply of the new week definition)
885      * @param appointmentForm
886      *            the appointment form
887      * @return true if there are appointments impacted
888      */
889     public static boolean checkNoAppointmentsImpacted( List<Appointment> listAppointment, int nIdForm, LocalDate dateOfModification,
890             AppointmentFormDTO appointmentForm )
891     {
892         boolean bNoAppointmentsImpacted = true;
893         // Find the previous WeekDefinition
894         WeekDefinition previousWeekDefinition = WeekDefinitionService.findWeekDefinitionByIdFormAndClosestToDateOfApply( nIdForm, dateOfModification );
895         // Find the previous reservation rule
896         ReservationRule previousReservationRule = ReservationRuleService.findReservationRuleByIdFormAndClosestToDateOfApply( nIdForm, dateOfModification );
897         // Build the previous appointment form with the previous week
898         // definition and the previous reservation rule
899         AppointmentFormDTO previousAppointmentForm = FormService.buildAppointmentForm( nIdForm, previousReservationRule.getIdReservationRule( ),
900                 previousWeekDefinition.getIdWeekDefinition( ) );
901         // Need to check if the new definition week has more open days.
902         List<DayOfWeek> previousOpenDays = WorkingDayService.getOpenDays( previousAppointmentForm );
903         List<DayOfWeek> newOpenDays = WorkingDayService.getOpenDays( appointmentForm );
904         // If new open days
905         if ( newOpenDays.containsAll( previousOpenDays ) )
906         {
907             // Nothing to check
908         }
909         else
910         {
911             // Else we remove all the corresponding days
912             previousOpenDays.removeAll( newOpenDays );
913             // For the remaining days
914             // for each appointment, need to check if the appointment is
915             // not in the remaining open days
916             boolean bAppointmentOnOpenDays = false;
917             for ( Appointment appointment : listAppointment )
918             {
919                 Slot tempSlot = SlotService.findSlotById( appointment.getIdSlot( ) );
920                 if ( previousOpenDays.contains( tempSlot.getStartingDateTime( ).getDayOfWeek( ) ) )
921                 {
922                     bAppointmentOnOpenDays = true;
923                     break;
924                 }
925             }
926             bNoAppointmentsImpacted = !bAppointmentOnOpenDays;
927         }
928         LocalTime newStartingTime = LocalTime.parse( appointmentForm.getTimeStart( ) );
929         LocalTime newEndingTime = LocalTime.parse( appointmentForm.getTimeEnd( ) );
930         LocalTime oldStartingTime = LocalTime.parse( previousAppointmentForm.getTimeStart( ) );
931         LocalTime oldEndingTime = LocalTime.parse( previousAppointmentForm.getTimeEnd( ) );
932         // If we have changed the duration of an appointment
933         if ( appointmentForm.getDurationAppointments( ) != previousAppointmentForm.getDurationAppointments( ) )
934         {
935             bNoAppointmentsImpacted = false;
936         }
937         // We have change the open hours
938         // if the time slot is reduced
939         if ( newStartingTime.isAfter( oldStartingTime ) || newEndingTime.isBefore( oldEndingTime ) )
940         {
941             bNoAppointmentsImpacted = false;
942         }
943 
944         return bNoAppointmentsImpacted;
945     }
946 
947     /**
948      * Check that there is no validated appointments on a slot
949      * 
950      * @param slot
951      *            the slot
952      * @return true if there are no validated appointments on this slot, false otherwise
953      */
954     public static boolean checkNoValidatedAppointmentsOnThisSlot( Slot slot )
955     {
956         boolean bNoValidatedAppointmentsOnThisSlot = true;
957         List<Appointment> listAppointmentsOnThisSlot = AppointmentService.findListAppointmentBySlot( slot.getIdSlot( ) );
958         if ( CollectionUtils.isNotEmpty( listAppointmentsOnThisSlot ) )
959         {
960             listAppointmentsOnThisSlot = listAppointmentsOnThisSlot.stream( ).filter( a -> a.getIsCancelled( ) == false ).collect( Collectors.toList( ) );
961         }
962         if ( CollectionUtils.isNotEmpty( listAppointmentsOnThisSlot ) )
963         {
964             bNoValidatedAppointmentsOnThisSlot = false;
965         }
966         return bNoValidatedAppointmentsOnThisSlot;
967     }
968 
969     /**
970      * Return the slots impacted by the modification of this time slot
971      * 
972      * @param timeSlot
973      *            the time slot
974      * @param nIdForm
975      *            the form id
976      * @param nIdWeekDefinition
977      *            the week definition id
978      * @param bShiftSlot
979      *            the boolean value for the shift
980      * @return the list of slots impacted
981      */
982     public static List<Slot> findSlotsImpactedByThisTimeSlot( TimeSlot timeSlot, int nIdForm, int nIdWeekDefinition, boolean bShiftSlot )
983     {
984         List<Slot> listSlotsImpacted = new ArrayList<>( );
985         LocalDate maxDate = null;
986         // Get the weekDefinition that is currently modified
987         WeekDefinition currentModifiedWeekDefinition = WeekDefinitionService.findWeekDefinitionById( nIdWeekDefinition );
988         // Find the next weekDefinition, if exist, to have the max date to
989         // search slots with appointments
990         WeekDefinition nextWeekDefinition = WeekDefinitionService.findNextWeekDefinition( nIdForm, currentModifiedWeekDefinition.getDateOfApply( ) );
991         if ( nextWeekDefinition != null )
992         {
993             maxDate = nextWeekDefinition.getDateOfApply( );
994         }
995         else
996         {
997             // If there is no next weekDefinition
998             // Get the ending validity date of the form
999             Form form = FormService.findFormLightByPrimaryKey( nIdForm );
1000             if ( form.getEndingValidityDate( ) != null )
1001             {
1002                 maxDate = form.getEndingValidityDate( );
1003             }
1004             else
1005             {
1006                 // If there is no ending validity date
1007                 // Find the slot with the max date
1008                 Slot slotWithMaxDate = SlotService.findSlotWithMaxDate( nIdForm );
1009                 if ( slotWithMaxDate != null && slotWithMaxDate.getStartingDateTime( ) != null )
1010                 {
1011                     maxDate = slotWithMaxDate.getStartingDateTime( ).toLocalDate( );
1012                 }
1013             }
1014         }
1015         if ( maxDate != null )
1016         {
1017             // We have an upper bound to search with
1018             List<Slot> listSlots = SlotService.findSlotsByIdFormAndDateRange( nIdForm, currentModifiedWeekDefinition.getDateOfApply( ).atStartOfDay( ),
1019                     maxDate.atTime( LocalTime.MAX ) );
1020             // Need to check if the modification of the time slot or the typical
1021             // week impacts these slots
1022             WorkingDay workingDay = WorkingDayService.findWorkingDayLightById( timeSlot.getIdWorkingDay( ) );
1023             // Filter all the slots with the working day and the starting time
1024             // ending time of the time slot
1025             // The begin time of the slot can be before or after the begin time
1026             // of the time slot
1027             // and the ending time of the slot can be before or after the ending
1028             // time of the time slot (specific slot)
1029 
1030             // If shiftTimeSlot is checked, need to check all the slots impacted
1031             // until the end of the day
1032             if ( bShiftSlot )
1033             {
1034                 listSlotsImpacted = listSlots
1035                         .stream( )
1036                         .filter(
1037                                 slot -> ( ( slot.getStartingDateTime( ).getDayOfWeek( ) == DayOfWeek.of( workingDay.getDayOfWeek( ) ) ) && ( !slot
1038                                         .getStartingTime( ).isBefore( timeSlot.getStartingTime( ) ) || ( slot.getStartingTime( ).isBefore(
1039                                         timeSlot.getStartingTime( ) ) && ( slot.getEndingTime( ).isAfter( timeSlot.getStartingTime( ) ) ) ) ) ) )
1040                         .collect( Collectors.toList( ) );
1041             }
1042             else
1043             {
1044                 listSlotsImpacted = listSlots
1045                         .stream( )
1046                         .filter(
1047                                 slot -> ( slot.getStartingDateTime( ).getDayOfWeek( ) == DayOfWeek.of( workingDay.getDayOfWeek( ) ) )
1048                                         && ( slot.getStartingTime( ).equals( timeSlot.getStartingTime( ) )
1049                                                 || ( slot.getStartingTime( ).isBefore( timeSlot.getStartingTime( ) ) && ( slot.getEndingTime( )
1050                                                         .isAfter( timeSlot.getStartingTime( ) ) ) ) || ( slot.getStartingTime( ).isAfter(
1051                                                 timeSlot.getStartingTime( ) ) && ( !slot.getEndingTime( ).isAfter( timeSlot.getEndingTime( ) ) ) ) ) )
1052                         .collect( Collectors.toList( ) );
1053             }
1054         }
1055         return listSlotsImpacted;
1056     }
1057 
1058     /**
1059      * D├ętermines if the slot is AM or PM.
1060      *
1061      * @return a boolean
1062      */
1063     public static boolean isSlotAM( Slot slot )
1064     {
1065         String maxAM = AppPropertiesService.getProperty( KEY_HORAIRE_MAX_AM );
1066         if ( maxAM != null && maxAM.length() == 3 )
1067         {
1068             maxAM = "0" + maxAM;
1069         }
1070         LocalTime maxTimeAM = LocalTime.parse( maxAM, DateTimeFormatter.ofPattern("HHmm") );
1071         return slot.getStartingTime( ).isBefore( maxTimeAM );
1072     }
1073 
1074 }