PluginProbe ʕ •ᴥ•ʔ
Booking for Appointments and Events Calendar – Amelia / 1.2.32
Booking for Appointments and Events Calendar – Amelia v1.2.32
2.4.9 2.4.8 2.4.7 2.4.6 2.4.5 2.4.4 2.4.3 2.4.2 2.4.1 2.4 trunk 1.2.1 1.2.10 1.2.11 1.2.12 1.2.13 1.2.14 1.2.15 1.2.16 1.2.17 1.2.18 1.2.19 1.2.2 1.2.20 1.2.21 1.2.22 1.2.23 1.2.24 1.2.25 1.2.26 1.2.27 1.2.28 1.2.29 1.2.3 1.2.30 1.2.31 1.2.32 1.2.33 1.2.34 1.2.35 1.2.36 1.2.37 1.2.38 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 2.0 2.0.1 2.0.2 2.1 2.1.1 2.1.2 2.1.3 2.2 2.2.1 2.3
ameliabooking / src / Application / Commands / Booking / Appointment / GetTimeSlotsCommandHandler.php
ameliabooking / src / Application / Commands / Booking / Appointment Last commit date
AddAppointmentCommand.php 1 year ago AddAppointmentCommandHandler.php 1 year ago AddBookingCommand.php 1 year ago AddBookingCommandHandler.php 1 year ago ApproveBookingRemotelyCommand.php 1 year ago ApproveBookingRemotelyCommandHandler.php 1 year ago CancelBookingCommand.php 1 year ago CancelBookingCommandHandler.php 1 year ago CancelBookingRemotelyCommand.php 1 year ago CancelBookingRemotelyCommandHandler.php 2 years ago DeleteAppointmentCommand.php 1 year ago DeleteAppointmentCommandHandler.php 1 year ago DeleteBookingCommand.php 1 year ago DeleteBookingCommandHandler.php 1 year ago DeleteBookingRemotelyCommand.php 1 year ago DeleteBookingRemotelyCommandHandler.php 1 year ago GetAppointmentCommand.php 7 years ago GetAppointmentCommandHandler.php 1 year ago GetAppointmentsCommand.php 1 year ago GetAppointmentsCommandHandler.php 1 year ago GetIcsCommand.php 1 year ago GetIcsCommandHandler.php 4 years ago GetPackageAppointmentsCommand.php 1 year ago GetPackageAppointmentsCommandHandler.php 1 year ago GetTimeSlotsCommand.php 1 year ago GetTimeSlotsCommandHandler.php 1 year ago ReassignBookingCommand.php 1 year ago ReassignBookingCommandHandler.php 1 year ago RejectBookingRemotelyCommand.php 1 year ago RejectBookingRemotelyCommandHandler.php 1 year ago SuccessfulBookingCommand.php 1 year ago SuccessfulBookingCommandHandler.php 1 year ago UpdateAppointmentCommand.php 1 year ago UpdateAppointmentCommandHandler.php 1 year ago UpdateAppointmentStatusCommand.php 1 year ago UpdateAppointmentStatusCommandHandler.php 1 year ago UpdateAppointmentTimeCommand.php 1 year ago UpdateAppointmentTimeCommandHandler.php 1 year ago UpdateBookingStatusCommand.php 1 year ago UpdateBookingStatusCommandHandler.php 1 year ago
GetTimeSlotsCommandHandler.php
427 lines
1 <?php
2
3 namespace AmeliaBooking\Application\Commands\Booking\Appointment;
4
5 use AmeliaBooking\Application\Commands\CommandHandler;
6 use AmeliaBooking\Application\Commands\CommandResult;
7 use AmeliaBooking\Application\Services\TimeSlot\TimeSlotService as ApplicationTimeSlotService;
8 use AmeliaBooking\Domain\Entity\User\Provider;
9 use AmeliaBooking\Domain\Services\Entity\EntityService;
10 use AmeliaBooking\Domain\Common\Exceptions\InvalidArgumentException;
11 use AmeliaBooking\Domain\Entity\Bookable\Service\Service;
12 use AmeliaBooking\Domain\Entity\Booking\SlotsEntities;
13 use AmeliaBooking\Domain\Services\DateTime\DateTimeService;
14 use AmeliaBooking\Domain\Services\Settings\SettingsService;
15 use AmeliaBooking\Domain\ValueObjects\PositiveDuration;
16 use AmeliaBooking\Infrastructure\Common\Exceptions\QueryExecutionException;
17 use AmeliaBooking\Infrastructure\Repository\Booking\Appointment\AppointmentRepository;
18 use AmeliaBooking\Infrastructure\Services\Apple\AbstractAppleCalendarService;
19 use AmeliaBooking\Infrastructure\Services\Google\AbstractGoogleCalendarService;
20 use AmeliaBooking\Infrastructure\Services\Outlook\AbstractOutlookCalendarService;
21 use DateTimeZone;
22 use Exception;
23 use Interop\Container\Exception\ContainerException;
24 use Slim\Exception\ContainerValueNotFoundException;
25
26 /**
27 * Class GetTimeSlotsCommandHandler
28 *
29 * @package AmeliaBooking\Application\Commands\Booking\Appointment
30 */
31 class GetTimeSlotsCommandHandler extends CommandHandler
32 {
33 /**
34 * @var array
35 */
36 public $mandatoryFields = [
37 'serviceId'
38 ];
39
40 /**
41 * @param GetTimeSlotsCommand $command
42 *
43 * @return CommandResult
44 * @throws ContainerValueNotFoundException
45 * @throws ContainerException
46 * @throws Exception
47 * @throws InvalidArgumentException
48 * @throws QueryExecutionException
49 */
50 public function handle(GetTimeSlotsCommand $command)
51 {
52 $result = new CommandResult();
53
54 $this->checkMandatoryFields($command);
55
56 /** @var EntityService $entityService */
57 $entityService = $this->container->get('domain.entity.service');
58
59 /** @var ApplicationTimeSlotService $applicationTimeSlotService */
60 $applicationTimeSlotService = $this->container->get('application.timeSlot.service');
61
62 /** @var SettingsService $settingsDS */
63 $settingsDS = $this->container->get('domain.settings.service');
64
65 $props = [
66 'serviceId' => $command->getField('serviceId'),
67 'providerIds' => $command->getField('providerIds'),
68 'locationId' => $command->getField('locationId'),
69 'locationIds' => $command->getField('locationIds'),
70 'extras' => $command->getField('extras'),
71 'excludeAppointmentId' => $command->getField('excludeAppointmentId'),
72 'personsCount' => $command->getField('group') ? $command->getField('persons') : null,
73 'isFrontEndBooking' => $command->getField('page') === 'booking' || $command->getField('page') === 'cabinet',
74 'totalPersons' => $command->getField('persons'),
75 'serviceDuration' => $command->getField('serviceDuration'),
76 'monthsLoad' => $command->getField('monthsLoad'),
77 'startDateTime' => $command->getField('startDateTime'),
78 'endDateTime' => $command->getField('endDateTime'),
79 'queryTimeZone' => $command->getField('queryTimeZone'),
80 'timeZone' => $command->getField('timeZone'),
81 'structured' => $command->getField('structured'),
82
83 ];
84
85 $props = apply_filters('amelia_before_get_timeslots_filter', $props);
86
87 do_action('amelia_before_get_timeslots', $props);
88
89 $isFrontEndBooking = $props['isFrontEndBooking'];
90
91 /** @var SlotsEntities $slotsEntities */
92 $slotsEntities = $applicationTimeSlotService->getSlotsEntities(
93 [
94 'isFrontEndBooking' => $isFrontEndBooking,
95 'providerIds' => $props['providerIds'],
96 ]
97 );
98
99 $settings = $applicationTimeSlotService->getSlotsSettings($isFrontEndBooking, $slotsEntities, $props);
100
101 $lastBookedProviderId = null;
102
103 /** @var SlotsEntities $filteredSlotEntities */
104 $filteredSlotEntities = $entityService->getFilteredSlotsEntities(
105 $settings,
106 $props,
107 $slotsEntities
108 );
109
110 /** @var Service $service */
111 $service = $filteredSlotEntities->getServices()->getItem($props['serviceId']);
112
113 if ($props['serviceDuration']) {
114 $service->setDuration(new PositiveDuration($props['serviceDuration']));
115 }
116
117 $minimumBookingTimeInSeconds = $settingsDS
118 ->getEntitySettings($service->getSettings())
119 ->getGeneralSettings()
120 ->getMinimumTimeRequirementPriorToBooking();
121
122 $maximumBookingTimeInDays = $settingsDS
123 ->getEntitySettings($service->getSettings())
124 ->getGeneralSettings()
125 ->getNumberOfDaysAvailableForBooking();
126
127 $monthsLoad = $props['monthsLoad'];
128
129 $loadGeneratedPeriod = $monthsLoad &&
130 !$props['endDateTime'];
131
132 $timeZone = $props['queryTimeZone'] ?: DateTimeService::getTimeZone()->getName();
133
134 $queryStartDateTime = $props['startDateTime'] ?
135 DateTimeService::getDateTimeObjectInTimeZone(
136 $props['startDateTime'],
137 $timeZone
138 )->setTimezone(DateTimeService::getTimeZone()) : null;
139
140 $queryEndDateTime = $props['endDateTime'] ?
141 DateTimeService::getDateTimeObjectInTimeZone(
142 $props['endDateTime'],
143 $timeZone
144 )->setTimezone(DateTimeService::getTimeZone()) : null;
145
146 $minimumDateTime = $applicationTimeSlotService->getMinimumDateTimeForBooking(
147 null,
148 $isFrontEndBooking,
149 $minimumBookingTimeInSeconds
150 );
151
152 $startDateTime = $queryStartDateTime ?:
153 $applicationTimeSlotService->getMinimumDateTimeForBooking(
154 null,
155 $isFrontEndBooking,
156 $minimumBookingTimeInSeconds
157 );
158
159 $endDateTime = $queryEndDateTime ?:
160 $applicationTimeSlotService->getMaximumDateTimeForBooking(
161 null,
162 $isFrontEndBooking,
163 $maximumBookingTimeInDays
164 );
165
166 $maximumDateTime = $applicationTimeSlotService->getMaximumDateTimeForBooking(
167 null,
168 $isFrontEndBooking,
169 $maximumBookingTimeInDays
170 );
171
172 if ($isFrontEndBooking) {
173 $startDateTime = $startDateTime < $minimumDateTime ? $minimumDateTime : $startDateTime;
174
175 $endDateTime = $endDateTime > $maximumDateTime ? $maximumDateTime : $endDateTime;
176 }
177
178 // set initial search period if query dates are not set
179 if ($loadGeneratedPeriod) {
180 $endDateTime = DateTimeService::getCustomDateTimeObject(
181 $startDateTime->format('Y-m-d H:i:s')
182 )->setTimezone(
183 new DateTimeZone($timeZone)
184 );
185
186 $endDateTime->modify('first day of this month');
187
188 $endDateTime->modify('+' . ($monthsLoad - 1) . 'months');
189
190 $endDateTime->modify('last day of this month');
191
192 $endDateTime->modify('+12days');
193
194 $endDateTime->setTime(23, 59, 59);
195
196 if ($isFrontEndBooking) {
197 $endDateTime = $endDateTime > $maximumDateTime ?
198 DateTimeService::getDateTimeObjectInTimeZone(
199 $maximumDateTime->format('Y-m-d H:i'),
200 $timeZone
201 ) : $endDateTime;
202 }
203
204 $endDateTime->setTimezone(DateTimeService::getTimeZone());
205 }
206
207 /** @var Service $filteredSlotEntitiesService */
208 foreach ($filteredSlotEntities->getServices()->getItems() as $filteredSlotEntitiesService) {
209 if ($filteredSlotEntitiesService->getId()->getValue() === $service->getId()->getValue()) {
210 $filteredSlotEntitiesService->setDuration($service->getDuration());
211
212 break;
213 }
214 }
215
216 /** @var Provider $filteredSlotEntitiesProvider */
217 foreach ($filteredSlotEntities->getProviders()->getItems() as $filteredSlotEntitiesProvider) {
218 /** @var Service $providerService */
219 foreach ($filteredSlotEntitiesProvider->getServiceList()->getItems() as $providerService) {
220 if ($providerService->getId()->getValue() === $service->getId()->getValue()) {
221 $providerService->setDuration($service->getDuration());
222
223 break;
224 }
225 }
226 }
227
228 $freeSlots = $applicationTimeSlotService->getSlotsByProps(
229 $settings,
230 array_merge(
231 $props,
232 [
233 'startDateTime' => $startDateTime,
234 'endDateTime' => $endDateTime,
235 ]
236 ),
237 $filteredSlotEntities
238 );
239
240 if ($loadGeneratedPeriod) {
241 // search with new period until slots are not found
242 while (!$freeSlots['available'] && $endDateTime && $endDateTime <= $maximumDateTime) {
243 $startDateTime = DateTimeService::getCustomDateTimeObject(
244 $endDateTime->format('Y-m-d H:i:s')
245 )->setTimezone(
246 new DateTimeZone($timeZone)
247 );
248
249 $startDateTime->setTime(0, 0, 0);
250
251 $endDateTime->modify('first day of this month');
252
253 $endDateTime->modify('+' . ($monthsLoad - 1) . 'months');
254
255 $endDateTime->modify('last day of this month');
256
257 $endDateTime->modify('+12days');
258
259 $endDateTime->setTime(23, 59, 59);
260
261 if ($isFrontEndBooking) {
262 $endDateTime = $endDateTime > $maximumDateTime ?
263 DateTimeService::getDateTimeObjectInTimeZone(
264 $maximumDateTime->format('Y-m-d H:i'),
265 $timeZone
266 ) : $endDateTime;
267 }
268
269 $endDateTime->setTimezone(DateTimeService::getTimeZone());
270
271 AbstractGoogleCalendarService::$providersGoogleEvents = [];
272
273 AbstractOutlookCalendarService::$providersOutlookEvents = [];
274
275 AbstractAppleCalendarService::$providersAppleEvents = [];
276
277 $freeSlots = $applicationTimeSlotService->getSlotsByProps(
278 $settings,
279 array_merge(
280 $props,
281 [
282 'startDateTime' => $startDateTime,
283 'endDateTime' => $endDateTime,
284 ]
285 ),
286 $filteredSlotEntities
287 );
288
289 $endDateTimeCopy = clone $endDateTime;
290
291 $maximumDateTimeCopy = clone $maximumDateTime;
292
293 if (
294 $endDateTimeCopy->format('Y-m-d H:i') === $maximumDateTimeCopy->format('Y-m-d H:i') ||
295 ($endDateTimeCopy->setTimezone(new DateTimeZone('UTC'))->format('Y-m-d H:i') ===
296 $maximumDateTimeCopy->setTimezone(new DateTimeZone('UTC'))->format('Y-m-d H:i')) ||
297 ($endDateTimeCopy > $maximumDateTimeCopy)
298 ) {
299 break;
300 }
301 }
302
303 // search once more if first available date is in 11 days added to endDateTime (days outside calendar on frontend form)
304 foreach (array_slice($freeSlots['available'], 0, 1, true) as $slotDate => $slotTimes) {
305 if (substr($slotDate, 0, 7) === $endDateTime->format('Y-m')) {
306 $endDateTime->modify('last day of this month');
307
308 $endDateTime->modify('+12days');
309
310 $endDateTime->setTime(23, 59, 59);
311
312 if ($isFrontEndBooking) {
313 $endDateTime = $endDateTime > $maximumDateTime ?
314 DateTimeService::getDateTimeObjectInTimeZone(
315 $maximumDateTime->format('Y-m-d H:i'),
316 $timeZone
317 ) : $endDateTime;
318 }
319
320 AbstractGoogleCalendarService::$providersGoogleEvents = [];
321
322 AbstractOutlookCalendarService::$providersOutlookEvents = [];
323
324 AbstractAppleCalendarService::$providersAppleEvents = [];
325
326 $freeSlots = $applicationTimeSlotService->getSlotsByProps(
327 $settings,
328 array_merge(
329 $props,
330 [
331 'startDateTime' => $startDateTime,
332 'endDateTime' => $endDateTime,
333 ]
334 ),
335 $filteredSlotEntities
336 );
337 }
338 }
339 }
340
341 $busyness = [];
342
343 foreach ($freeSlots['available'] as $slotDate => $slotTimes) {
344 $busyness[$slotDate] = round(
345 count(!empty($freeSlots['occupied'][$slotDate]) ? $freeSlots['occupied'][$slotDate] : []) /
346 (count(!empty($freeSlots['available'][$slotDate]) ? $freeSlots['available'][$slotDate] : []) +
347 count(!empty($freeSlots['occupied'][$slotDate]) ? $freeSlots['occupied'][$slotDate] : []))
348 * 100
349 );
350 }
351
352 $converted = ['available' => [], 'occupied' => []];
353
354 $isUtcResponse = ($settingsDS->getSetting('general', 'showClientTimeZone') && $isFrontEndBooking) ||
355 $props['timeZone'];
356
357 if ($isUtcResponse) {
358 foreach (['available', 'occupied'] as $type) {
359 foreach ($freeSlots[$type] as $slotDate => $slotTimes) {
360 foreach ($freeSlots[$type][$slotDate] as $slotTime => $slotTimesProviders) {
361 $convertedSlotParts = explode(
362 ' ',
363 $props['timeZone'] ?
364 DateTimeService::getCustomDateTimeObjectInTimeZone(
365 $slotDate . ' ' . $slotTime,
366 $props['timeZone']
367 )->format('Y-m-d H:i') :
368 DateTimeService::getCustomDateTimeObjectInUtc(
369 $slotDate . ' ' . $slotTime
370 )->format('Y-m-d H:i')
371 );
372
373 $converted[$type][$convertedSlotParts[0]][$convertedSlotParts[1]] = $slotTimesProviders;
374 }
375 }
376 }
377 }
378
379 if (count($props['providerIds']) !== 1 && $settingsDS->getSetting('appointments', 'employeeSelection') === 'roundRobin') {
380 /** @var AppointmentRepository $appointmentRepository */
381 $appointmentRepository = $this->container->get('domain.booking.appointment.repository');
382
383 $lastBookedProviderId = $appointmentRepository->getLastBookedEmployee($props['providerIds']);
384 }
385
386 $resultData = [
387 'slots' => $converted['available'] ?: $freeSlots['available'],
388 'occupied' => $converted['occupied'] ?: $freeSlots['occupied'],
389 'minimum' => $isUtcResponse ?
390 $minimumDateTime->setTimezone(
391 new DateTimeZone('UTC')
392 )->format('Y-m-d H:i') : $minimumDateTime->format('Y-m-d H:i'),
393 'maximum' => $isUtcResponse ?
394 $maximumDateTime->setTimezone(
395 new DateTimeZone('UTC')
396 )->format('Y-m-d H:i') : $maximumDateTime->format('Y-m-d H:i'),
397 'busyness' => $busyness,
398 'lastBookedProviderId' => $lastBookedProviderId,
399 'appCount' => $freeSlots['appCount'],
400 'duration' => $freeSlots['duration'],
401 ];
402
403
404 $resultData = apply_filters('amelia_get_timeslots_filter', $resultData, $props);
405
406 do_action('amelia_get_timeslots', $resultData, $props);
407
408
409 $result->setResult(CommandResult::RESULT_SUCCESS);
410 $result->setMessage('Successfully retrieved free slots');
411 $result->setData(
412 [
413 'minimum' => $resultData['minimum'],
414 'maximum' => $resultData['maximum'],
415 'slots' => $resultData['slots'],
416 'occupied' => $resultData['occupied'],
417 'busyness' => $resultData['busyness'],
418 'lastProvider' => $resultData['lastBookedProviderId'],
419 'appCount' => $resultData['appCount'],
420 'duration' => $resultData['duration'],
421 ]
422 );
423
424 return $result;
425 }
426 }
427