PluginProbe ʕ •ᴥ•ʔ
Booking for Appointments and Events Calendar – Amelia / 2.2
Booking for Appointments and Events Calendar – Amelia v2.2
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 / Calendar / GetCalendarSlotsCommandHandler.php
ameliabooking / src / Application / Commands / Calendar Last commit date
DeleteBlockTimeCommand.php 3 months ago DeleteBlockTimeCommandHandler.php 3 months ago GetBlockTimeCommand.php 3 months ago GetBlockTimeCommandHandler.php 3 months ago GetCalendarEventsCommand.php 6 months ago GetCalendarEventsCommandHandler.php 3 months ago GetCalendarSlotAvailabilityCommand.php 6 months ago GetCalendarSlotAvailabilityHandler.php 5 months ago GetCalendarSlotEntitiesCommand.php 6 months ago GetCalendarSlotEntitiesCommandHandler.php 6 months ago GetCalendarSlotsCommand.php 6 months ago GetCalendarSlotsCommandHandler.php 3 months ago ManageCalendarBlockTimeCommand.php 3 months ago ManageCalendarBlockTimeCommandHandler.php 3 months ago
GetCalendarSlotsCommandHandler.php
625 lines
1 <?php
2
3 /**
4 * @copyright © Melograno Ventures. All rights reserved.
5 * @licence See LICENCE.md for license details.
6 */
7
8 namespace AmeliaBooking\Application\Commands\Calendar;
9
10 use AmeliaBooking\Application\Commands\CommandHandler;
11 use AmeliaBooking\Application\Commands\CommandResult;
12 use AmeliaBooking\Application\Services\User\ProviderApplicationService;
13 use AmeliaBooking\Domain\Entity\Entities;
14 use AmeliaBooking\Domain\Collection\Collection;
15 use AmeliaBooking\Domain\Common\Exceptions\InvalidArgumentException;
16 use AmeliaBooking\Domain\Entity\Schedule\DayOff;
17 use AmeliaBooking\Domain\Entity\Schedule\Period;
18 use AmeliaBooking\Domain\Entity\Schedule\SpecialDay;
19 use AmeliaBooking\Domain\Entity\Schedule\WeekDay;
20 use AmeliaBooking\Domain\Entity\User\AbstractUser;
21 use AmeliaBooking\Domain\Entity\User\Provider;
22 use AmeliaBooking\Domain\Services\DateTime\DateTimeService;
23 use AmeliaBooking\Domain\ValueObjects\DateTime\DateTimeValue;
24 use AmeliaBooking\Domain\ValueObjects\String\BookingStatus;
25 use AmeliaBooking\Domain\ValueObjects\String\Status;
26 use AmeliaVendor\Psr\Container\ContainerExceptionInterface;
27 use DateInterval;
28 use DateInvalidTimeZoneException;
29 use DateMalformedPeriodStringException;
30 use DatePeriod;
31 use DateTime;
32 use DateTimeZone;
33 use Exception;
34
35 class GetCalendarSlotsCommandHandler extends CommandHandler
36 {
37 private $timeLimits = ['slotMinTime' => '24:00:00', 'slotMaxTime' => '00:00:00'];
38 private $userTimezone;
39
40 public function handle(GetCalendarSlotsCommand $command): CommandResult
41 {
42 $result = new CommandResult();
43
44 $providerRepository = $this->container->get('domain.users.providers.repository');
45 $locationRepository = $this->container->get('domain.locations.repository');
46
47 $this->userTimezone = DateTimeService::getTimeZone()->getName();
48
49 /** @var AbstractUser $user */
50 $user = $this->container->get('logged.in.user');
51 if ($user->getType() === Entities::PROVIDER) {
52 /** @var ProviderApplicationService $providerAS */
53 $providerAS = $this->container->get('application.user.provider.service');
54 $this->userTimezone = $providerAS->getTimeZone($user);
55 }
56
57 $queryParams = $command->getField('queryParams');
58 $allWorkDays = [];
59 $selectedService = $queryParams['service'] ?? null;
60
61 $queryParams['locations'] = array_map(
62 fn($location) => $location['id'],
63 $locationRepository->getFiltered(
64 ['status' => !empty($queryParams['providers']) ? null : Status::VISIBLE],
65 0
66 )->toArray()
67 );
68
69 $criteria = ['providerStatus' => !empty($queryParams['providers']) ? null : Status::VISIBLE];
70 foreach ($queryParams as $key => $value) {
71 if ($key !== 'providerStatus') {
72 $criteria[$key] = $value;
73 }
74 }
75
76 $providers = $providerRepository->getWithSchedule($criteria)->getItems();
77
78 foreach ($providers as $provider) {
79 if (!$selectedService) {
80 $providerWorkDays = $this->getProviderWorkDays($provider, $queryParams);
81 $this->getTimeLimitsByProvider($queryParams, $providerWorkDays, $provider);
82 $this->mergeProviderWorkDays($allWorkDays, $providerWorkDays);
83 }
84 }
85
86 if (empty($allWorkDays) || $user->getType() === Entities::CUSTOMER) {
87 $this->fillEmptyWorkDays($allWorkDays, $queryParams);
88 }
89
90 $this->processCompanyDaysOff($allWorkDays, $queryParams);
91 $formattedWorkPeriods = $this->formatWorkDays($allWorkDays);
92
93 $this->getTimeLimitsFromAppointmentsAndEvents($queryParams);
94
95 $result->setData([
96 'workPeriods' => $formattedWorkPeriods,
97 'slotMinTime' => $this->timeLimits['slotMinTime'],
98 'slotMaxTime' => $this->timeLimits['slotMaxTime'],
99 'now' => DateTimeService::getNowDateTime()
100 ]);
101
102 return $result;
103 }
104
105 /**
106 * @throws DateMalformedPeriodStringException
107 */
108 private function fillEmptyWorkDays(array &$allWorkDays, array $queryParams): void
109 {
110 [$this->timeLimits['slotMinTime'], $this->timeLimits['slotMaxTime']] = $this->getLimitsFromCompanyWorkHours();
111
112 if ($this->timeLimits['slotMinTime'] === '24:00:00' && $this->timeLimits['slotMaxTime'] === '00:00:00') {
113 $this->timeLimits['slotMinTime'] = '09:00:00';
114 $this->timeLimits['slotMaxTime'] = '17:00:00';
115 }
116
117 $calendarStartDate = DateTime::createFromFormat('Y-m-d', $queryParams['calendarStartDate']);
118 $calendarEndDate = DateTime::createFromFormat('Y-m-d', $queryParams['calendarEndDate']);
119
120 $datePeriod = new DatePeriod($calendarStartDate, new DateInterval('P1D'), $calendarEndDate);
121
122 foreach ($datePeriod as $date) {
123 $dateString = $date->format('Y-m-d');
124 $allWorkDays[$dateString] = ['groupId' => 'notWorkHours', 'periods' => []];
125 }
126 }
127
128 /**
129 * @throws DateMalformedPeriodStringException
130 * @throws InvalidArgumentException
131 * @throws Exception
132 */
133 private function getProviderWorkDays(Provider $provider, array $queryParams): array
134 {
135 $providerTimeZone = $provider->getTimezone() ? $provider->getTimezone()->getValue() : DateTimeService::getTimeZone()->getName();
136
137 $employeeDays = [];
138 $startDate = (new DateTime($queryParams['calendarStartDate'], new DateTimeZone($this->userTimezone)))->modify('-1 day');
139 $endDate = (new DateTime($queryParams['calendarEndDate'], new DateTimeZone($this->userTimezone)))->modify('+1 day');
140
141 $datePeriod = new DatePeriod($startDate, new DateInterval('P1D'), $endDate);
142
143 $weekDays = $provider->getWeekDayList()->getItems();
144 $specialDays = $provider->getSpecialDayList()->getItems();
145 $daysOff = $provider->getDayOffList()->getItems();
146
147 foreach ($datePeriod as $date) {
148 $dateString = $date->format('Y-m-d');
149 $weekDay = $this->findMatchingDay($weekDays, $date->format('N'));
150
151 $specialDay = $this->findMatchingSpecialDay($specialDays, $date);
152
153 if ($specialDay) {
154 $this->mapPeriods(
155 $employeeDays,
156 $specialDay->getPeriodList()->getItems(),
157 $dateString,
158 $providerTimeZone,
159 $this->userTimezone,
160 );
161
162 continue;
163 }
164
165 $this->mapPeriods(
166 $employeeDays,
167 $weekDay ? $weekDay->getPeriodList()->getItems() : [],
168 $dateString,
169 $providerTimeZone,
170 $this->userTimezone,
171 );
172
173 $dayOff = $this->findMatchingDayOff($daysOff, $date);
174 if ($dayOff) {
175 $this->mapPeriods(
176 $employeeDays,
177 [
178 new Period(
179 new DateTimeValue(\DateTime::createFromFormat('H:i:s', '00:00:00')),
180 new DateTimeValue(\DateTime::createFromFormat('H:i:s', '00:00:00')),
181 new Collection(),
182 new Collection()
183 )
184 ],
185 $dateString,
186 $providerTimeZone,
187 $this->userTimezone,
188 'dayOff'
189 );
190 }
191 }
192
193 return $employeeDays;
194 }
195
196 private function findMatchingDay(array $weekDays, int $dateDayIndex): ?WeekDay
197 {
198 foreach ($weekDays as $weekDay) {
199 if ($weekDay->getDayIndex()->getValue() === $dateDayIndex) {
200 return $weekDay;
201 }
202 }
203
204 return null;
205 }
206
207 private function findMatchingSpecialDay(array $specialDays, DateTime $date): ?SpecialDay
208 {
209 foreach ($specialDays as $specialDay) {
210 if ($date >= $specialDay->getStartDate()->getValue() && $date <= $specialDay->getEndDate()->getValue()) {
211 return $specialDay;
212 }
213 }
214
215 return null;
216 }
217
218 private function findMatchingDayOff(array $daysOff, DateTime $date): ?DayOff
219 {
220 foreach ($daysOff as $dayOff) {
221 if ($date >= $dayOff->getStartDate()->getValue() && $date <= $dayOff->getEndDate()->getValue()) {
222 return $dayOff;
223 }
224 }
225
226 return null;
227 }
228
229 /**
230 * @throws Exception
231 */
232 private function mapPeriods(
233 array &$employeeDays,
234 array $periods,
235 string $dateString,
236 string $providerTimeZone,
237 string $currentUserTimeZone,
238 string $groupId = 'workHours'
239 ): void {
240 if (!isset($employeeDays[$dateString])) {
241 $employeeDays[$dateString] = [
242 'groupId' => 'workHours',
243 'periods' => []
244 ];
245 }
246
247 foreach ($periods as $period) {
248 $startDateTime = $this->convertWorkPeriods(
249 new DateTime($dateString . $period->getStartTime()->getValue()->format('H:i:s')),
250 $providerTimeZone,
251 $currentUserTimeZone
252 );
253
254 $endDateString = $dateString;
255 $endTimeString = $period->getEndTime()->getValue()->format('H:i:s');
256 if ($endTimeString === '00:00:00') {
257 $endDateString = (new DateTime($dateString))->modify('+1 day')->format('Y-m-d');
258 }
259
260 $endDateTime = $this->convertWorkPeriods(
261 new DateTime($endDateString . $endTimeString),
262 $providerTimeZone,
263 $currentUserTimeZone
264 );
265
266 $startDate = $startDateTime->format('Y-m-d');
267 $startTime = $startDateTime->format('H:i:s');
268 $endDate = $endDateTime->format('Y-m-d');
269 $endTime = $endDateTime->format('H:i:s');
270
271 if ($startDate !== $dateString || $endDate !== $dateString) {
272 if (!isset($employeeDays[$startDate])) {
273 $employeeDays[$startDate] = [
274 'groupId' => 'workHours',
275 'periods' => []
276 ];
277 }
278
279 if (!isset($employeeDays[$endDate])) {
280 $employeeDays[$endDate] = [
281 'groupId' => 'workHours',
282 'periods' => []
283 ];
284 }
285
286 $this->addOrReplacePeriod($employeeDays[$startDate]['periods'], $groupId, $startTime, '24:00:00');
287 $this->addOrReplacePeriod($employeeDays[$endDate]['periods'], $groupId, '00:00:00', $endTime);
288
289 continue;
290 }
291
292 $normalizedEndTime = $endTime === '00:00:00' ? '24:00:00' : $endTime;
293 $this->addOrReplacePeriod($employeeDays[$dateString]['periods'], $groupId, $startTime, $normalizedEndTime);
294 }
295 }
296
297 /**
298 * @throws Exception
299 */
300 private function addOrReplacePeriod(array &$periods, string $groupId, string $startTime, string $endTime): void
301 {
302 if ($groupId === 'dayOff') {
303 foreach ($periods as $key => $existingPeriod) {
304 if (new DateTime($existingPeriod['start']) >= new DateTime($startTime) && new DateTime($existingPeriod['end']) <= new DateTime($endTime)) {
305 unset($periods[$key]);
306 }
307 }
308 }
309
310 if (new DateTime($startTime) < new DateTime($endTime)) {
311 $periods[] = ['groupId' => $groupId, 'start' => $startTime, 'end' => $endTime];
312 }
313 }
314
315 private function mergeProviderWorkDays(array &$allWorkDays, array $providerWorkDays): void
316 {
317 foreach ($providerWorkDays as $date => $info) {
318 if (!isset($allWorkDays[$date])) {
319 $allWorkDays[$date] = [
320 'groupId' => $info['groupId'],
321 'periods' => []
322 ];
323 }
324
325 foreach ($info['periods'] as $period) {
326 $merged = false;
327
328 foreach ($allWorkDays[$date]['periods'] as &$existingPeriod) {
329 if (
330 $period['groupId'] === $existingPeriod['groupId'] &&
331 $period['start'] <= $existingPeriod['end'] &&
332 $period['end'] >= $existingPeriod['start']
333 ) {
334 $existingPeriod['start'] = min($existingPeriod['start'], $period['start']);
335 $existingPeriod['end'] = max($existingPeriod['end'], $period['end']);
336 $merged = true;
337 }
338 }
339
340 if (!$merged) {
341 $allWorkDays[$date]['periods'][] = $period;
342 }
343 }
344 }
345 }
346
347 private function formatWorkDays(array $allWorkDays): array
348 {
349 $formattedPeriods = [];
350
351 foreach ($allWorkDays as $date => $info) {
352 $periods = $info['periods'];
353 if (empty($periods)) {
354 $formattedPeriods[] = $this->createPeriod($date, $date, 'notWorkHours', 'not-work-hours');
355 continue;
356 }
357
358 usort($periods, fn($a, $b) => $a['start'] <=> $b['start']);
359
360 foreach ($periods as $i => $period) {
361 $start = "{$date}T{$period['start']}";
362 $end = "{$date}T{$period['end']}";
363
364 if ($i === 0 && $period['start'] !== '00:00:00') {
365 $formattedPeriods[] = $this->createPeriod("{$date}T00:00:00", $start, 'notWorkHours', 'not-work-hours');
366 }
367
368 if ($period['groupId'] === 'dayOff') {
369 $formattedPeriods[] = $this->createPeriod($start, $end, 'dayOff', 'day-off');
370 } else {
371 $formattedPeriods[] = $this->createPeriod($start, $end, 'workHours', 'work-hours');
372 }
373
374 if (isset($periods[$i + 1]) && $period['end'] !== $periods[$i + 1]['start']) {
375 $formattedPeriods[] = $this->createPeriod($end, "{$date}T{$periods[$i + 1]['start']}", 'notWorkHours', 'not-work-hours');
376 }
377
378 if ($i === count($periods) - 1 && $period['end'] !== '24:00:00') {
379 $formattedPeriods[] = $this->createPeriod($end, "{$date}T24:00:00", 'notWorkHours', 'not-work-hours');
380 }
381 }
382 }
383
384 return $formattedPeriods;
385 }
386
387 private function createPeriod(string $start, string $end, string $groupId, string $className): array
388 {
389 return [
390 'groupId' => $groupId,
391 'start' => $start,
392 'end' => $end,
393 'display' => 'background',
394 'className' => $className
395 ];
396 }
397
398 private function getTimeLimitsByProvider(array $queryParams, array $periods, Provider $provider): void
399 {
400 [$this->timeLimits['slotMinTime'], $this->timeLimits['slotMaxTime']] = $this->getTimeLimitsFromPeriods(
401 $periods,
402 $this->timeLimits['slotMinTime'],
403 $this->timeLimits['slotMaxTime']
404 );
405
406 if ($this->timeLimits['slotMinTime'] === '24:00:00' && $this->timeLimits['slotMaxTime'] === '00:00:00') {
407 [$this->timeLimits['slotMinTime'], $this->timeLimits['slotMaxTime']] = $this->getLimitsFromCompanyWorkHours();
408 }
409
410 if ($this->timeLimits['slotMinTime'] === '24:00:00' && $this->timeLimits['slotMaxTime'] === '00:00:00') {
411 $this->timeLimits['slotMinTime'] = '09:00:00';
412 $this->timeLimits['slotMaxTime'] = '17:00:00';
413 }
414 }
415
416 private function getTimeLimitsFromPeriods(array $providerWorkDays, string $slotMinTime, string $slotMaxTime): array
417 {
418 foreach ($providerWorkDays as $providerWorkDay) {
419 foreach ($providerWorkDay['periods'] as $period) {
420 if ($period['groupId'] === 'dayOff') {
421 continue;
422 }
423
424 $slotMinTime = min($slotMinTime, $period['start']);
425 $slotMaxTime = max($slotMaxTime, $period['end']);
426 }
427 }
428
429 return [$slotMinTime, $slotMaxTime];
430 }
431
432 private function getLimitsFromCompanyWorkHours(): array
433 {
434 $settingsDS = $this->container->get('domain.settings.service');
435 $slotMinTime = '24:00:00';
436 $slotMaxTime = '00:00:00';
437 $companyWorkHours = $settingsDS->getCategorySettings('weekSchedule');
438
439 foreach ($companyWorkHours as $companyWorkHour) {
440 if (!is_null($companyWorkHour['time'][0]) && !is_null($companyWorkHour['time'][1])) {
441 $slotMinTime = min($slotMinTime, $companyWorkHour['time'][0] . ':00');
442 $slotMaxTime = max($slotMaxTime, $companyWorkHour['time'][1] . ':00');
443 }
444 }
445
446 return [$slotMinTime, $slotMaxTime];
447 }
448
449 private function getTimeLimitsFromAppointmentsAndEvents(array $queryParams): void
450 {
451 if (isset($queryParams['entitiesToShow']) && in_array('appointments', $queryParams['entitiesToShow'])) {
452 [$this->timeLimits['slotMinTime'], $this->timeLimits['slotMaxTime']] =
453 $this->getLimitsForAppointments($queryParams, $this->timeLimits['slotMinTime'], $this->timeLimits['slotMaxTime']);
454 }
455
456 if (isset($queryParams['entitiesToShow']) && in_array('events', $queryParams['entitiesToShow'])) {
457 [$this->timeLimits['slotMinTime'], $this->timeLimits['slotMaxTime']] =
458 $this->getLimitsForEvents($queryParams, $this->timeLimits['slotMinTime'], $this->timeLimits['slotMaxTime']);
459 }
460
461 [$this->timeLimits['slotMinTime'], $this->timeLimits['slotMaxTime']] =
462 $this->getLimitsForBlockTime($queryParams, $this->timeLimits['slotMinTime'], $this->timeLimits['slotMaxTime']);
463 }
464
465 private function getLimitsForAppointments($queryParams, $slotMinTime, $slotMaxTime): array
466 {
467 $appointmentRepository = $this->container->get('domain.booking.appointment.repository');
468
469 $queryParams['calendarStartDate'] = DateTimeService::getCustomDateTimeInUtc($queryParams['calendarStartDate'] . ' 00:00:00');
470 $queryParams['calendarEndDate'] = DateTimeService::getCustomDateTimeInUtc($queryParams['calendarEndDate'] . ' 23:59:59');
471
472 $statuses = isset($queryParams['statuses']) && in_array('pendingAppointments', $queryParams['statuses'])
473 ? [BookingStatus::APPROVED, BookingStatus::PENDING]
474 : [BookingStatus::APPROVED];
475
476 $appointments = $appointmentRepository->getFiltered([
477 'dates' => [$queryParams['calendarStartDate'], $queryParams['calendarEndDate']],
478 'providers' => !empty($queryParams['providers']) ? $queryParams['providers'] : [],
479 'statuses' => $statuses
480 ]);
481
482 foreach ($appointments->getItems() as $appointment) {
483 $startDateTime = $appointment->getBookingStart()->getValue()->setTimezone(new DateTimeZone($this->userTimezone))->sub(new DateInterval(
484 'PT' . abs($appointment->getService()->getTimeBefore() ? $appointment->getService()->getTimeBefore()->getValue() : 0) . 'S'
485 ));
486 $endDateTime = $appointment->getBookingEnd()->getValue()->setTimezone(new DateTimeZone($this->userTimezone))->add(new DateInterval(
487 'PT' . abs($appointment->getService()->getTimeAfter() ? $appointment->getService()->getTimeAfter()->getValue() : 0) . 'S'
488 ));
489
490 $startDate = $startDateTime->format('Y-m-d');
491 $endDate = $endDateTime->format('Y-m-d');
492
493 if ($startDate !== $endDate) {
494 return ['00:00:00', '24:00:00'];
495 }
496
497 $slotMinTime = min($slotMinTime, $startDateTime->format('H:i:s'));
498 $slotMaxTime = max($slotMaxTime, $endDateTime->format('H:i:s'));
499 }
500
501 return [$slotMinTime, $slotMaxTime];
502 }
503
504 private function getLimitsForEvents($queryParams, $slotMinTime, $slotMaxTime): array
505 {
506 $eventAS = $this->container->get('application.booking.event.service');
507
508 $statuses = isset($queryParams['statuses']) && in_array('pendingAppointments', $queryParams['statuses'])
509 ? [BookingStatus::APPROVED, BookingStatus::PENDING]
510 : [BookingStatus::APPROVED];
511
512 $events = $eventAS->getEventsByCriteria(
513 [
514 'dates' => [$queryParams['calendarStartDate'], $queryParams['calendarEndDate']],
515 'providers' => !empty($queryParams['providers']) ? $queryParams['providers'] : null,
516 'statuses' => $statuses
517 ],
518 ['fetchEventsPeriods' => true],
519 -1
520 );
521
522 foreach ($events->getItems() as $event) {
523 foreach ($event->getPeriods()->getItems() as $period) {
524 $startDateTime = $period->getPeriodStart()->getValue()->format('H:i:s');
525 $endDateTime = $period->getPeriodEnd()->getValue()->format('H:i:s');
526
527 $slotMinTime = min($slotMinTime, $startDateTime);
528 $slotMaxTime = max($slotMaxTime, $endDateTime);
529 }
530 }
531
532 return [$slotMinTime, $slotMaxTime];
533 }
534
535 private function getLimitsForBlockTime($queryParams, $slotMinTime, $slotMaxTime): array
536 {
537 $dayOffRepository = $this->container->get('domain.schedule.dayOff.repository');
538
539 $queryParams['type'] = 'blockTime';
540 $queryParams['dates'] = [$queryParams['calendarStartDate'], $queryParams['calendarEndDate']];
541
542 $blockTimes = $dayOffRepository->getFiltered($queryParams);
543
544 foreach ($blockTimes->getItems() as $blockTime) {
545 $startDateTime = $blockTime->getStartDate()->getValue()->setTimezone(new DateTimeZone($this->userTimezone));
546 $endDateTime = $blockTime->getEndDate()->getValue()->setTimezone(new DateTimeZone($this->userTimezone));
547
548 $startDate = $startDateTime->format('Y-m-d');
549 $endDate = $endDateTime->format('Y-m-d');
550
551 if ($startDate !== $endDate) {
552 return ['00:00:00', '24:00:00'];
553 }
554
555 $slotMinTime = min($slotMinTime, $startDateTime->format('H:i:s'));
556 $slotMaxTime = max($slotMaxTime, $endDateTime->format('H:i:s'));
557 }
558
559 return [$slotMinTime, $slotMaxTime];
560 }
561
562 /**
563 * @param array $allWorkDays
564 * @param array $queryParams
565 * @return void
566 * @throws ContainerExceptionInterface
567 * @throws DateInvalidTimeZoneException
568 * @throws DateMalformedPeriodStringException
569 * @throws InvalidArgumentException
570 */
571 private function processCompanyDaysOff(array &$allWorkDays, array $queryParams): void
572 {
573 $isDateRangeOverlapping = fn(DateTime $start1, DateTime $end1, DateTime $start2, DateTime $end2): bool =>
574 $start1 <= $end2 && $end1 >= $start2;
575
576 $settingsDS = $this->container->get('domain.settings.service');
577 $calendarStartDate = DateTime::createFromFormat('Y-m-d', $queryParams['calendarStartDate']);
578 $calendarEndDate = DateTime::createFromFormat('Y-m-d', $queryParams['calendarEndDate']);
579
580 $systemTimezone = DateTimeService::getTimeZone();
581 $userTimezone = DateTimeService::getTimeZone()->getName();
582
583 $companyDaysOff = $settingsDS->getCategorySettings('daysOff');
584
585 foreach ($companyDaysOff as $key => $companyDayOff) {
586 $dayOffStartDate = (new DateTime($companyDayOff['startDate'] . ' 00:00:00', $systemTimezone))->setTimezone(new DateTimeZone($userTimezone));
587 $dayOffEndDate = (new DateTime($companyDayOff['endDate'] . ' 23:59:59', $systemTimezone))->setTimezone(new DateTimeZone($userTimezone));
588
589 if (!$isDateRangeOverlapping($calendarStartDate, $calendarEndDate, $dayOffStartDate, $dayOffEndDate)) {
590 unset($companyDaysOff[$key]);
591 }
592 }
593
594 foreach ($companyDaysOff as $companyDayOff) {
595 $dayOffStartDate = (new DateTime($companyDayOff['startDate'] . ' 00:00:00', $systemTimezone))->setTimezone(new DateTimeZone($userTimezone));
596 $dayOffEndDate = (new DateTime($companyDayOff['endDate'] . ' 23:59:59', $systemTimezone))->setTimezone(new DateTimeZone($userTimezone));
597
598 $datePeriod = new DatePeriod($dayOffStartDate, new DateInterval('P1D'), $dayOffEndDate);
599 foreach ($datePeriod as $date) {
600 $this->mapPeriods(
601 $allWorkDays,
602 [
603 new Period(
604 new DateTimeValue(\DateTime::createFromFormat('H:i:s', '00:00:00')),
605 new DateTimeValue(\DateTime::createFromFormat('H:i:s', '00:00:00')),
606 new Collection(),
607 new Collection()
608 )
609 ],
610 $date->format('Y-m-d'),
611 $systemTimezone->getName(),
612 $userTimezone,
613 'dayOff'
614 );
615 }
616 }
617 }
618
619 private function convertWorkPeriods($period, string $providerTimezone, string $userTimezone): DateTime
620 {
621 return (new DateTime($period->format('Y-m-d H:i:s'), new DateTimeZone($providerTimezone)))
622 ->setTimezone(new DateTimeZone($userTimezone));
623 }
624 }
625