1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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
94
95 private SlotSafeService( )
96 {
97 }
98
99
100
101
102
103
104 public static Map<Integer, Lock> getListSlotInMemory( )
105 {
106
107 return _listSlot;
108 }
109
110
111
112
113
114
115
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
129
130
131
132
133 public static void removeSlotInMemory( int nIdSlot )
134 {
135
136 _listSlot.remove( nIdSlot );
137 }
138
139
140
141
142
143
144
145
146 private static Object getLockOnForm( int nIdform )
147 {
148 _lockFormId.putIfAbsent( nIdform, new Object( ) );
149 return _lockFormId.get( nIdform );
150 }
151
152
153
154
155
156
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
185
186
187
188
189
190
191
192
193
194
195
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
225
226
227
228
229
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
265
266
267
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
297
298
299
300
301
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
331
332
333
334
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
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
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
410
411
412
413
414
415
416
417
418
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
445
446
447
448
449
450
451
452
453
454
455
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
478
479
480
481
482
483
484
485 static void updateRemaningPlacesWithAppointmentMovedDeletedOrCanceled( int nbPlaces, int nIdSlot )
486 {
487
488
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
499
500 int nOldRemainingPlaces = slot.getNbRemainingPlaces( );
501 int nOldPotentialRemaningPlaces = slot.getNbPotentialRemainingPlaces( );
502 int nOldPlacesTaken = slot.getNbPlacesTaken( );
503 int nNewPlacesTaken = nOldPlacesTaken - nbPlaces;
504
505
506
507
508
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
530
531
532
533
534
535
536
537 static void updateRemaningPlacesWithAppointmentReactivated( int nbPlaces, int nIdSlot )
538 {
539
540
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
564
565
566
567
568
569
570
571
572
573
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
582 if ( !bShifSlot )
583 {
584 updateSlotWithoutShift( slot );
585 }
586 else
587 {
588
589
590 updateSlotWithShift( slot, previousEndingTime );
591 }
592 SlotListenerManager.notifySlotEndingTimeHasChanged( slot.getIdSlot( ), slot.getIdForm( ), slot.getEndingDateTime( ) );
593 }
594 else
595 {
596
597
598 if ( slot.getIdSlot( ) != 0 )
599 {
600 updateRemainingPlaces( slot );
601 }
602 saveSlot( slot );
603 }
604
605 }
606
607
608
609
610
611
612
613 private static void updateSlotWithoutShift( Slot slot )
614 {
615 List<Slot> listSlotToCreate = new ArrayList<>( );
616
617 List<Slot> listSlotToDelete = SlotService.findSlotsByIdFormAndDateRange( slot.getIdForm( ), slot.getStartingDateTime( ).plusMinutes( 1 ),
618 slot.getEndingDateTime( ) );
619 SlotService.deleteListSlots( listSlotToDelete );
620
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
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
636
637
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
654
655
656 listSlotToCreate.addAll( generateListSlotToCreateAfterATime( slot.getEndingDateTime( ), slot.getIdForm( ) ) );
657 }
658 }
659
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
667 if ( slot.getIdSlot( ) != 0 )
668 {
669 updateRemainingPlaces( slot );
670 }
671 saveSlot( slot );
672 createListSlot( listSlotToCreate );
673 }
674
675
676
677
678
679
680
681
682
683 private static void updateSlotWithShift( Slot slot, LocalTime previousEndingTime )
684 {
685
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
691 List<Slot> listAllSlotsOfThisDayToBuildOrInDb = SlotService.buildListSlot( slot.getIdForm( ), mapReservationRule, dateOfSlot, dateOfSlot );
692
693 listAllSlotsOfThisDayToBuildOrInDb = listAllSlotsOfThisDayToBuildOrInDb.stream( )
694 .filter( slotToKeep -> slotToKeep.getStartingDateTime( ).isAfter( slot.getStartingDateTime( ) ) ).collect( Collectors.toList( ) );
695
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
703 List<Slot> listExistingSlots = listAllSlotsOfThisDayToBuildOrInDb.stream( ).filter( existingSlot -> existingSlot.getIdSlot( ) != 0 )
704 .collect( Collectors.toList( ) );
705
706 listAllSlotsOfThisDayToBuildOrInDb.removeAll( listExistingSlots );
707
708 createListSlot( listAllSlotsOfThisDayToBuildOrInDb );
709 List<Slot> listSlotToShift = new ArrayList<>( );
710 listSlotToShift.addAll( listExistingSlots );
711 listSlotToShift.addAll( listAllSlotsOfThisDayToBuildOrInDb );
712
713
714
715
716
717
718
719 listSlotToShift = listSlotToShift.stream( ).sorted( ( slot1, slot2 ) -> slot1.getStartingDateTime( ).compareTo( slot2.getStartingDateTime( ) ) )
720 .collect( Collectors.toList( ) );
721 boolean bNewEndingTimeIsAfterThePreviousTime = false;
722
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
742
743
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
767 if ( slot.getIdSlot( ) != 0 )
768 {
769 updateRemainingPlaces( slot );
770 }
771 saveSlot( slot );
772
773
774 for ( Slot slotToShift : listSlotToShift )
775 {
776
777 if ( bNewEndingTimeIsAfterThePreviousTime )
778 {
779
780
781 if ( slotToShift.getStartingDateTime( ).plus( timeToAdd, ChronoUnit.MINUTES ).isBefore( endingDateTimeOfTheDay ) )
782 {
783 slotToShift.setStartingDateTime( slotToShift.getStartingDateTime( ).plus( timeToAdd, ChronoUnit.MINUTES ) );
784
785
786
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
801
802 SlotService.deleteSlot( slotToShift );
803 }
804 }
805 else
806 {
807
808
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
818
819
820 List<Slot> listSlotsToAdd = generateListSlotToCreateAfterATime( endingDateTimeOfTheDay.minusMinutes( timeToSubstract ), slot.getIdForm( ) );
821 createListSlot( listSlotsToAdd );
822 }
823
824 }
825
826
827
828
829
830
831
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
877
878
879
880
881
882
883
884
885
886
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
894 if ( nNewNbMaxCapacity != nOldBnMaxCapacity )
895 {
896
897
898
899
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
909
910
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
926
927
928
929
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
947
948
949
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
961
962
963
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
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
999
1000
1001
1002
1003
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
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
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
1037
1038
1039 request.setAttribute(AppointmentUtilities.OLD_APPOINTMENT_DTO, AppointmentUtilities.buildAppointmentDTO(oldAppointment));
1040 }
1041 for ( AppointmentSlot appSlot : appointmentDTO.getListAppointmentSlot( ) )
1042 {
1043 Slot slt = null;
1044
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
1072 updateRemaningPlacesAndappointmentDTO( appSlot.getNbPlaces( ), slt, appointmentDTO );
1073 listSlotToUpdate.add( slt );
1074 }
1075
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
1088
1089
1090
1091
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
1106
1107
1108
1109
1110
1111
1112
1113
1114 private static Slott">Slot updateRemaningPlacesWithAppointmentMoved( int nbPlaces, Slot slot )
1115 {
1116
1117
1118 int nMaxCapacity = slot.getMaxCapacity( );
1119
1120 int nOldRemainingPlaces = slot.getNbRemainingPlaces( );
1121 int nOldPotentialRemaningPlaces = slot.getNbPotentialRemainingPlaces( );
1122 int nOldPlacesTaken = slot.getNbPlacesTaken( );
1123 int nNewPlacesTaken = nOldPlacesTaken - nbPlaces;
1124
1125
1126
1127
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
1139
1140
1141
1142
1143
1144
1145
1146
1147 private static void updateRemaningPlacesAndappointmentDTO( int effectiveBookedSeats, Slot slt, AppointmentDTO appointmentDTO )
1148 {
1149
1150
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 }