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