PluginProbe ʕ •ᴥ•ʔ
Booking for Appointments and Events Calendar – Amelia / 2.0.2
Booking for Appointments and Events Calendar – Amelia v2.0.2
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 / GetAppointmentsCommandHandler.php
ameliabooking / src / Application / Commands / Booking / Appointment Last commit date
AddAppointmentCommand.php 1 year ago AddAppointmentCommandHandler.php 6 months ago AddBookingCommand.php 1 year ago AddBookingCommandHandler.php 1 year ago ApproveBookingRemotelyCommand.php 1 year ago ApproveBookingRemotelyCommandHandler.php 6 months ago CancelBookingCommand.php 1 year ago CancelBookingCommandHandler.php 6 months ago CancelBookingRemotelyCommand.php 1 year ago CancelBookingRemotelyCommandHandler.php 6 months ago DeleteAppointmentCommand.php 1 year ago DeleteAppointmentCommandHandler.php 1 year ago DeleteBookingCommand.php 1 year ago DeleteBookingCommandHandler.php 10 months ago DeleteBookingRemotelyCommand.php 10 months ago DeleteBookingRemotelyCommandHandler.php 6 months ago GetAppointmentBookingsCommand.php 6 months ago GetAppointmentBookingsCommandHandler.php 6 months ago GetAppointmentCommand.php 7 years ago GetAppointmentCommandHandler.php 5 months ago GetAppointmentsCommand.php 1 year ago GetAppointmentsCommandHandler.php 6 months ago GetIcsCommand.php 6 months ago GetIcsCommandHandler.php 4 years ago GetPackageAppointmentsCommand.php 1 year ago GetPackageAppointmentsCommandHandler.php 6 months ago GetTimeSlotsCommand.php 1 year ago GetTimeSlotsCommandHandler.php 10 months ago ReassignBookingCommand.php 1 year ago ReassignBookingCommandHandler.php 10 months ago RejectBookingRemotelyCommand.php 1 year ago RejectBookingRemotelyCommandHandler.php 6 months ago SuccessfulBookingCommand.php 1 year ago SuccessfulBookingCommandHandler.php 1 year ago UpdateAppointmentCommand.php 1 year ago UpdateAppointmentCommandHandler.php 6 months ago UpdateAppointmentStatusCommand.php 1 year ago UpdateAppointmentStatusCommandHandler.php 5 months ago UpdateAppointmentTimeCommand.php 1 year ago UpdateAppointmentTimeCommandHandler.php 10 months ago UpdateBookingStatusCommand.php 1 year ago UpdateBookingStatusCommandHandler.php 5 months ago
GetAppointmentsCommandHandler.php
492 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\Common\Exceptions\AccessDeniedException;
8 use AmeliaBooking\Application\Services\Bookable\AbstractPackageApplicationService;
9 use AmeliaBooking\Application\Services\Booking\AppointmentApplicationService;
10 use AmeliaBooking\Application\Services\Booking\BookingApplicationService;
11 use AmeliaBooking\Application\Services\User\UserApplicationService;
12 use AmeliaBooking\Domain\Collection\Collection;
13 use AmeliaBooking\Domain\Common\Exceptions\AuthorizationException;
14 use AmeliaBooking\Domain\Common\Exceptions\InvalidArgumentException;
15 use AmeliaBooking\Domain\Entity\Bookable\Service\Service;
16 use AmeliaBooking\Domain\Entity\Booking\Appointment\Appointment;
17 use AmeliaBooking\Domain\Entity\Booking\Appointment\CustomerBooking;
18 use AmeliaBooking\Domain\Entity\Entities;
19 use AmeliaBooking\Domain\Entity\User\AbstractUser;
20 use AmeliaBooking\Domain\Entity\User\Provider;
21 use AmeliaBooking\Domain\Services\DateTime\DateTimeService;
22 use AmeliaBooking\Domain\Services\Settings\SettingsService;
23 use AmeliaBooking\Domain\ValueObjects\String\BookingStatus;
24 use AmeliaBooking\Infrastructure\Common\Exceptions\QueryExecutionException;
25 use AmeliaBooking\Infrastructure\Repository\Bookable\Service\ServiceRepository;
26 use AmeliaBooking\Infrastructure\Repository\Booking\Appointment\AppointmentRepository;
27 use AmeliaBooking\Infrastructure\Repository\Booking\Appointment\CustomerBookingRepository;
28 use Interop\Container\Exception\ContainerException;
29
30 /**
31 * Class GetAppointmentsCommandHandler
32 *
33 * @package AmeliaBooking\Application\Commands\Booking\Appointment
34 */
35 class GetAppointmentsCommandHandler extends CommandHandler
36 {
37 /**
38 * @param GetAppointmentsCommand $command
39 *
40 * @return CommandResult
41 *
42 * @throws InvalidArgumentException
43 * @throws QueryExecutionException
44 * @throws AccessDeniedException
45 * @throws ContainerException
46 */
47 public function handle(GetAppointmentsCommand $command)
48 {
49 $result = new CommandResult();
50
51 /** @var AppointmentRepository $appointmentRepository */
52 $appointmentRepository = $this->container->get('domain.booking.appointment.repository');
53
54 /** @var ServiceRepository $serviceRepository */
55 $serviceRepository = $this->container->get('domain.bookable.service.repository');
56
57 /** @var AbstractPackageApplicationService $packageAS */
58 $packageAS = $this->container->get('application.bookable.package');
59
60 /** @var SettingsService $settingsDS */
61 $settingsDS = $this->container->get('domain.settings.service');
62
63 /** @var UserApplicationService $userAS */
64 $userAS = $this->container->get('application.user.service');
65
66 /** @var BookingApplicationService $bookingAS */
67 $bookingAS = $this->container->get('application.booking.booking.service');
68
69 /** @var AppointmentApplicationService $appointmentAS */
70 $appointmentAS = $this->container->get('application.booking.appointment.service');
71
72 $params = $command->getField('params');
73
74 $isCabinetPage = $command->getPage() === 'cabinet';
75
76 $isCalendarPage = $command->getPage() === 'calendar';
77
78 $isCabinetPackageRequest = $isCabinetPage && isset($params['activePackages']);
79
80 $isDashboardPackageRequest = !$isCabinetPage && (isset($params['packageId']) || !empty($params['packageBookings']));
81
82 try {
83 /** @var AbstractUser $user */
84 $user = $command->getUserApplicationService()->authorization($isCabinetPage ? $command->getToken() : null, $command->getCabinetType());
85 } catch (AuthorizationException $e) {
86 $result->setResult(CommandResult::RESULT_ERROR);
87 $result->setData(
88 [
89 'reauthorize' => true
90 ]
91 );
92
93 return $result;
94 }
95
96 $readOthers = $this->container->getPermissionsService()->currentUserCanReadOthers(Entities::APPOINTMENTS);
97
98 $providerCountParams = [];
99 if (
100 (!$readOthers) &&
101 $user && $user->getType() === Entities::PROVIDER
102 ) {
103 $providerCountParams['providerId'] = $user->getId()->getValue();
104 }
105
106 // TODO: Redesign - replace 'customerId' parameter with 'customers' on all /appointments calls and remove this part
107 if (!empty($params['customerId'])) {
108 $params['customers'] = $params['customerId'];
109 }
110
111 $customerCountParams = [];
112 if ($user && $user->getType() === Entities::CUSTOMER) {
113 $customerCountParams['customers'] = [$user->getId()->getValue()];
114 $params['customers'] = [$user->getId()->getValue()];
115 }
116
117 if (!empty($params['dates'])) {
118 !empty($params['dates'][0]) ? $params['dates'][0] .= ' 00:00:00' : null;
119 !empty($params['dates'][1]) ? $params['dates'][1] .= ' 23:59:59' : null;
120
121 if ($isCabinetPage && !empty($params['timeZone'])) {
122 foreach ([0, 1] as $index) {
123 if (!empty($params['dates'][$index])) {
124 $params['dates'][$index] = DateTimeService::getDateTimeObjectInTimeZone(
125 $params['dates'][$index],
126 $params['timeZone']
127 )->setTimezone(DateTimeService::getTimeZone())->format('Y-m-d H:i:s');
128 }
129 }
130 }
131 }
132
133 $entitiesIds = !empty($params['search']) && !$isCabinetPackageRequest ?
134 $appointmentAS->getAppointmentEntitiesIdsBySearchString($params['search']) : [];
135
136
137 $countParams = $params;
138
139 $appointmentsIds = [];
140
141 if (
142 !empty($params['search']) &&
143 !$entitiesIds['customers'] &&
144 !$entitiesIds['services'] &&
145 !$entitiesIds['providers']
146 ) {
147 $result->setResult(CommandResult::RESULT_SUCCESS);
148 $result->setMessage('Successfully retrieved appointments');
149 $result->setData(
150 [
151 Entities::APPOINTMENTS => [],
152 'availablePackageBookings' => [],
153 'occupied' => [],
154 'total' => 0,
155 'totalApproved' => 0,
156 'totalPending' => 0,
157 'totalCount' => $appointmentRepository->getPeriodAppointmentsCount(array_merge($providerCountParams, $customerCountParams)),
158 'filteredCount' => 0,
159 ]
160 );
161
162 return $result;
163 }
164
165 $availablePackageBookings = [];
166
167 if (!$isCabinetPackageRequest && !$isDashboardPackageRequest) {
168 /** @var Collection $periodAppointments */
169 $periodAppointments = $appointmentRepository->getPeriodAppointments(
170 array_merge(
171 [
172 'customers' => $isCabinetPage && $user->getType() === Entities::CUSTOMER ?
173 [$user->getId()->getValue()] : [],
174 'providers' => $user && $user->getType() === Entities::PROVIDER ?
175 [$user->getId()->getValue()] : [],
176 ],
177 array_merge($params, ['endsInDateRange' => $isCalendarPage]),
178 $entitiesIds,
179 ['skipBookings' => !$isCabinetPage && empty($params['customers']) && empty($entitiesIds['customers'])]
180 ),
181 $settingsDS->getSetting('general', 'itemsPerPage')
182 );
183
184 /** @var Appointment $appointment */
185 foreach ($periodAppointments->getItems() as $appointment) {
186 $appointmentsIds[] = $appointment->getId()->getValue();
187 }
188 }
189
190 /** @var Collection $appointments */
191 $appointments = new Collection();
192
193 $customersNoShowCountIds = [];
194
195 $noShowTagEnabled = $settingsDS->isFeatureEnabled('noShowTag');
196
197 if (!$isCabinetPackageRequest && $appointmentsIds) {
198 $appointments = $appointmentRepository->getFiltered(
199 [
200 'ids' => $appointmentsIds,
201 'skipServices' => isset($params['skipServices']) ? $params['skipServices'] : false,
202 'skipProviders' => isset($params['skipProviders']) ? $params['skipProviders'] : false,
203 'joinPackages' => $isCabinetPage
204 ]
205 );
206 } elseif ($isDashboardPackageRequest || ($user && $user->getId() && $isCabinetPackageRequest)) {
207 $availablePackageBookings = $packageAS->getPackageAvailability(
208 $appointments,
209 [
210 'purchased' => !empty($params['dates']) ? $params['dates'] : [],
211 'customers' => $isCabinetPackageRequest ? [$user->getId()->getValue()] : (!empty($params['customers']) ? $params['customers'] : null),
212 'packageId' => !$isCabinetPackageRequest && !empty($params['packageId']) ?
213 (int)$params['packageId'] : null,
214 ]
215 );
216
217 if ($noShowTagEnabled && !!$availablePackageBookings) {
218 $customersNoShowCountIds[] = $availablePackageBookings[0]['customerId'];
219 }
220 }
221
222 /** @var Collection $services */
223 $services = $serviceRepository->getAllArrayIndexedById();
224
225 if (!$isCabinetPage) {
226 $packageAS->setPackageBookingsForAppointments($appointments);
227 }
228
229 $occupiedTimes = [];
230
231 $currentDateTime = DateTimeService::getNowDateTimeObject();
232
233 $groupedAppointments = [];
234
235 /** @var Appointment $appointment */
236 foreach ($appointments->getItems() as $appointment) {
237 /** @var Service $service */
238 $service = $services->getItem($appointment->getServiceId()->getValue());
239
240 $bookingsCount = 0;
241
242 /** @var CustomerBooking $booking */
243 foreach ($appointment->getBookings()->getItems() as $booking) {
244 // fix for wrongly saved JSON
245 if (
246 $booking->getCustomFields() &&
247 json_decode($booking->getCustomFields()->getValue(), true) === null
248 ) {
249 $booking->setCustomFields(null);
250 }
251
252 if ($bookingAS->isBookingApprovedOrPending($booking->getStatus()->getValue())) {
253 $bookingsCount++;
254 }
255
256 if ($noShowTagEnabled && !in_array($booking->getCustomerId()->getValue(), $customersNoShowCountIds)) {
257 $customersNoShowCountIds[] = $booking->getCustomerId()->getValue();
258 }
259 }
260
261 $providerId = $appointment->getProviderId()->getValue();
262
263 $isGroup = false;
264
265 // skip appointments/bookings for other customers if user is customer, and remember that time/date values
266 if ($userAS->isCustomer($user)) {
267 /** @var CustomerBooking $booking */
268 foreach ($appointment->getBookings()->getItems() as $bookingKey => $booking) {
269 if (!$user->getId() || $booking->getCustomerId()->getValue() !== $user->getId()->getValue()) {
270 /** @var CustomerBooking $otherBooking */
271 $otherBooking = $appointment->getBookings()->getItem($bookingKey);
272
273 if (
274 $otherBooking->getStatus()->getValue() === BookingStatus::APPROVED ||
275 $otherBooking->getStatus()->getValue() === BookingStatus::PENDING
276 ) {
277 $isGroup = true;
278 }
279
280 $appointment->getBookings()->deleteItem($bookingKey);
281 }
282 }
283
284 if ($appointment->getBookings()->length() === 0) {
285 $serviceTimeBefore = $service->getTimeBefore() ?
286 $service->getTimeBefore()->getValue() : 0;
287
288 $serviceTimeAfter = $service->getTimeAfter() ?
289 $service->getTimeAfter()->getValue() : 0;
290
291 $occupiedTimeStart = DateTimeService::getCustomDateTimeObject(
292 $appointment->getBookingStart()->getValue()->format('Y-m-d H:i:s')
293 )->modify('-' . $serviceTimeBefore . ' second')->format('H:i:s');
294
295 $occupiedTimeEnd = DateTimeService::getCustomDateTimeObject(
296 $appointment->getBookingEnd()->getValue()->format('Y-m-d H:i:s')
297 )->modify('+' . $serviceTimeAfter . ' second')->format('H:i:s');
298
299 $occupiedTimes[$appointment->getBookingStart()->getValue()->format('Y-m-d')][] =
300 [
301 'employeeId' => $providerId,
302 'startTime' => $occupiedTimeStart,
303 'endTime' => $occupiedTimeEnd,
304 ];
305
306 continue;
307 }
308 }
309
310 // skip appointments for other providers if user is provider
311 if (
312 (!$readOthers || $isCabinetPage) &&
313 $user->getType() === Entities::PROVIDER &&
314 $user->getId()->getValue() !== $providerId
315 ) {
316 continue;
317 }
318
319 $appointmentAS->calculateAndSetAppointmentEnd($appointment, $service);
320
321 $minimumCancelTimeInSeconds = $settingsDS
322 ->getEntitySettings($service->getSettings())
323 ->getGeneralSettings()
324 ->getMinimumTimeRequirementPriorToCanceling();
325
326 $minimumCancelTime = DateTimeService::getCustomDateTimeObject(
327 $appointment->getBookingStart()->getValue()->format('Y-m-d H:i:s')
328 )->modify("-{$minimumCancelTimeInSeconds} seconds");
329
330 $date = $appointment->getBookingStart()->getValue()->format('Y-m-d');
331
332 $cancelable = $currentDateTime <= $minimumCancelTime;
333
334 $minimumRescheduleTimeInSeconds = $settingsDS
335 ->getEntitySettings($service->getSettings())
336 ->getGeneralSettings()
337 ->getMinimumTimeRequirementPriorToRescheduling();
338
339 $minimumRescheduleTime = DateTimeService::getCustomDateTimeObject(
340 $appointment->getBookingStart()->getValue()->format('Y-m-d H:i:s')
341 )->modify("-{$minimumRescheduleTimeInSeconds} seconds");
342
343 $reschedulable = $currentDateTime <= $minimumRescheduleTime;
344
345
346 if ($isCabinetPage || ($user && $user->getType() === Entities::PROVIDER)) {
347 $timeZone = 'UTC';
348
349 if (!empty($params['timeZone'])) {
350 $timeZone = $params['timeZone'];
351 } elseif (
352 ($user instanceof Provider) &&
353 empty($user->getTimeZone()) && !empty(get_option('timezone_string'))
354 ) {
355 $timeZone = get_option('timezone_string');
356 }
357
358 $appointment->getBookingStart()->getValue()->setTimezone(new \DateTimeZone($timeZone));
359 $appointment->getBookingEnd()->getValue()->setTimezone(new \DateTimeZone($timeZone));
360
361 $date = $appointment->getBookingStart()->getValue()->format('Y-m-d');
362
363 foreach ($availablePackageBookings as &$packageCustomerPurchase) {
364 foreach ($packageCustomerPurchase['packages'] as &$packagePurchase) {
365 foreach ($packagePurchase['services'] as &$packageService) {
366 foreach ($packageService['bookings'] as &$purchase) {
367 $purchasedDate = DateTimeService::getCustomDateTimeObjectInTimeZone(
368 $purchase['purchased'],
369 $timeZone
370 );
371
372 $purchase['purchased'] = $purchasedDate->format('Y-m-d H:i');
373 }
374 }
375 }
376 }
377 }
378
379 $groupedAppointments[$date]['date'] = $date;
380
381 $groupedAppointments[$date]['appointments'][] = array_merge(
382 $appointment->toArray(),
383 [
384 'cancelable' => $cancelable,
385 'reschedulable' => $reschedulable,
386 'past' => $currentDateTime >= $appointment->getBookingStart()->getValue(),
387 'isGroup' => $isGroup,
388 ]
389 );
390 }
391
392 $emptyBookedPackages = null;
393
394 if (
395 !empty($params['packageId']) &&
396 empty($params['services']) &&
397 empty($params['providers']) &&
398 empty($params['locations'])
399 ) {
400 /** @var AbstractPackageApplicationService $packageApplicationService */
401 $packageApplicationService = $this->container->get('application.bookable.package');
402
403 /** @var Collection $emptyBookedPackages */
404 $emptyBookedPackages = $packageApplicationService->getEmptyPackages(
405 [
406 'packages' => [$params['packageId']],
407 'purchased' => !empty($params['dates']) ? $params['dates'] : [],
408 'customers' => !empty($params['customers']) ? $params['customers'] : null,
409 ]
410 );
411 }
412
413 $periodsAppointmentsCount = 0;
414
415 $periodsAppointmentsApprovedCount = 0;
416
417 $periodsAppointmentsPendingCount = 0;
418
419 $periodsAppointmentsTotalCount = 0;
420
421 if (!empty($countParams['page']) || (!$isCabinetPackageRequest && !$isCabinetPage && !$isCalendarPage)) {
422 if (
423 (!$readOthers) &&
424 $user->getType() === Entities::PROVIDER
425 ) {
426 $countParams['providerId'] = $user->getId()->getValue();
427 }
428 $periodsAppointmentsCount = $appointmentRepository->getPeriodAppointmentsCount(
429 array_merge($countParams, $providerCountParams, $entitiesIds)
430 );
431
432 $periodsAppointmentsTotalCount = $appointmentRepository->getPeriodAppointmentsCount(array_merge($providerCountParams, $customerCountParams));
433
434 $periodsAppointmentsApprovedCount = $appointmentRepository->getPeriodAppointmentsCount(
435 array_merge(
436 $countParams,
437 $providerCountParams,
438 ['status' => BookingStatus::APPROVED],
439 $entitiesIds
440 )
441 );
442
443 $periodsAppointmentsPendingCount = $appointmentRepository->getPeriodAppointmentsCount(
444 array_merge(
445 $countParams,
446 $providerCountParams,
447 ['status' => BookingStatus::PENDING],
448 $entitiesIds
449 )
450 );
451 }
452
453 $customersNoShowCount = [];
454
455 if ($noShowTagEnabled && $customersNoShowCountIds) {
456 /** @var CustomerBookingRepository $bookingRepository */
457 $bookingRepository = $this->container->get('domain.booking.customerBooking.repository');
458
459 $customersNoShowCount = $bookingRepository->countByNoShowStatus($customersNoShowCountIds);
460 }
461
462 $groupedAppointments = apply_filters('amelia_get_appointments_filter', $groupedAppointments);
463
464 do_action('amelia_get_appointments', $groupedAppointments);
465
466
467 // TODO: Redesign - remove total, totalApproved, totalPending
468 $result->setResult(CommandResult::RESULT_SUCCESS);
469 $result->setMessage('Successfully retrieved appointments');
470 $result->setData(
471 [
472 Entities::APPOINTMENTS =>
473 !empty($params['asArray']) && filter_var($params['asArray'], FILTER_VALIDATE_BOOLEAN) ?
474 $appointments->toArray() :
475 $groupedAppointments,
476 'availablePackageBookings' => $availablePackageBookings,
477 'emptyPackageBookings' => !empty($emptyBookedPackages) ? $emptyBookedPackages->toArray() : [],
478 'occupied' => $occupiedTimes,
479 'total' => $periodsAppointmentsCount,
480 'totalApproved' => $periodsAppointmentsApprovedCount,
481 'totalPending' => $periodsAppointmentsPendingCount,
482 'totalCount' => $periodsAppointmentsTotalCount,
483 'filteredCount' => $periodsAppointmentsCount,
484 'currentUser' => $user ? $user->toArray() : null,
485 'customersNoShowCount' => $customersNoShowCount ? array_values($customersNoShowCount) : []
486 ]
487 );
488
489 return $result;
490 }
491 }
492