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