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