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