CalendarRescheduleEventCommand.php
6 months ago
CalendarRescheduleEventCommandHandler.php
6 months ago
GetCalendarEventsCommand.php
6 months ago
GetCalendarEventsCommandHandler.php
6 months ago
GetCalendarSlotAvailabilityCommand.php
6 months ago
GetCalendarSlotAvailabilityHandler.php
6 months ago
GetCalendarSlotEntitiesCommand.php
6 months ago
GetCalendarSlotEntitiesCommandHandler.php
6 months ago
GetCalendarSlotsCommand.php
6 months ago
GetCalendarSlotsCommandHandler.php
6 months ago
GetCalendarEventsCommandHandler.php
276 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\Common\Exceptions\AccessDeniedException; |
| 13 | use AmeliaBooking\Application\Services\Booking\EventApplicationService; |
| 14 | use AmeliaBooking\Domain\Entity\Booking\Appointment\Appointment; |
| 15 | use AmeliaBooking\Domain\Entity\Booking\Event\Event; |
| 16 | use AmeliaBooking\Domain\Entity\Booking\Event\EventPeriod; |
| 17 | use AmeliaBooking\Domain\Entity\Entities; |
| 18 | use AmeliaBooking\Domain\Entity\User\AbstractUser; |
| 19 | use AmeliaBooking\Domain\Services\Settings\SettingsService; |
| 20 | use AmeliaBooking\Domain\ValueObjects\String\BookingStatus; |
| 21 | use AmeliaBooking\Infrastructure\Common\Exceptions\QueryExecutionException; |
| 22 | use AmeliaBooking\Infrastructure\Repository\Booking\Appointment\AppointmentRepository; |
| 23 | |
| 24 | class GetCalendarEventsCommandHandler extends CommandHandler |
| 25 | { |
| 26 | /** |
| 27 | * @param GetCalendarEventsCommand $command |
| 28 | * @return CommandResult |
| 29 | * @throws AccessDeniedException |
| 30 | * @throws QueryExecutionException |
| 31 | */ |
| 32 | public function handle(GetCalendarEventsCommand $command): CommandResult |
| 33 | { |
| 34 | $result = new CommandResult(); |
| 35 | $queryParams = $command->getField('queryParams'); |
| 36 | |
| 37 | if ( |
| 38 | !$command->getPermissionService()->currentUserCanRead(Entities::APPOINTMENTS) && |
| 39 | !$command->getPermissionService()->currentUserCanRead(Entities::EVENTS) |
| 40 | ) { |
| 41 | throw new AccessDeniedException('You are not allowed to read calendar events.'); |
| 42 | } |
| 43 | |
| 44 | /** @var AbstractUser $user */ |
| 45 | $user = $this->container->get('logged.in.user'); |
| 46 | |
| 47 | if ($user->getType() === Entities::CUSTOMER) { |
| 48 | $queryParams['customers'] = [$user->getId()->getValue()]; |
| 49 | } |
| 50 | |
| 51 | if ($user->getType() === Entities::PROVIDER) { |
| 52 | $queryParams['providers'] = [$user->getId()->getValue()]; |
| 53 | } |
| 54 | |
| 55 | $appointments = $this->getAppointments($queryParams); |
| 56 | $events = $this->getEvents($queryParams); |
| 57 | $sortedItems = array_merge($appointments, $events); |
| 58 | |
| 59 | usort( |
| 60 | $sortedItems, |
| 61 | function ($a, $b) { |
| 62 | $startA = $a instanceof Appointment ? $a->getBookingStart()->getValue() : $a['eventPeriod']->getPeriodStart()->getValue(); |
| 63 | $startB = $b instanceof Appointment ? $b->getBookingStart()->getValue() : $b['eventPeriod']->getPeriodStart()->getValue(); |
| 64 | return $startA <=> $startB; |
| 65 | } |
| 66 | ); |
| 67 | |
| 68 | $filledDays = []; |
| 69 | $maxNumberOfEvents = PHP_INT_MAX; |
| 70 | |
| 71 | if ($queryParams['view'] === 'dayGridMonth') { |
| 72 | $maxNumberOfEvents = 4; |
| 73 | } |
| 74 | |
| 75 | if (in_array($queryParams['view'], ['dayGridMonthSevenDays', 'dayGridMonthMobile'])) { |
| 76 | $maxNumberOfEvents = 2; |
| 77 | } |
| 78 | |
| 79 | foreach ($sortedItems as $item) { |
| 80 | $itemStartDate = $item instanceof Appointment |
| 81 | ? $item->getBookingStart()->getValue()->format('Y-m-d') |
| 82 | : $item['eventPeriod']->getPeriodStart()->getValue()->format('Y-m-d'); |
| 83 | |
| 84 | if (!isset($filledDays[$itemStartDate])) { |
| 85 | $filledDays[$itemStartDate] = ['events' => [], 'count' => 0, 'more' => 0]; |
| 86 | } |
| 87 | |
| 88 | // Add more button items number |
| 89 | if ($filledDays[$itemStartDate]['count'] >= $maxNumberOfEvents) { |
| 90 | $filledDays[$itemStartDate]['more']++; |
| 91 | $this->processEventDates($filledDays, $item, 'more'); |
| 92 | |
| 93 | continue; |
| 94 | } |
| 95 | |
| 96 | $filledDays[$itemStartDate]['events'][] = $item instanceof Appointment |
| 97 | ? $this->appointmentFormatter($item, $queryParams, $user) |
| 98 | : $this->eventFormatter($item['event'], $item['eventPeriod'], $queryParams); |
| 99 | $filledDays[$itemStartDate]['count']++; |
| 100 | $this->processEventDates($filledDays, $item, 'count'); |
| 101 | } |
| 102 | |
| 103 | $result->setData(['events' => $filledDays]); |
| 104 | return $result; |
| 105 | } |
| 106 | |
| 107 | /** |
| 108 | * @param array $filledDays |
| 109 | * @param array|Appointment $item |
| 110 | * @param string $counterKey |
| 111 | * @return void |
| 112 | */ |
| 113 | private function processEventDates(array &$filledDays, $item, string $counterKey): void |
| 114 | { |
| 115 | if (!$item instanceof Appointment) { |
| 116 | $eventStartDate = $item['eventPeriod']->getPeriodStart()->getValue()->setTime(0, 0, 0); |
| 117 | $eventEndDate = $item['eventPeriod']->getPeriodEnd()->getValue()->setTime(23, 59, 59); |
| 118 | |
| 119 | for ($date = (clone $eventStartDate)->modify('+1 day'); $date <= $eventEndDate; $date->modify('+1 day')) { |
| 120 | $formattedDate = $date->format('Y-m-d'); |
| 121 | if (!isset($filledDays[$formattedDate])) { |
| 122 | $filledDays[$formattedDate] = ['events' => [], 'count' => 0, 'more' => 0]; |
| 123 | } |
| 124 | $filledDays[$formattedDate][$counterKey]++; |
| 125 | } |
| 126 | } |
| 127 | } |
| 128 | |
| 129 | /** |
| 130 | * @throws QueryExecutionException |
| 131 | */ |
| 132 | private function getAppointments(array $queryParams): array |
| 133 | { |
| 134 | if (!isset($queryParams['entitiesToShow']) || !in_array('appointments', $queryParams['entitiesToShow']) || !empty($queryParams['events'])) { |
| 135 | return []; |
| 136 | } |
| 137 | |
| 138 | /** @var AppointmentRepository $appointmentRepository */ |
| 139 | $appointmentRepository = $this->container->get('domain.booking.appointment.repository'); |
| 140 | |
| 141 | $queryParams['statuses'] = |
| 142 | isset($queryParams['statuses']) && in_array('pendingAppointments', $queryParams['statuses']) |
| 143 | ? [BookingStatus::APPROVED, BookingStatus::PENDING] |
| 144 | : [BookingStatus::APPROVED]; |
| 145 | |
| 146 | $queryParams['dates'] = [$queryParams['calendarStartDate'], $queryParams['calendarEndDate']]; |
| 147 | |
| 148 | $appointments = $appointmentRepository->getFiltered($queryParams); |
| 149 | |
| 150 | return $appointments->getItems(); |
| 151 | } |
| 152 | |
| 153 | private function getEvents(array $queryParams): array |
| 154 | { |
| 155 | if (!isset($queryParams['entitiesToShow']) || !in_array('events', $queryParams['entitiesToShow']) || !empty($queryParams['services'])) { |
| 156 | return []; |
| 157 | } |
| 158 | |
| 159 | $eventPeriods = []; |
| 160 | |
| 161 | /** @var EventApplicationService $eventAS */ |
| 162 | $eventAS = $this->container->get('application.booking.event.service'); |
| 163 | |
| 164 | $queryParams['dates'] = [$queryParams['calendarStartDate'], $queryParams['calendarEndDate']]; |
| 165 | |
| 166 | if (!empty($queryParams['events'])) { |
| 167 | $queryParams['id'] = $queryParams['events']; |
| 168 | } |
| 169 | |
| 170 | $events = $eventAS->getEventsByCriteria($queryParams, ['fetchEventsPeriods' => true], -1); |
| 171 | |
| 172 | /** @var Event $event */ |
| 173 | foreach ($events->getItems() as $event) { |
| 174 | /** @var EventPeriod $eventPeriod */ |
| 175 | foreach ($event->getPeriods()->getItems() as $eventPeriod) { |
| 176 | $eventPeriods[] = ['event' => $event, 'eventPeriod' => $eventPeriod]; |
| 177 | } |
| 178 | } |
| 179 | |
| 180 | return $eventPeriods; |
| 181 | } |
| 182 | |
| 183 | private function appointmentFormatter(Appointment $appointment, array $queryParams, AbstractUser $user): array |
| 184 | { |
| 185 | /** @var SettingsService $settingsService */ |
| 186 | $settingsService = $this->container->get('domain.settings.service'); |
| 187 | $timeSlotStep = $settingsService->getSetting('general', 'timeSlotLength'); |
| 188 | $appointmentDurationInSeconds = $appointment->getBookingEnd()->getValue()->getTimestamp() - |
| 189 | $appointment->getBookingEnd()->getValue()->getTimestamp(); |
| 190 | $bufferTimeBefore = $appointment->getService() && $appointment->getService()->getTimeBefore() |
| 191 | ? $appointment->getService()->getTimeBefore()->getValue() |
| 192 | : 0; |
| 193 | $bufferTimeAfter = $appointment->getService() && $appointment->getService()->getTimeAfter() |
| 194 | ? $appointment->getService()->getTimeAfter()->getValue() |
| 195 | : 0; |
| 196 | |
| 197 | $startWithoutBuffer = $appointment->getBookingStart()->getValue(); |
| 198 | $start = (clone($startWithoutBuffer))->modify('-' . $bufferTimeBefore . 'seconds'); |
| 199 | |
| 200 | $endWithoutBuffer = $appointment->getBookingEnd()->getValue(); |
| 201 | $end = (clone($endWithoutBuffer))->modify($bufferTimeAfter . 'seconds'); |
| 202 | |
| 203 | $title = $appointment->getService()->getName()->getValue(); |
| 204 | if (!empty($queryParams['showEmployeeName'])) { |
| 205 | $title = $appointment->getProvider()->getFullName(); |
| 206 | } |
| 207 | |
| 208 | return [ |
| 209 | 'uuid' => $appointment->getId()->getValue(), |
| 210 | 'id' => $appointment->getId()->getValue(), |
| 211 | 'bookings' => $appointment->getBookings()->toArray(), |
| 212 | 'title' => $title, |
| 213 | 'start' => $start->format('Y-m-d H:i:s'), |
| 214 | 'end' => $end->format('Y-m-d H:i:s'), |
| 215 | 'startWithoutBuffer' => $startWithoutBuffer->format('Y-m-d H:i:s'), |
| 216 | 'endWithoutBuffer' => $endWithoutBuffer->format('Y-m-d H:i:s'), |
| 217 | 'mainColor' => $appointment->getService()->getColor()->getValue(), |
| 218 | 'numberOfSlots' => $appointmentDurationInSeconds / $timeSlotStep, |
| 219 | 'serviceId' => $appointment->getService()->getId()->getValue(), |
| 220 | 'employeeId' => $appointment->getProvider()->getId()->getValue(), |
| 221 | 'locationId' => $appointment->getLocationId() ?: null, |
| 222 | 'bufferTimeBefore' => $bufferTimeBefore, |
| 223 | 'bufferTimeAfter' => $bufferTimeAfter, |
| 224 | 'notes' => $appointment->getInternalNotes()->getValue(), |
| 225 | 'integrationCalendarType' => false, |
| 226 | 'type' => $appointment->getBookings()->length() === 1 |
| 227 | ? 'singleAppointment' |
| 228 | : 'groupAppointment', |
| 229 | 'editable' => $user->getType() === Entities::CUSTOMER |
| 230 | ? $settingsService->getSetting('roles', 'allowCustomerReschedule') |
| 231 | : ( |
| 232 | $user->getType() === Entities::PROVIDER ? |
| 233 | $settingsService->getSetting('roles', 'allowWriteAppointments') |
| 234 | : true |
| 235 | ), |
| 236 | ]; |
| 237 | } |
| 238 | |
| 239 | private function eventFormatter(Event $eventEntity, EventPeriod $eventPeriod, array $queryParams): array |
| 240 | { |
| 241 | $periodStartDate = clone $eventPeriod->getPeriodStart()->getValue(); |
| 242 | $periodEndDate = clone $eventPeriod->getPeriodEnd()->getValue(); |
| 243 | |
| 244 | $title = $eventEntity->getName()->getValue(); |
| 245 | if (!empty($queryParams['showEmployeeName'])) { |
| 246 | $title = $eventEntity->getOrganizer() ? $eventEntity->getOrganizer()->getFullName() : $title; |
| 247 | } |
| 248 | |
| 249 | $event = [ |
| 250 | 'uuid' => $eventEntity->getId()->getValue(), |
| 251 | 'title' => $title, |
| 252 | 'mainColor' => $eventEntity->getColor() ? $eventEntity->getColor()->getValue() : '#1788FB', |
| 253 | 'type' => 'event', |
| 254 | 'editable' => false, |
| 255 | 'startWithoutBuffer' => $periodStartDate->format('Y-m-d H:i:s'), |
| 256 | 'endWithoutBuffer' => $periodEndDate->format('Y-m-d H:i:s'), |
| 257 | 'notes' => '', |
| 258 | ]; |
| 259 | |
| 260 | if (in_array($queryParams['view'], ['dayGridMonthSevenDays', 'dayGridMonth', 'dayGridMonthMobile'])) { |
| 261 | $event['start'] = $periodStartDate->format('Y-m-d'); |
| 262 | $event['end'] = $periodEndDate->modify('+1 day')->format('Y-m-d'); |
| 263 | } else { |
| 264 | $event['groupId'] = $eventPeriod->getId()->getValue(); |
| 265 | $event['startRecur'] = $periodStartDate->format('Y-m-d'); |
| 266 | $event['endRecur'] = $periodEndDate->modify('+1 day')->format('Y-m-d'); |
| 267 | $event['startTime'] = $periodStartDate->format('H:i:s'); |
| 268 | $event['endTime'] = $periodEndDate->format('H:i:s') === '00:00:00' |
| 269 | ? '23:59:59' |
| 270 | : $periodEndDate->format('H:i:s'); |
| 271 | } |
| 272 | |
| 273 | return $event; |
| 274 | } |
| 275 | } |
| 276 |