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.service;
35  
36  import java.time.LocalDate;
37  import java.time.LocalDateTime;
38  import java.time.LocalTime;
39  import java.time.temporal.ChronoUnit;
40  import java.util.ArrayList;
41  import java.util.Collections;
42  import java.util.HashMap;
43  import java.util.HashSet;
44  import java.util.Iterator;
45  import java.util.List;
46  import java.util.Locale;
47  import java.util.Map;
48  import java.util.Optional;
49  import java.util.Set;
50  import java.util.concurrent.ConcurrentHashMap;
51  import java.util.concurrent.ConcurrentMap;
52  import java.util.concurrent.TimeUnit;
53  import java.util.concurrent.locks.Lock;
54  import java.util.concurrent.locks.ReentrantLock;
55  import java.util.stream.Collectors;
56  
57  import javax.servlet.http.HttpServletRequest;
58  import org.apache.commons.collections.CollectionUtils;
59  import fr.paris.lutece.plugins.appointment.business.appointment.Appointment;
60  import fr.paris.lutece.plugins.appointment.business.appointment.AppointmentSlot;
61  import fr.paris.lutece.plugins.appointment.business.form.Form;
62  import fr.paris.lutece.plugins.appointment.business.planning.TimeSlot;
63  import fr.paris.lutece.plugins.appointment.business.planning.WeekDefinition;
64  import fr.paris.lutece.plugins.appointment.business.planning.WorkingDay;
65  import fr.paris.lutece.plugins.appointment.business.rule.ReservationRule;
66  import fr.paris.lutece.plugins.appointment.business.slot.Period;
67  import fr.paris.lutece.plugins.appointment.business.slot.Slot;
68  import fr.paris.lutece.plugins.appointment.business.slot.SlotHome;
69  import fr.paris.lutece.plugins.appointment.business.user.User;
70  import fr.paris.lutece.plugins.appointment.exception.AppointmentSavedException;
71  import fr.paris.lutece.plugins.appointment.exception.SlotEditTaskExpiredTimeException;
72  import fr.paris.lutece.plugins.appointment.exception.SlotFullException;
73  import fr.paris.lutece.plugins.appointment.service.listeners.AppointmentListenerManager;
74  import fr.paris.lutece.plugins.appointment.service.listeners.SlotListenerManager;
75  import fr.paris.lutece.plugins.appointment.service.lock.SlotEditTask;
76  import fr.paris.lutece.plugins.appointment.web.dto.AppointmentDTO;
77  import fr.paris.lutece.plugins.genericattributes.business.Response;
78  import fr.paris.lutece.plugins.genericattributes.business.ResponseHome;
79  import fr.paris.lutece.portal.business.user.AdminUser;
80  import fr.paris.lutece.portal.service.admin.AdminUserService;
81  import fr.paris.lutece.portal.service.util.AppLogService;
82  import fr.paris.lutece.portal.service.workflow.WorkflowService;
83  import fr.paris.lutece.portal.web.l10n.LocaleService;
84  import fr.paris.lutece.util.sql.TransactionManager;
85  
86  public final class SlotSafeService
87  {
88  
89      private static final ConcurrentMap<Integer, Lock> _listSlot = new ConcurrentHashMap<>( );
90      private static final ConcurrentMap<Integer, Object> _lockFormId = new ConcurrentHashMap<>( );
91  
92      /**
93       * Private constructor - this class does not need to be instantiated
94       */
95      private SlotSafeService( )
96      {
97      }
98  
99      /**
100      * Get the slot in memory
101      * 
102      * @return Map of slot
103      */
104     public static Map<Integer, Lock> getListSlotInMemory( )
105     {
106 
107         return _listSlot;
108     }
109 
110     /**
111      * get lock for slot
112      * 
113      * @param nIdSlot
114      *            the Id Slot
115      * @return return the lock
116      */
117     public static Lock getLockOnSlot( int nIdSlot )
118     {
119         if ( nIdSlot == 0 )
120         {
121             return new ReentrantLock( );
122         }
123         _listSlot.putIfAbsent( nIdSlot, new ReentrantLock( ) );
124         return _listSlot.get( nIdSlot );
125     }
126 
127     /**
128      * remove slot in map memory
129      * 
130      * @param nIdSlot
131      *            the Id Slot
132      */
133     public static void removeSlotInMemory( int nIdSlot )
134     {
135 
136         _listSlot.remove( nIdSlot );
137     }
138 
139     /**
140      * get lock for form
141      * 
142      * @param nIdform
143      *            Id from
144      * @return return lock
145      */
146     private static Object getLockOnForm( int nIdform )
147     {
148         _lockFormId.putIfAbsent( nIdform, new Object( ) );
149         return _lockFormId.get( nIdform );
150     }
151 
152     /**
153      * Create slot
154      * 
155      * @param slot
156      * @return slot
157      */
158     public static Slot../../../../../../fr/paris/lutece/plugins/appointment/business/slot/Slot.html#Slot">Slot createSlot( Slot slot )
159     {
160         Object formLock = getLockOnForm( slot.getIdForm( ) );
161         synchronized( formLock )
162         {
163             Slot slotSaved = null;
164             HashMap<LocalDateTime, Slot> slotInDbMap = SlotService.buildMapSlotsByIdFormAndDateRangeWithDateForKey( slot.getIdForm( ),
165                     slot.getStartingDateTime( ), slot.getEndingDateTime( ) );
166             if ( !slotInDbMap.isEmpty( ) )
167             {
168                 slotSaved = slotInDbMap.get( slot.getStartingDateTime( ) );
169             }
170             else
171             {
172 
173                 slotSaved = SlotHome.create( slot );
174                 SlotListenerManager.notifyListenersSlotCreation( slot.getIdSlot( ) );
175             }
176 
177             return slotSaved;
178 
179         }
180     }
181 
182     /**
183      * 
184      * Increment max capacity
185      * 
186      * @param nIdForm
187      *            the Id form
188      * @param nIncrementingValue
189      *            the incrementing value
190      * @param startindDateTime
191      *            the starting date Time
192      * @param endingDateTime
193      *            the ending Date time
194      * @param lace
195      *            the lace
196      */
197     public static void incrementMaxCapacity( int nIdForm, int nIncrementingValue, LocalDateTime startindDateTime, LocalDateTime endingDateTime, boolean lace )
198     {
199         int index = 0;
200         List<WeekDefinition> listWeekDefinition = WeekDefinitionService.findListWeekDefinition( nIdForm );
201         Map<WeekDefinition, ReservationRule> mapReservationRule = ReservationRuleService.findAllReservationRule( nIdForm, listWeekDefinition );
202         List<Slot> listSlot = SlotService.buildListSlot( nIdForm, mapReservationRule, startindDateTime.toLocalDate( ), endingDateTime.toLocalDate( ) );
203         listSlot = listSlot.stream( )
204                 .filter( slt -> slt.getEndingDateTime( ).isBefore( endingDateTime ) && slt.getEndingDateTime( ).isAfter( startindDateTime ) )
205                 .collect( Collectors.toList( ) );
206         for ( Slot slot : listSlot )
207         {
208             if ( !lace )
209             {
210                 incrementMaxCapacity( nIncrementingValue, slot );
211             }
212             else
213             {
214                 if ( index % 2 == 0 )
215                 {
216                     incrementMaxCapacity( nIncrementingValue, slot );
217                 }
218                 index++;
219             }
220         }
221     }
222 
223     /**
224      * Incrementing max capacity
225      * 
226      * @param nIncrementingValue
227      *            the incrementing value
228      * @param slot
229      *            the slot
230      */
231     private static void incrementMaxCapacity( int nIncrementingValue, Slot slot )
232     {
233         Slot editSlot = null;
234 
235         if ( slot.getIdSlot( ) == 0 )
236         {
237             editSlot = createSlot( slot );
238         }
239         else
240         {
241             editSlot = slot;
242         }
243         Lock lock = getLockOnSlot( editSlot.getIdSlot( ) );
244         lock.lock( );
245         try
246         {
247             editSlot = SlotService.findSlotById( editSlot.getIdSlot( ) );
248 
249             editSlot.setMaxCapacity( editSlot.getMaxCapacity( ) + nIncrementingValue );
250             editSlot.setNbPotentialRemainingPlaces( editSlot.getNbPotentialRemainingPlaces( ) + nIncrementingValue );
251             editSlot.setNbRemainingPlaces( editSlot.getNbRemainingPlaces( ) + nIncrementingValue );
252             editSlot.setIsSpecific( SlotService.isSpecificSlot( editSlot ) );
253             saveSlot( editSlot );
254         }
255         finally
256         {
257 
258             lock.unlock( );
259         }
260 
261     }
262 
263     /**
264      * Update potential remaining places
265      * 
266      * @param task
267      *            the task timer
268      */
269     public static Slot incrementPotentialRemainingPlaces( SlotEditTask task )
270     {
271         Lock lock = getLockOnSlot( task.getIdSlot( ) );
272         Slot slot;
273         lock.lock( );
274         try
275         {
276             slot = SlotService.findSlotById( task.getIdSlot( ) );
277             if ( slot != null )
278             {
279 
280                 int nNewPotentialRemainingPlaces = Math.min( slot.getNbPotentialRemainingPlaces( ) + task.getNbPlacesTaken( ), slot.getNbRemainingPlaces( ) );
281                 slot.setNbPotentialRemainingPlaces( nNewPotentialRemainingPlaces );
282                 SlotHome.updatePotentialRemainingPlaces( nNewPotentialRemainingPlaces, slot.getIdSlot( ) );
283                 SlotListenerManager.notifyListenersSlotChange( slot.getIdSlot( ) );
284 
285             }
286         }
287         finally
288         {
289             lock.unlock( );
290 
291         }
292         return slot;
293     }
294 
295     /**
296      * Update potential remaining places
297      * 
298      * @param nbPotentialRemainingPlaces
299      *            the nbPotentialRemainingPlaces
300      * @param nIdSlot
301      *            the is Slot
302      */
303     public static void decrementPotentialRemainingPlaces( int nbPotentialRemainingPlaces, int nIdSlot )
304     {
305 
306         Lock lock = getLockOnSlot( nIdSlot );
307         lock.lock( );
308         try
309         {
310             Slot slot = SlotService.findSlotById( nIdSlot );
311             if ( slot != null )
312             {
313                 int nNewPotentialRemainingPlaces = slot.getNbPotentialRemainingPlaces( ) - nbPotentialRemainingPlaces;
314                 slot.setNbPotentialRemainingPlaces( nNewPotentialRemainingPlaces );
315                 SlotHome.updatePotentialRemainingPlaces( nNewPotentialRemainingPlaces, nIdSlot );
316                 SlotListenerManager.notifyListenersSlotChange( slot.getIdSlot( ) );
317 
318             }
319 
320         }
321         finally
322         {
323 
324             lock.unlock( );
325         }
326 
327     }
328 
329     /**
330      * Save a slot in database
331      * 
332      * @param slot
333      *            the slot to save
334      * @return the slot saved
335      */
336     public static int saveAppointment( AppointmentDTO appointmentDTO, HttpServletRequest request )
337     {
338         Locale locale = null;
339         User user = appointmentDTO.getUser( );
340         List<Lock> listLock = new ArrayList<>( );
341         // change date appointment
342         boolean isReport = appointmentDTO.getIdAppointment( ) != 0;
343         if ( appointmentDTO.getIsSaved( ) )
344         {
345             throw new AppointmentSavedException( "Appointment is already saved " );
346         }
347         if ( request != null )
348         {
349             locale = LocaleService.getContextUserLocale( request );
350 
351             for ( Slot slt : appointmentDTO.getSlot( ) )
352             {
353                 if ( AppointmentUtilities.isEditSlotTaskExpiredTime( request, slt.getIdSlot( ) ) )
354                     throw new SlotEditTaskExpiredTimeException( "appointment edit expired time" );
355             }
356         }
357         AppointmentService.buildListAppointmentSlot( appointmentDTO );
358         TransactionManager.beginTransaction( AppointmentPlugin.getPlugin( ) );
359         try
360         {
361             Set<Integer> listSlotUpdated = saveSlots( appointmentDTO, listLock, request );
362             if ( !isReport )
363             {
364                 user = UserService.saveUser( appointmentDTO );
365             }
366             // Create or update the appointment
367             Appointment appointment = AppointmentService.buildAndCreateAppointment( appointmentDTO, user );
368             if ( !isReport && CollectionUtils.isNotEmpty( appointmentDTO.getListResponse( ) ) )
369             {
370                 for ( Response response : appointmentDTO.getListResponse( ) )
371                 {
372                     ResponseHome.create( response );
373                     AppointmentResponseService.insertAppointmentResponse( appointment.getIdAppointment( ), response.getIdResponse( ) );
374                 }
375             }
376             processeActionWorkflow( appointment, request, locale, appointmentDTO.getIdForm( ), isReport );
377             TransactionManager.commitTransaction( AppointmentPlugin.getPlugin( ) );
378             appointmentDTO.setIdAppointment( appointment.getIdAppointment( ) );
379             appointmentDTO.setIsSaved( true );
380             notifyListner( appointment, listSlotUpdated, isReport, locale );
381             if ( request != null )
382             {
383                 for ( AppointmentSlot apptSlot : appointmentDTO.getListAppointmentSlot( ) )
384                 {
385                     AppointmentUtilities.cancelTaskTimer( request, apptSlot.getIdSlot( ) );
386                 }
387             }
388             appointmentDTO.setReference(appointment.getReference( ));
389             appointmentDTO.setUser( user );
390             return appointment.getIdAppointment( );
391 
392         }
393         catch( Exception e )
394         {
395             TransactionManager.rollBack( AppointmentPlugin.getPlugin( ) );
396             AppLogService.error( "Error Save appointment " + e.getMessage( ), e );
397             throw new SlotFullException( e.getMessage( ), e );
398         }
399         finally
400         {
401             for ( Lock lock : listLock )
402             {
403                 lock.unlock( );
404             }
405         }
406     }
407 
408     /**
409      * notify Appointment/Slot Listner
410      * 
411      * @param appointment
412      *            the appointment
413      * @param listSlotUpdated
414      *            the list slot updated to notify
415      * @param isReport,
416      *            true if it is a postponement of appointment
417      * @param locale
418      *            the locale
419      */
420     private static void notifyListner( Appointment appointment, Set<Integer> listSlotUpdated, boolean isReport, Locale locale )
421     {
422 
423         for ( int idSlot : listSlotUpdated )
424         {
425             SlotListenerManager.notifyListenersSlotChange( idSlot );
426         }
427         if ( isReport )
428         {
429             AppointmentListenerManager.notifyListenersAppointmentDateChanged( appointment.getIdAppointment( ),
430                     appointment.getListAppointmentSlot( ).stream( ).map( AppointmentSlot::getIdSlot ).collect( Collectors.toList( ) ), locale );
431             if ( appointment.getIdActionReported( ) != 0 )
432             {
433                 AppointmentListenerManager.notifyAppointmentWFActionTriggered( appointment.getIdAppointment( ), appointment.getIdActionReported( ) );
434             }
435 
436         }
437         else
438         {
439             AppointmentListenerManager.notifyListenersAppointmentCreated( appointment.getIdAppointment( ) );
440         }
441     }
442 
443     /**
444      * Process Action workflow
445      * 
446      * @param appointment
447      *            the appointment
448      * @param request
449      *            the request
450      * @param locale
451      *            the locale
452      * @param nIdFom
453      *            the id appointment form
454      * @param isReport
455      *            true if it is a postponement of appointment
456      */
457     private static void processeActionWorkflow( Appointment appointment, HttpServletRequest request, Locale locale, int nIdFom, boolean isReport )
458     {
459 
460         Form form = FormService.findFormLightByPrimaryKey( nIdFom );
461         if ( form.getIdWorkflow( ) > 0 )
462         {
463             WorkflowService.getInstance( ).getState( appointment.getIdAppointment( ), Appointment.APPOINTMENT_RESOURCE_TYPE, form.getIdWorkflow( ),
464                     form.getIdForm( ) );
465 
466             if ( isReport && appointment.getIdActionReported( ) != 0 )
467             {
468                 AdminUser adminUser = ( request != null ) ? AdminUserService.getAdminUser( request ) : null;
469                 WorkflowService.getInstance( ).doProcessAction( appointment.getIdAppointment( ), Appointment.APPOINTMENT_RESOURCE_TYPE,
470                         appointment.getIdActionReported( ), form.getIdForm( ), request, locale, adminUser == null, adminUser );
471 
472             }
473         }
474     }
475 
476     /**
477      * Set the new number of remaining places (and potential) when an appointment is deleted or cancelled This new value must take in account the capacity of
478      * the slot, in case of the slot was already over booked
479      * 
480      * @param nbPlaces
481      *            the nb places taken of the appointment that we want to delete (or cancel, or move)
482      * @param slot
483      *            the related slot
484      */
485     static void updateRemaningPlacesWithAppointmentMovedDeletedOrCanceled( int nbPlaces, int nIdSlot )
486     {
487         // The capacity of the slot (that can be less than the number of places
488         // taken on the slot --> overbook)
489 
490         Lock lock = getLockOnSlot( nIdSlot );
491         lock.lock( );
492         try
493         {
494             Slot slot = SlotService.findSlotById( nIdSlot );
495             if ( slot != null )
496             {
497                 int nMaxCapacity = slot.getMaxCapacity( );
498                 // The old remaining places of the slot (before we delete or cancel or move the
499                 // appointment
500                 int nOldRemainingPlaces = slot.getNbRemainingPlaces( );
501                 int nOldPotentialRemaningPlaces = slot.getNbPotentialRemainingPlaces( );
502                 int nOldPlacesTaken = slot.getNbPlacesTaken( );
503                 int nNewPlacesTaken = nOldPlacesTaken - nbPlaces;
504                 // The new value of the remaining places of the slot is the minimal
505                 // value between :
506                 // - the minimal value between the potentially new max capacity and the old remaining places plus the number of places released by the
507                 // appointment
508                 // - and the capacity of the slot minus the new places taken on the slot
509                 int nNewRemainingPlaces = Math.min( Math.min( nMaxCapacity, nOldRemainingPlaces + nbPlaces ), ( nMaxCapacity - nNewPlacesTaken ) );
510 
511                 int nNewPotentialRemainingPlaces = Math.min( Math.min( nMaxCapacity, nOldPotentialRemaningPlaces + nbPlaces ),
512                         ( nMaxCapacity - nNewPlacesTaken ) );
513 
514                 slot.setNbRemainingPlaces( nNewRemainingPlaces );
515                 slot.setNbPotentialRemainingPlaces( nNewPotentialRemainingPlaces );
516                 slot.setNbPlacestaken( nNewPlacesTaken );
517                 SlotHome.update( slot );
518             }
519         }
520         finally
521         {
522 
523             lock.unlock( );
524         }
525 
526     }
527 
528     /**
529      * Set the new number of remaining places (and potential) when an appointment is reactivated(not reserved to reserved) This new value must take in account
530      * the capacity of the slot, in case of the slot was already over booked
531      * 
532      * @param nbPlaces
533      *            the nb places taken of the appointment on the slot
534      * @param nIdSlot
535      *            the id slot to update
536      */
537     static void updateRemaningPlacesWithAppointmentReactivated( int nbPlaces, int nIdSlot )
538     {
539         // The capacity of the slot (that can be less than the number of places
540         // taken on the slot --> overbook)
541         Lock lock = getLockOnSlot( nIdSlot );
542         lock.lock( );
543         try
544         {
545             Slot slot = SlotService.findSlotById( nIdSlot );
546             if ( slot != null )
547             {
548                 slot.setNbRemainingPlaces( slot.getNbRemainingPlaces( ) - nbPlaces );
549                 slot.setNbPotentialRemainingPlaces( slot.getNbPotentialRemainingPlaces( ) - nbPlaces );
550                 slot.setNbPlacestaken( slot.getNbPlacesTaken( ) + nbPlaces );
551                 SlotHome.update( slot );
552             }
553         }
554         finally
555         {
556 
557             lock.unlock( );
558         }
559 
560     }
561 
562     /**
563      * Update a slot in database and possibly all the slots after (if the ending hour has changed, all the next slots are impacted in case of the user decide to
564      * shift the next slots)
565      * 
566      * @param slot
567      *            the slot to update
568      * @param bEndingTimeHasChanged
569      *            true if the ending time has changed
570      * @param previousEndingTime
571      *            the previous ending time
572      * @param bShifSlot
573      *            true if the user has decided to shift the next slots
574      */
575     public static void updateSlot( Slot slot, boolean bEndingTimeHasChanged, LocalTime previousEndingTime, boolean bShifSlot )
576     {
577 
578         slot.setIsSpecific( SlotService.isSpecificSlot( slot ) );
579         if ( bEndingTimeHasChanged )
580         {
581             // If we don't want to shift the next slots
582             if ( !bShifSlot )
583             {
584                 updateSlotWithoutShift( slot );
585             }
586             else
587             {
588                 // We want to shift the next slots at the end of the current
589                 // slot
590                 updateSlotWithShift( slot, previousEndingTime );
591             }
592             SlotListenerManager.notifySlotEndingTimeHasChanged( slot.getIdSlot( ), slot.getIdForm( ), slot.getEndingDateTime( ) );
593         }
594         else
595         {
596             // The ending time of the slot has not changed
597             // If it's an update of an existing slot
598             if ( slot.getIdSlot( ) != 0 )
599             {
600                 updateRemainingPlaces( slot );
601             }
602             saveSlot( slot );
603         }
604 
605     }
606 
607     /**
608      * Update the current slot and don't shift the next slots
609      * 
610      * @param slot
611      *            the current slot
612      */
613     private static void updateSlotWithoutShift( Slot slot )
614     {
615         List<Slot> listSlotToCreate = new ArrayList<>( );
616         // Need to get all the slots until the new end of this slot
617         List<Slot> listSlotToDelete = SlotService.findSlotsByIdFormAndDateRange( slot.getIdForm( ), slot.getStartingDateTime( ).plusMinutes( 1 ),
618                 slot.getEndingDateTime( ) );
619         SlotService.deleteListSlots( listSlotToDelete );
620         // Get the list of slot after the modified slot
621         HashMap<LocalDateTime, Slot> mapNextSlot = SlotService.buildMapSlotsByIdFormAndDateRangeWithDateForKey( slot.getIdForm( ), slot.getEndingDateTime( ),
622                 slot.getDate( ).atTime( LocalTime.MAX ) );
623         List<LocalDateTime> listStartingDateTimeNextSlot = new ArrayList<>( mapNextSlot.keySet( ) );
624         // Get the next date time slot
625         LocalDateTime nextStartingDateTime = null;
626         if ( CollectionUtils.isNotEmpty( listStartingDateTimeNextSlot ) )
627         {
628             nextStartingDateTime = Utilities.getClosestDateTimeInFuture( listStartingDateTimeNextSlot, slot.getEndingDateTime( ) );
629         }
630         else
631         {
632             LocalDate dateOfSlot = slot.getDate( );
633             ReservationRule reservationRule = ReservationRuleService.findReservationRuleByIdFormAndClosestToDateOfApply( slot.getIdForm( ), dateOfSlot );
634             WorkingDay workingDay = WorkingDayService.getWorkingDayOfDayOfWeek( reservationRule.getListWorkingDay( ), dateOfSlot.getDayOfWeek( ) );
635             // No slot after this one.
636             // Need to compute between the end of this slot and the next
637             // time slot
638             if ( workingDay != null )
639             {
640                 List<TimeSlot> nextTimeSlots = TimeSlotService.getNextTimeSlotsInAListOfTimeSlotAfterALocalTime( workingDay.getListTimeSlot( ),
641                         slot.getEndingTime( ) );
642                 if ( CollectionUtils.isNotEmpty( nextTimeSlots ) )
643                 {
644                     Optional<TimeSlot> optTimeSlot = nextTimeSlots.stream( ).min( ( t1, t2 ) -> t1.getStartingTime( ).compareTo( t2.getStartingTime( ) ) );
645                     if ( optTimeSlot.isPresent( ) )
646                     {
647                         nextStartingDateTime = optTimeSlot.get( ).getStartingTime( ).atDate( dateOfSlot );
648                     }
649                 }
650             }
651             else
652             {
653                 // This is not a working day
654                 // Generated the new slots at the end of the modified
655                 // slot
656                 listSlotToCreate.addAll( generateListSlotToCreateAfterATime( slot.getEndingDateTime( ), slot.getIdForm( ) ) );
657             }
658         }
659         // Need to create a slot between these two dateTime
660         if ( nextStartingDateTime != null && !slot.getEndingDateTime( ).isEqual( nextStartingDateTime ) )
661         {
662             Slot slotToCreate = SlotService.buildSlot( slot.getIdForm( ), new Period( slot.getEndingDateTime( ), nextStartingDateTime ), slot.getMaxCapacity( ),
663                     slot.getMaxCapacity( ), slot.getMaxCapacity( ), 0, Boolean.FALSE, Boolean.TRUE );
664             listSlotToCreate.add( slotToCreate );
665         }
666         // If it's an update of an existing slot
667         if ( slot.getIdSlot( ) != 0 )
668         {
669             updateRemainingPlaces( slot );
670         }
671         saveSlot( slot );
672         createListSlot( listSlotToCreate );
673     }
674 
675     /**
676      * update the current slot and shift the next slots at the end of the current slot
677      * 
678      * @param slot
679      *            the current slot
680      * @param previousEndingTime
681      *            the previous ending time of the current slot
682      */
683     private static void updateSlotWithShift( Slot slot, LocalTime previousEndingTime )
684     {
685         // We want to shift all the next slots
686         LocalDate dateOfSlot = slot.getDate( );
687         List<WeekDefinition> listWeekDefinition = WeekDefinitionService.findListWeekDefinition( slot.getIdForm( ) );
688         Map<WeekDefinition, ReservationRule> mapReservationRule = ReservationRuleService.findAllReservationRule( slot.getIdForm( ), listWeekDefinition );
689 
690         // Build or get all the slots of the day
691         List<Slot> listAllSlotsOfThisDayToBuildOrInDb = SlotService.buildListSlot( slot.getIdForm( ), mapReservationRule, dateOfSlot, dateOfSlot );
692         // Remove the current slot and all the slot before it
693         listAllSlotsOfThisDayToBuildOrInDb = listAllSlotsOfThisDayToBuildOrInDb.stream( )
694                 .filter( slotToKeep -> slotToKeep.getStartingDateTime( ).isAfter( slot.getStartingDateTime( ) ) ).collect( Collectors.toList( ) );
695         // Need to delete all the slots until the new end of this slot
696         List<Slot> listSlotToDelete = listAllSlotsOfThisDayToBuildOrInDb.stream( )
697                 .filter( slotToDelete -> slotToDelete.getStartingDateTime( ).isAfter( slot.getStartingDateTime( ) )
698                         && !slotToDelete.getEndingDateTime( ).isAfter( slot.getEndingDateTime( ) ) && slotToDelete.getIdSlot( ) != 0 )
699                 .collect( Collectors.toList( ) );
700         SlotService.deleteListSlots( listSlotToDelete );
701         listAllSlotsOfThisDayToBuildOrInDb.removeAll( listSlotToDelete );
702         // Need to find all the existing slots
703         List<Slot> listExistingSlots = listAllSlotsOfThisDayToBuildOrInDb.stream( ).filter( existingSlot -> existingSlot.getIdSlot( ) != 0 )
704                 .collect( Collectors.toList( ) );
705         // Remove them from the list of slot to build
706         listAllSlotsOfThisDayToBuildOrInDb.removeAll( listExistingSlots );
707         // Save this list
708         createListSlot( listAllSlotsOfThisDayToBuildOrInDb );
709         List<Slot> listSlotToShift = new ArrayList<>( );
710         listSlotToShift.addAll( listExistingSlots );
711         listSlotToShift.addAll( listAllSlotsOfThisDayToBuildOrInDb );
712         // Need to order the list of slot to shift according to the shift
713         // if the new ending time is before the previous ending time,
714         // the list has to be ordered in chronological order ascending
715         // and the first slot to shift is the closest to the current
716         // slot
717         // (because we have an integrity constraint for the slot, it
718         // can't have the same starting or ending time as another slot
719         listSlotToShift = listSlotToShift.stream( ).sorted( ( slot1, slot2 ) -> slot1.getStartingDateTime( ).compareTo( slot2.getStartingDateTime( ) ) )
720                 .collect( Collectors.toList( ) );
721         boolean bNewEndingTimeIsAfterThePreviousTime = false;
722         // Need to know the ending time of the day
723         LocalDateTime endingDateTimeOfTheDay = null;
724         ReservationRule reservationRule = ReservationRuleService.findReservationRuleByIdFormAndClosestToDateOfApply( slot.getIdForm( ), dateOfSlot );
725         WorkingDay workingDay = WorkingDayService.getWorkingDayOfDayOfWeek( reservationRule.getListWorkingDay( ), dateOfSlot.getDayOfWeek( ) );
726         LocalTime endingTimeOfTheDay;
727         if ( workingDay != null )
728         {
729             endingTimeOfTheDay = WorkingDayService.getMaxEndingTimeOfAWorkingDay( workingDay );
730         }
731         else
732         {
733             endingTimeOfTheDay = WorkingDayService.getMaxEndingTimeOfAListOfWorkingDay( reservationRule.getListWorkingDay( ) );
734         }
735         endingDateTimeOfTheDay = endingTimeOfTheDay.atDate( dateOfSlot );
736         long timeToAdd = 0;
737         long timeToSubstract = 0;
738         if ( previousEndingTime.isBefore( slot.getEndingTime( ) ) )
739         {
740             bNewEndingTimeIsAfterThePreviousTime = true;
741             // Need to find the next available slot, to know how to
742             // add to the starting time of the next slot to match
743             // with the new end of the current slot
744             if ( CollectionUtils.isNotEmpty( listSlotToShift ) )
745             {
746                 Slot nextSlot = listSlotToShift.stream( ).min( ( s1, s2 ) -> s1.getStartingDateTime( ).compareTo( s2.getStartingDateTime( ) ) ).orElse( slot );
747                 if ( slot.getEndingDateTime( ).isAfter( nextSlot.getStartingDateTime( ) ) )
748                 {
749                     timeToAdd = nextSlot.getStartingDateTime( ).until( slot.getEndingDateTime( ), ChronoUnit.MINUTES );
750                 }
751                 else
752                 {
753                     timeToAdd = slot.getEndingDateTime( ).until( nextSlot.getStartingDateTime( ), ChronoUnit.MINUTES );
754                 }
755                 Collections.reverse( listSlotToShift );
756             }
757             else
758             {
759                 timeToAdd = previousEndingTime.until( slot.getEndingTime( ), ChronoUnit.MINUTES );
760             }
761         }
762         else
763         {
764             timeToSubstract = slot.getEndingTime( ).until( previousEndingTime, ChronoUnit.MINUTES );
765         }
766         // If it's an update of an existing slot
767         if ( slot.getIdSlot( ) != 0 )
768         {
769             updateRemainingPlaces( slot );
770         }
771         saveSlot( slot );
772         // Need to set the new starting and ending time of all the slots
773         // to shift and update them
774         for ( Slot slotToShift : listSlotToShift )
775         {
776             // If the new ending time is after the previous time
777             if ( bNewEndingTimeIsAfterThePreviousTime )
778             {
779                 // If the starting time + the time to add is before the
780                 // ending time of the day
781                 if ( slotToShift.getStartingDateTime( ).plus( timeToAdd, ChronoUnit.MINUTES ).isBefore( endingDateTimeOfTheDay ) )
782                 {
783                     slotToShift.setStartingDateTime( slotToShift.getStartingDateTime( ).plus( timeToAdd, ChronoUnit.MINUTES ) );
784                     // if the ending time is after the ending time of
785                     // the day, we set the new ending time to the ending
786                     // time of the day
787                     if ( slotToShift.getEndingDateTime( ).plus( timeToAdd, ChronoUnit.MINUTES ).isAfter( endingDateTimeOfTheDay ) )
788                     {
789                         slotToShift.setEndingDateTime( endingDateTimeOfTheDay );
790                     }
791                     else
792                     {
793                         slotToShift.setEndingDateTime( slotToShift.getEndingDateTime( ).plus( timeToAdd, ChronoUnit.MINUTES ) );
794                     }
795                     slotToShift.setIsSpecific( SlotService.isSpecificSlot( slotToShift ) );
796                     saveSlot( slotToShift );
797                 }
798                 else
799                 {
800                     // Delete this slot (the slot can not be after the
801                     // ending time of the day)
802                     SlotService.deleteSlot( slotToShift );
803                 }
804             }
805             else
806             {
807                 // The new ending time is before the previous ending
808                 // time
809                 slotToShift.setStartingDateTime( slotToShift.getStartingDateTime( ).minus( timeToSubstract, ChronoUnit.MINUTES ) );
810                 slotToShift.setEndingDateTime( slotToShift.getEndingDateTime( ).minus( timeToSubstract, ChronoUnit.MINUTES ) );
811                 slotToShift.setIsSpecific( SlotService.isSpecificSlot( slotToShift ) );
812                 saveSlot( slotToShift );
813             }
814         }
815         if ( !bNewEndingTimeIsAfterThePreviousTime )
816         {
817             // If the slots have been shift earlier,
818             // there is no slot(s) between the last slot created
819             // and the ending time of the day, need to create it(them)
820             List<Slot> listSlotsToAdd = generateListSlotToCreateAfterATime( endingDateTimeOfTheDay.minusMinutes( timeToSubstract ), slot.getIdForm( ) );
821             createListSlot( listSlotsToAdd );
822         }
823 
824     }
825 
826     /**
827      * Generate the list of slot to create after a slot (taking into account the week definition and the rules to apply)
828      * 
829      * @param slot
830      *            the slot
831      * @return the list of next slots
832      */
833     private static List<Slot> generateListSlotToCreateAfterATime( LocalDateTime dateTimeToStartCreation, int nIdForm )
834     {
835         List<Slot> listSlotToCreate = new ArrayList<>( );
836         LocalDate dateOfCreation = dateTimeToStartCreation.toLocalDate( );
837         ReservationRule reservationRule = ReservationRuleService.findReservationRuleByIdFormAndClosestToDateOfApply( nIdForm, dateOfCreation );
838         int nMaxCapacity = reservationRule.getMaxCapacityPerSlot( );
839 
840         WorkingDay workingDay = WorkingDayService.getWorkingDayOfDayOfWeek( reservationRule.getListWorkingDay( ), dateOfCreation.getDayOfWeek( ) );
841         LocalTime endingTimeOfTheDay = null;
842         List<TimeSlot> listTimeSlot = new ArrayList<>( );
843         int nDurationSlot = reservationRule.getDurationAppointments( );
844         if ( workingDay != null )
845         {
846             endingTimeOfTheDay = WorkingDayService.getMaxEndingTimeOfAWorkingDay( workingDay );
847             listTimeSlot = TimeSlotService.findListTimeSlotByWorkingDay( workingDay.getIdWorkingDay( ) );
848         }
849         else
850         {
851             endingTimeOfTheDay = WorkingDayService.getMaxEndingTimeOfAListOfWorkingDay( reservationRule.getListWorkingDay( ) );
852         }
853         LocalDateTime endingDateTimeOfTheDay = endingTimeOfTheDay.atDate( dateOfCreation );
854         LocalDateTime startingDateTime = dateTimeToStartCreation;
855         LocalDateTime endingDateTime = startingDateTime.plusMinutes( nDurationSlot );
856         while ( !endingDateTime.isAfter( endingDateTimeOfTheDay ) )
857         {
858             Slot slotToCreate = SlotService.buildSlot( nIdForm, new Period( startingDateTime, endingDateTime ), nMaxCapacity, nMaxCapacity, nMaxCapacity, 0,
859                     Boolean.FALSE, Boolean.TRUE );
860             slotToCreate.setIsSpecific( SlotService.isSpecificSlot( slotToCreate, workingDay, listTimeSlot, nMaxCapacity ) );
861             startingDateTime = endingDateTime;
862             endingDateTime = startingDateTime.plusMinutes( nDurationSlot );
863             listSlotToCreate.add( slotToCreate );
864         }
865         if ( startingDateTime.isBefore( endingDateTimeOfTheDay ) && endingDateTime.isAfter( endingDateTimeOfTheDay ) )
866         {
867             Slot slotToCreate = SlotService.buildSlot( nIdForm, new Period( startingDateTime, endingDateTimeOfTheDay ), nMaxCapacity, nMaxCapacity,
868                     nMaxCapacity, 0, Boolean.FALSE, Boolean.TRUE );
869             slotToCreate.setIsSpecific( SlotService.isSpecificSlot( slotToCreate, workingDay, listTimeSlot, nMaxCapacity ) );
870             listSlotToCreate.add( slotToCreate );
871         }
872         return listSlotToCreate;
873     }
874 
875     /**
876      * Build a Slot object from the resultset
877      * 
878      * @param daoUtil
879      *            the prepare statement util object
880      * 
881      */
882     /**
883      * Update the capacity of the slot
884      * 
885      * @param slot
886      *            the slot to update
887      */
888     public static void updateRemainingPlaces( Slot slot )
889     {
890         Slot oldSlot = SlotHome.findByPrimaryKey( slot.getIdSlot( ) );
891         int nNewNbMaxCapacity = slot.getMaxCapacity( );
892         int nOldBnMaxCapacity = oldSlot.getMaxCapacity( );
893         // If the max capacity has been modified
894         if ( nNewNbMaxCapacity != nOldBnMaxCapacity )
895         {
896             // Need to update the remaining places
897 
898             // Need to add the diff between the old value and the new value
899             // to the remaining places (if the new is higher)
900             if ( nNewNbMaxCapacity > nOldBnMaxCapacity )
901             {
902                 int nValueToAdd = nNewNbMaxCapacity - nOldBnMaxCapacity;
903                 slot.setNbPotentialRemainingPlaces( oldSlot.getNbPotentialRemainingPlaces( ) + nValueToAdd );
904                 slot.setNbRemainingPlaces( oldSlot.getNbRemainingPlaces( ) + nValueToAdd );
905             }
906             else
907             {
908                 // the new value is lower than the previous capacity
909                 // !!!! If there are appointments on this slot and if the
910                 // slot is already full, the slot will be surbooked !!!!
911                 int nValueToSubstract = nOldBnMaxCapacity - nNewNbMaxCapacity;
912                 slot.setNbPotentialRemainingPlaces( oldSlot.getNbPotentialRemainingPlaces( ) - nValueToSubstract );
913                 slot.setNbRemainingPlaces( oldSlot.getNbRemainingPlaces( ) - nValueToSubstract );
914             }
915         }
916         else
917         {
918 
919             slot.setNbPotentialRemainingPlaces( oldSlot.getNbPotentialRemainingPlaces( ) );
920             slot.setNbRemainingPlaces( oldSlot.getNbRemainingPlaces( ) );
921         }
922     }
923 
924     /**
925      * Save a slot in database
926      * 
927      * @param slot
928      *            the slot to save
929      * @return the slot saved
930      */
931     public static Slot="../../../../../../fr/paris/lutece/plugins/appointment/business/slot/Slot.html#Slot">Slot saveSlot( Slot slot )
932     {
933         Slot slotSaved = null;
934         if ( slot.getIdSlot( ) == 0 )
935         {
936             slotSaved = createSlot( slot );
937         }
938         else
939         {
940             slotSaved = updateSlot( slot );
941         }
942         return slotSaved;
943     }
944 
945     /**
946      * Update a slot
947      * 
948      * @param slot
949      *            the slot updated
950      */
951     public static Slot../../../../../../fr/paris/lutece/plugins/appointment/business/slot/Slot.html#Slot">Slot updateSlot( Slot slot )
952     {
953         Slot slotToReturn = SlotHome.update( slot );
954         SlotListenerManager.notifyListenersSlotChange( slot.getIdSlot( ) );
955         return slotToReturn;
956 
957     }
958 
959     /**
960      * Create in database the slots given
961      * 
962      * @param listSlotToCreate
963      *            the list of slots to create in database
964      */
965     private static void createListSlot( List<Slot> listSlotToCreate )
966     {
967         if ( CollectionUtils.isNotEmpty( listSlotToCreate ) )
968         {
969             for ( Slot slotTemp : listSlotToCreate )
970             {
971                 createSlot( slotTemp );
972             }
973         }
974     }
975 
976     /**
977      * Clean slotlist
978      */
979     public static void cleanSlotlist( )
980     {
981 
982         Iterator<Integer> it = _listSlot.keySet( ).iterator( );
983         int idSlot;
984         while ( it.hasNext( ) )
985         {
986 
987             idSlot = it.next( );
988             Slot slot = SlotService.findSlotById( idSlot );
989             if ( slot == null || slot.getStartingDateTime( ).isBefore( LocalDateTime.now( ) ) || slot.getMaxCapacity( ) <= slot.getNbPlacesTaken( ) )
990             {
991                 _listSlot.remove( idSlot );
992             }
993 
994         }
995     }
996 
997     /**
998      * Save and update slots
999      * 
1000      * @param appointmentDTO
1001      *            the appointmentDTO
1002      * @return list id slot updated
1003      * @throws InterruptedException
1004      */
1005     private static Set<Integer> saveSlots( AppointmentDTO appointmentDTO, List<Lock> listLock, HttpServletRequest request ) throws InterruptedException, CloneNotSupportedException
1006     {
1007         Appointment oldAppointment = null;
1008         List<Slot> listOldSlot = new ArrayList<>( );
1009         List<Slot> listSlotToUpdate = new ArrayList<>( );
1010         int nbSumRemainingPlaces = 0;
1011         // if it's an update for modification of the date of the appointment
1012         if ( appointmentDTO.getIdAppointment( ) != 0 )
1013         {
1014             oldAppointment = AppointmentService.findAppointmentById( appointmentDTO.getIdAppointment( ) );
1015             if ( oldAppointment.getIsCancelled( ) )
1016             {
1017                 throw new SlotFullException( "ERROR APPOINTMENT CANCELLED " );
1018             }
1019             // Need to update the old slot
1020             for ( AppointmentSlot appointmentSlot : oldAppointment.getListAppointmentSlot( ) )
1021             {
1022                 Lock lock = getLockOnSlot( appointmentSlot.getIdSlot( ) );
1023                 if ( lock.tryLock( 3, TimeUnit.SECONDS ) )
1024                 {
1025                     listLock.add( lock );
1026                 }
1027                 else
1028                 {
1029                     throw new SlotFullException( "ERROR SLOT LOCKED" );
1030                 }
1031                 Slot slt = SlotService.findSlotById( appointmentSlot.getIdSlot( ) );
1032                 oldAppointment.addSlot( slt.clone( ) );
1033                 slt = updateRemaningPlacesWithAppointmentMoved( appointmentSlot.getNbPlaces( ), slt );
1034                 listOldSlot.add( slt );
1035             }
1036             //We set the appointmentDTO object in the request before proceeding with its update, 
1037             //especially in the context of report an appointment. 
1038             //This ensures that the object will be available in the request parameter that we pass during the execution of workflow tasks.
1039             request.setAttribute(AppointmentUtilities.OLD_APPOINTMENT_DTO, AppointmentUtilities.buildAppointmentDTO(oldAppointment));
1040         }
1041         for ( AppointmentSlot appSlot : appointmentDTO.getListAppointmentSlot( ) )
1042         {
1043             Slot slt = null;
1044             // if it's an update for modification of the date of the appointment we load the slots in the listOldSlot because are updated already
1045             if ( appointmentDTO.getIdAppointment( ) != 0 && listOldSlot.stream( ).anyMatch( slot -> slot.getIdSlot( ) == appSlot.getIdSlot( ) ) )
1046             {
1047                 slt = listOldSlot.stream( ).filter( slot -> slot.getIdSlot( ) == appSlot.getIdSlot( ) ).findFirst( ).orElse( null );
1048                 listOldSlot.removeIf( slot -> slot.getIdSlot( ) == appSlot.getIdSlot( ) );
1049             }
1050             else
1051             {
1052                 Lock lock = getLockOnSlot( appSlot.getIdSlot( ) );
1053                 if ( lock.tryLock( 3, TimeUnit.SECONDS ) )
1054                 {
1055                     listLock.add( lock );
1056                 }
1057                 else
1058                 {
1059                     throw new SlotFullException( "ERROR SLOT LOCKED" );
1060                 }
1061                 slt = SlotService.findSlotById( appSlot.getIdSlot( ) );
1062             }
1063             if ( slt == null || ( ( appSlot.getNbPlaces( ) > slt.getNbRemainingPlaces( ) && !appointmentDTO.getOverbookingAllowed( ) )
1064                     || slt.getEndingDateTime( ).isBefore( LocalDateTime.now( ) ) ) )
1065 
1066             {
1067                 AppLogService.error( "ERROR SLOT FULL, ID SLOT: " + appSlot.getIdSlot( ) );
1068                 throw new SlotFullException( "ERROR SLOT FULL " );
1069             }
1070             nbSumRemainingPlaces = nbSumRemainingPlaces + slt.getNbRemainingPlaces( );
1071             // Update of the remaining places of the slot and appointmentDTO if over booking Allowed
1072             updateRemaningPlacesAndappointmentDTO( appSlot.getNbPlaces( ), slt, appointmentDTO );
1073             listSlotToUpdate.add( slt );
1074         }
1075         // this test is for form with the possibility of taking several appointments on the same slot
1076         if ( appointmentDTO.getNbBookedSeats( ) > nbSumRemainingPlaces && !appointmentDTO.getOverbookingAllowed( ) )
1077         {
1078             AppLogService.error( "ERROR SLOT FULL" );
1079             throw new SlotFullException( "ERROR SLOT FULL" );
1080         }
1081         listSlotToUpdate.addAll( listOldSlot );
1082         return updateListSlots( listSlotToUpdate );
1083 
1084     }
1085 
1086     /**
1087      * Update slots passed in the parmaters
1088      * 
1089      * @param listSlotToUpdate
1090      *            the list of slot to update
1091      * @return ids list slot Updated
1092      */
1093     private static Set<Integer> updateListSlots( List<Slot> listSlotToUpdate )
1094     {
1095         Set<Integer> listSlot = new HashSet<>( );
1096         for ( Slot slot : listSlotToUpdate )
1097         {
1098             SlotHome.update( slot );
1099             listSlot.add( slot.getIdSlot( ) );
1100         }
1101         return listSlot;
1102     }
1103 
1104     /**
1105      * Set the new number of remaining places (and potential) when an appointment is moved This new value must take in account the capacity of the slot, in case
1106      * of the slot was already over booked
1107      * 
1108      * @param nbPlaces
1109      *            the nb places taken of the appointment that we want to move
1110      * @param slot
1111      *            the related slot
1112      * @return the slot updated
1113      */
1114     private static Slott">Slot updateRemaningPlacesWithAppointmentMoved( int nbPlaces, Slot slot )
1115     {
1116         // The capacity of the slot (that can be less than the number of places
1117         // taken on the slot --> overbook)
1118         int nMaxCapacity = slot.getMaxCapacity( );
1119         // The old remaining places of the slot before we move the appointment
1120         int nOldRemainingPlaces = slot.getNbRemainingPlaces( );
1121         int nOldPotentialRemaningPlaces = slot.getNbPotentialRemainingPlaces( );
1122         int nOldPlacesTaken = slot.getNbPlacesTaken( );
1123         int nNewPlacesTaken = nOldPlacesTaken - nbPlaces;
1124         // The new value of the remaining places of the slot is the minimal
1125         // value between :
1126         // - the minimal value between the potentially new max capacity and the old remaining places plus the number of places released by the appointment
1127         // - and the capacity of the slot minus the new places taken on the slot
1128         int nNewRemainingPlaces = Math.min( Math.min( nMaxCapacity, nOldRemainingPlaces + nbPlaces ), ( nMaxCapacity - nNewPlacesTaken ) );
1129         int nNewPotentialRemainingPlaces = Math.min( Math.min( nNewRemainingPlaces, nOldPotentialRemaningPlaces + nbPlaces ),
1130                 ( nMaxCapacity - nNewPlacesTaken ) );
1131         slot.setNbRemainingPlaces( nNewRemainingPlaces );
1132         slot.setNbPotentialRemainingPlaces( nNewPotentialRemainingPlaces );
1133         slot.setNbPlacestaken( nNewPlacesTaken );
1134         return slot;
1135     }
1136 
1137     /**
1138      * update remaning places and appointmentDTO
1139      * 
1140      * @param effectiveBookedSeats
1141      *            the effective booked seats
1142      * @param slt
1143      *            the slot
1144      * @param appointmentDTO
1145      *            the appointment
1146      */
1147     private static void updateRemaningPlacesAndappointmentDTO( int effectiveBookedSeats, Slot slt, AppointmentDTO appointmentDTO )
1148     {
1149 
1150         // Update of the remaining places of the slot
1151         int newNbRemainingPlaces = slt.getNbRemainingPlaces( ) - effectiveBookedSeats;
1152         int newPotentialRemaningPlaces = slt.getNbPotentialRemainingPlaces( ) + appointmentDTO.getNbMaxPotentialBookedSeats( ) - effectiveBookedSeats;
1153         int newNbPlacesTaken = slt.getNbPlacesTaken( ) + effectiveBookedSeats;
1154         slt.setNbRemainingPlaces( newNbRemainingPlaces );
1155         slt.setNbPlacestaken( newNbPlacesTaken );
1156         slt.setNbPotentialRemainingPlaces( Math.min( newPotentialRemaningPlaces, newNbRemainingPlaces ) );
1157 
1158         if ( slt.getNbPlacesTaken( ) > slt.getMaxCapacity( ) )
1159         {
1160             if ( appointmentDTO.getOverbookingAllowed( ) )
1161             {
1162 
1163                 appointmentDTO.setIsSurbooked( true );
1164             }
1165             else
1166             {
1167                 throw new SlotFullException( "case of overbooking" );
1168             }
1169         }
1170     }
1171 }