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.util.HashMap;
40  import java.util.List;
41  import java.util.Map;
42  import java.util.stream.Collectors;
43  
44  import org.apache.commons.collections.CollectionUtils;
45  
46  import fr.paris.lutece.plugins.appointment.business.planning.TimeSlot;
47  import fr.paris.lutece.plugins.appointment.business.planning.WeekDefinition;
48  import fr.paris.lutece.plugins.appointment.business.planning.WorkingDay;
49  import fr.paris.lutece.plugins.appointment.business.rule.ReservationRule;
50  import fr.paris.lutece.plugins.appointment.business.slot.Period;
51  import fr.paris.lutece.plugins.appointment.business.slot.Slot;
52  import fr.paris.lutece.plugins.appointment.business.slot.SlotHome;
53  import fr.paris.lutece.plugins.appointment.service.listeners.SlotListenerManager;
54  
55  /**
56   * Service class of a slot
57   * 
58   * @author Laurent Payen
59   *
60   */
61  public final class SlotService
62  {
63  
64      /**
65       * Private constructor - this class does not need to be instantiated
66       */
67      private SlotService( )
68      {
69      }
70  
71      /**
72       * Find slots of a form on a given period of time
73       * 
74       * @param nIdForm
75       *            the form Id
76       * @param startingDateTime
77       *            the starting date time to search
78       * @param endingDateTime
79       *            the ending date time to search
80       * @return a list of the slots found
81       */
82      public static List<Slot> findSlotsByIdFormAndDateRange( int nIdForm, LocalDateTime startingDateTime, LocalDateTime endingDateTime )
83      {
84          List<Slot> listSlots = SlotHome.findByIdFormAndDateRange( nIdForm, startingDateTime, endingDateTime );
85          for ( Slot slot : listSlots )
86          {
87              addDateAndTimeToSlot( slot );
88          }
89          return listSlots;
90      }
91  
92      /**
93       * Find slots with appointment of a form on a given period of time
94       * 
95       * @param nIdForm
96       *            the form Id
97       * @param startingDateTime
98       *            the starting date time to search
99       * @param endingDateTime
100      *            the ending date time to search
101      * @return a list of the slots found
102      */
103     public static List<Slot> findSlotWithAppointmentByDateRange( int nIdForm, LocalDateTime startingDateTime, LocalDateTime endingDateTime )
104     {
105         List<Slot> listSlots = SlotHome.findSlotWithAppointmentByDateRange( nIdForm, startingDateTime, endingDateTime );
106         for ( Slot slot : listSlots )
107         {
108             addDateAndTimeToSlot( slot );
109         }
110         return listSlots;
111     }
112 
113     /**
114      * Find specific slots of a form
115      * 
116      * @param nIdForm
117      *            the form Id
118      * @return a list of the slots found
119      */
120     public static List<Slot> findSpecificSlotsByIdForm( int nIdForm )
121     {
122         List<Slot> listSpecificSlots = SlotHome.findIsSpecificByIdForm( nIdForm );
123         for ( Slot slot : listSpecificSlots )
124         {
125             addDateAndTimeToSlot( slot );
126         }
127         return listSpecificSlots;
128     }
129 
130     /**
131      * Build a map (Date, Slot) of all the slots found between the two dates
132      * 
133      * @param nIdForm
134      *            the form id
135      * @param startingDateTime
136      *            the starting date time
137      * @param endingDateTime
138      *            the ending date time
139      * @return the map
140      */
141     public static HashMap<LocalDateTime, Slot> buildMapSlotsByIdFormAndDateRangeWithDateForKey( int nIdForm, LocalDateTime startingDateTime,
142             LocalDateTime endingDateTime )
143     {
144         HashMap<LocalDateTime, Slot> mapSlots = new HashMap<>( );
145         for ( Slot slot : findSlotsByIdFormAndDateRange( nIdForm, startingDateTime, endingDateTime ) )
146         {
147             mapSlots.put( slot.getStartingDateTime( ), slot );
148         }
149         return mapSlots;
150     }
151 
152     /**
153      * Fins all the slots of a form
154      * 
155      * @param nIdForm
156      *            the form id
157      * @return a list of all the slots of a form
158      */
159     public static List<Slot> findListSlot( int nIdForm )
160     {
161         return SlotHome.findByIdForm( nIdForm );
162     }
163 
164     /**
165      * Fins all the slots of a form
166      * 
167      * @param nIdAppointment
168      *            the appointment id
169      * @return a list of all the slots of a form
170      */
171     public static List<Slot> findListSlotByIdAppointment( int nIdAppointment )
172     {
173         return SlotHome.findByIdAppointment( nIdAppointment );
174     }
175 
176     /**
177      * Find the open slots of a form on a given period of time
178      * 
179      * @param nIdForm
180      *            the form Id
181      * @param startingDateTime
182      *            the starting Date time to search
183      * @param endingDateTime
184      *            the ending Date time to search
185      * @return a list of open slots whose matches the criteria
186      */
187     public static List<Slot> findListOpenSlotByIdFormAndDateRange( int nIdForm, LocalDateTime startingDateTime, LocalDateTime endingDateTime )
188     {
189         return SlotHome.findOpenSlotsByIdFormAndDateRange( nIdForm, startingDateTime, endingDateTime );
190     }
191 
192     /**
193      * Find a slot with its primary key
194      * 
195      * @param nIdSlot
196      *            the slot Id
197      * @return the Slot object
198      */
199     public static Slot findSlotById( int nIdSlot )
200     {
201         Slot slot = SlotHome.findByPrimaryKey( nIdSlot );
202         if ( slot != null )
203         {
204             SlotService.addDateAndTimeToSlot( slot );
205         }
206         return slot;
207     }
208 
209     /**
210      * Build all the slot for a period with all the rules (open hours ...) to apply on each day, for each slot
211      * 
212      * @param nIdForm
213      *            the form Id
214      * @param mapWeekDefinition
215      *            the map of the week definition
216      * @param startingDate
217      *            the starting date of the period
218      * @param nNbWeeksToDisplay
219      *            the number of weeks to build
220      * @return a list of all the slots built
221      */
222     public static List<Slot> buildListSlot( int nIdForm, HashMap<LocalDate, WeekDefinition> mapWeekDefinition, LocalDate startingDate, LocalDate endingDate )
223     {
224         Map<WeekDefinition, ReservationRule> mapReservationRule = ReservationRuleService.findAllReservationRule( nIdForm, mapWeekDefinition.values( ) );
225         return buildListSlot( nIdForm, mapReservationRule, startingDate, endingDate, 0 );
226     }
227 
228     /**
229      * Build all the slot for a period with all the rules (open hours ...) to apply on each day, for each slot
230      * 
231      * @param nIdForm
232      *            the form Id
233      * @param mapReservationRule
234      *            the map of the rule week definition
235      * @param startingDate
236      *            the starting date of the period
237      * @param endingDate
238      * @returna list of all the slots built
239      */
240     public static List<Slot> buildListSlot( int nIdForm, Map<WeekDefinition, ReservationRule> mapReservationRule, LocalDate startingDate, LocalDate endingDate )
241     {
242         return buildListSlot( nIdForm, mapReservationRule, startingDate, endingDate, 0 );
243     }
244 
245     /**
246      * Build all the slot for a period with all the rules (open hours ...) to apply on each day, for each slot
247      * 
248      * @param nIdForm
249      *            the form Id
250      * @param mapReservationRule
251      *            the map of the rule week definition
252      * @param startingDate
253      *            the starting date of the period
254      * @param endingDate
255      *            the ending date of the periode
256      * @param nNbPlaces
257      *            the number of place to take
258      * @returna list of all the slots built
259      */
260     public static List<Slot> buildListSlot( int nIdForm, Map<WeekDefinition, ReservationRule> mapReservationRule, LocalDate startingDate, LocalDate endingDate,
261             int nNbPlaces )
262     {
263         return buildListSlot( nIdForm, mapReservationRule, startingDate, endingDate, nNbPlaces, false );
264     }
265 
266     /**
267      * Build all the slot for a period with all the rules (open hours ...) to apply on each day, for each slot
268      * 
269      * @param nIdForm
270      *            the form Id
271      * @param mapReservationRule
272      *            the map of the rule week definition
273      * @param startingDate
274      *            the starting date of the period
275      * @param endingDate
276      *            the ending date of the periode
277      * @param nNbPlaces
278      *            the number of place to take
279      * @param bAllOpenSlot
280      *            build slots with the all open slot
281      * @return list of all the slots built
282      */
283     public static List<Slot> buildListSlot( int nIdForm, Map<WeekDefinition, ReservationRule> mapReservationRule, LocalDate startingDate, LocalDate endingDate,
284             int nNbPlaces, boolean bAllOpenSlot )
285     {
286         if ( nNbPlaces < 1 )
287         {
288             return CalendarBuilder.buildListSlot( nIdForm, mapReservationRule, startingDate, endingDate );
289         }
290         else
291         {
292             return CalendarBuilder.buildListSlot( nIdForm, mapReservationRule, startingDate, endingDate, nNbPlaces, bAllOpenSlot );
293         }
294     }
295 
296     /**
297      * Build a slot with all its values
298      * 
299      * @param nIdForm
300      *            the form Id
301      * @param startingDateTime
302      *            the starting date time
303      * @param endingDateTime
304      *            the ending date time
305      * @param nMaxCapacity
306      *            the maximum capacity for the slot
307      * @param nNbRemainingPlaces
308      *            the number of remaining places of the slot
309      * @param bIsOpen
310      *            true if the slot is open
311      * @return the slot built
312      */
313     public static Slot buildSlot( int nIdForm, Period period, int nMaxCapacity, int nNbRemainingPlaces, int nNbPotentialRemainingPlaces, int nNbPlacesTaken,
314             boolean bIsOpen, boolean bIsSpecific )
315     {
316         Slotugins/appointment/business/slot/Slot.html#Slot">Slot slot = new Slot( );
317         slot.setIdSlot( 0 );
318         slot.setIdForm( nIdForm );
319         slot.setStartingDateTime( period.getStartingDateTime( ) );
320         slot.setEndingDateTime( period.getEndingDateTime( ) );
321         slot.setMaxCapacity( nMaxCapacity );
322         slot.setNbRemainingPlaces( nNbRemainingPlaces );
323         slot.setNbPotentialRemainingPlaces( nNbPotentialRemainingPlaces );
324         slot.setNbPlacestaken( nNbPlacesTaken );
325         slot.setIsOpen( bIsOpen );
326         slot.setIsSpecific( bIsSpecific );
327         addDateAndTimeToSlot( slot );
328         return slot;
329     }
330 
331     /**
332      * To know if it's a specific slot, need to search for a similar time slot
333      * 
334      * @param slot
335      *            the slot
336      * @return true if specific
337      */
338     public static boolean isSpecificSlot( Slot slot )
339     {
340         LocalDate dateOfSlot = slot.getStartingDateTime( ).toLocalDate( );
341         ReservationRule reservationRule = ReservationRuleService.findReservationRuleByIdFormAndClosestToDateOfApply( slot.getIdForm( ), dateOfSlot );
342         WorkingDay workingDay = WorkingDayService.getWorkingDayOfDayOfWeek( reservationRule.getListWorkingDay( ), dateOfSlot.getDayOfWeek( ) );
343         List<TimeSlot> listTimeSlot = null;
344         if ( workingDay != null )
345         {
346             listTimeSlot = TimeSlotService.findListTimeSlotByWorkingDay( workingDay.getIdWorkingDay( ) );
347         }
348         return isSpecificSlot( slot, workingDay, listTimeSlot, reservationRule.getMaxCapacityPerSlot( ) );
349     }
350 
351     /**
352      * To know if it's a specific slot, need to search for a similar time slot
353      * 
354      * @param slot
355      *            the slot
356      * @param workingDay
357      *            the working day
358      * @param listTimeSlot
359      *            the list of time slots
360      * @return true if it's a specific slot
361      */
362     public static boolean isSpecificSlot( Slot slot, WorkingDay workingDay, List<TimeSlot> listTimeSlot, int nMaxCapacity )
363     {
364         boolean bIsSpecific = Boolean.TRUE;
365         List<TimeSlot> listMatchTimeSlot = null;
366         if ( workingDay == null )
367         {
368             if ( !slot.getIsOpen( ) && slot.getMaxCapacity( ) == nMaxCapacity )
369             {
370                 bIsSpecific = Boolean.FALSE;
371             }
372         }
373         else
374         {
375             listMatchTimeSlot = listTimeSlot.stream( )
376                     .filter( t -> ( t.getStartingTime( ).equals( slot.getStartingDateTime( ).toLocalTime( ) ) )
377                             && ( t.getEndingTime( ).equals( slot.getEndingDateTime( ).toLocalTime( ) ) ) && ( t.getIsOpen( ) == slot.getIsOpen( ) )
378                             && ( t.getMaxCapacity( ) == slot.getMaxCapacity( ) ) )
379                     .collect( Collectors.toList( ) );
380             if ( CollectionUtils.isNotEmpty( listMatchTimeSlot ) )
381             {
382                 bIsSpecific = Boolean.FALSE;
383             }
384         }
385         return bIsSpecific;
386     }
387 
388     /**
389      * 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
390      * shift the next slots)
391      * 
392      * @param slot
393      *            the slot to update
394      * @param bEndingTimeHasChanged
395      *            true if the ending time has changed
396      * @param previousEndingTime
397      *            the previous ending time
398      * @param bShifSlot
399      *            true if the user has decided to shift the next slots
400      */
401     public static void updateSlot( Slot slot, boolean bEndingTimeHasChanged, LocalTime previousEndingTime, boolean bShifSlot )
402     {
403 
404         SlotSafeService.updateSlot( slot, bEndingTimeHasChanged, previousEndingTime, bShifSlot );
405 
406     }
407 
408     /**
409      * Update the capacity of the slot
410      * 
411      * @param slot
412      *            the slot to update
413      */
414     public static void updateRemainingPlaces( Slot slot )
415     {
416         SlotSafeService.updateRemainingPlaces( slot );
417     }
418 
419     /**
420      * Save a slot in database
421      * 
422      * @param slot
423      *            the slot to save
424      * @return the slot saved
425      */
426     public static Slot="../../../../../../fr/paris/lutece/plugins/appointment/business/slot/Slot.html#Slot">Slot saveSlot( Slot slot )
427     {
428         return SlotSafeService.saveSlot( slot );
429 
430     }
431 
432     /**
433      * Update a slot
434      * 
435      * @param slot
436      *            the slot updated
437      */
438     public static Slot../../../../../../fr/paris/lutece/plugins/appointment/business/slot/Slot.html#Slot">Slot updateSlot( Slot slot )
439     {
440         return SlotSafeService.updateSlot( slot );
441     }
442 
443     /**
444      * Form the DTO, adding the date and the time to the slot
445      * 
446      * @param slot
447      *            the slot on which to add values
448      */
449     public static void addDateAndTimeToSlot( Slot slot )
450     {
451         if ( slot.getStartingDateTime( ) != null )
452         {
453             slot.setDate( slot.getStartingDateTime( ).toLocalDate( ) );
454             slot.setStartingTime( slot.getStartingDateTime( ).toLocalTime( ) );
455         }
456         if ( slot.getEndingDateTime( ) != null )
457         {
458             slot.setEndingTime( slot.getEndingDateTime( ).toLocalTime( ) );
459         }
460     }
461 
462     /**
463      * Create a slot in db
464      * 
465      * @param slot
466      *            the slot to create
467      * @return the slot created
468      */
469     public static Slot../../../../../../fr/paris/lutece/plugins/appointment/business/slot/Slot.html#Slot">Slot createSlot( Slot slot )
470     {
471         return SlotSafeService.createSlot( slot );
472     }
473 
474     /**
475      * Delete a list of slots
476      * 
477      * @param listSlotToDelete
478      *            the lost of slots to delete
479      */
480     public static void deleteListSlots( List<Slot> listSlotToDelete )
481     {
482 
483         for ( Slot slotToDelete : listSlotToDelete )
484         {
485             SlotSafeService.removeSlotInMemory( slotToDelete.getIdSlot( ) );
486             SlotHome.delete( slotToDelete.getIdSlot( ) );
487             SlotListenerManager.notifyListenersSlotRemoval( slotToDelete );
488         }
489 
490     }
491 
492     /**
493      * Delete a slot
494      * 
495      * @param slot
496      *            the slot to delete
497      */
498     public static void deleteSlot( Slot slot )
499     {
500         int nIdSlot = slot.getIdSlot( );
501         SlotSafeService.removeSlotInMemory( nIdSlot );
502         SlotHome.delete( nIdSlot );
503         SlotListenerManager.notifyListenersSlotRemoval( slot );
504     }
505 
506     /**
507      * Return the slot with the max Date
508      * 
509      * @param nIdForm
510      *            the form id
511      * @return the slot with the max date
512      */
513     public static Slot findSlotWithMaxDate( int nIdForm )
514     {
515         return SlotHome.findSlotWithTheMaxDate( nIdForm );
516     }
517 
518 }