PluginProbe ʕ •ᴥ•ʔ
Booking for Appointments and Events Calendar – Amelia / 1.2.4
Booking for Appointments and Events Calendar – Amelia v1.2.4
2.4.9 2.4.8 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 / Services / Notification / AbstractNotificationService.php
ameliabooking / src / Application / Services / Notification Last commit date
AbstractNotificationService.php 1 year ago AbstractWhatsAppNotificationService.php 1 year ago BasicWhatsAppNotificationService.php 1 year ago EmailNotificationService.php 1 year ago NotificationHelperService.php 1 year ago SMSAPIService.php 1 year ago SMSNotificationService.php 1 year ago
AbstractNotificationService.php
1305 lines
1 <?php
2 /**
3 * @copyright © TMS-Plugins. All rights reserved.
4 * @licence See LICENCE.md for license details.
5 */
6
7 namespace AmeliaBooking\Application\Services\Notification;
8
9 use AmeliaBooking\Application\Services\Booking\BookingApplicationService;
10 use AmeliaBooking\Application\Services\Booking\EventApplicationService;
11 use AmeliaBooking\Application\Services\Payment\PaymentApplicationService;
12 use AmeliaBooking\Domain\Collection\Collection;
13 use AmeliaBooking\Domain\Common\Exceptions\InvalidArgumentException;
14 use AmeliaBooking\Domain\Entity\Booking\Appointment\CustomerBooking;
15 use AmeliaBooking\Domain\Entity\Booking\Event\Event;
16 use AmeliaBooking\Domain\Entity\Entities;
17 use AmeliaBooking\Domain\Entity\Notification\Notification;
18 use AmeliaBooking\Domain\Factory\Booking\Appointment\CustomerBookingFactory;
19 use AmeliaBooking\Domain\Services\DateTime\DateTimeService;
20 use AmeliaBooking\Domain\Services\Settings\SettingsService;
21 use AmeliaBooking\Domain\ValueObjects\String\BookingStatus;
22 use AmeliaBooking\Domain\ValueObjects\String\NotificationSendTo;
23 use AmeliaBooking\Domain\ValueObjects\String\NotificationStatus;
24 use AmeliaBooking\Infrastructure\Common\Container;
25 use AmeliaBooking\Infrastructure\Common\Exceptions\NotFoundException;
26 use AmeliaBooking\Infrastructure\Common\Exceptions\QueryExecutionException;
27 use AmeliaBooking\Infrastructure\Repository\Notification\NotificationLogRepository;
28 use AmeliaBooking\Infrastructure\Repository\Notification\NotificationRepository;
29 use AmeliaBooking\Infrastructure\Repository\Notification\NotificationsToEntitiesRepository;
30 use AmeliaBooking\Infrastructure\Repository\User\ProviderRepository;
31 use Exception;
32 use Interop\Container\Exception\ContainerException;
33 use Slim\Exception\ContainerValueNotFoundException;
34
35 /**
36 * Class AbstractNotificationService
37 *
38 * @package AmeliaBooking\Application\Services\Notification
39 */
40 abstract class AbstractNotificationService
41 {
42 /** @var Container */
43 protected $container;
44
45 /** @var string */
46 protected $type;
47
48 /** @var array */
49 protected $sendNotifications = true;
50
51 /** @var array */
52 protected $preparedNotificationData = [];
53
54 /**
55 * AbstractNotificationService constructor.
56 *
57 * @param Container $container
58 * @param string $type
59 */
60 public function __construct(Container $container, $type)
61 {
62 $this->container = $container;
63
64 $this->type = $type;
65 }
66
67 /**
68 * @param bool $value
69 */
70 public function setSend($value)
71 {
72 $this->sendNotifications = $value;
73 }
74
75 /**
76 * @return bool
77 */
78 public function getSend()
79 {
80 return $this->sendNotifications;
81 }
82
83 /**
84 * @return array
85 */
86 protected function getPreparedNotificationData()
87 {
88 return $this->preparedNotificationData;
89 }
90
91 /**
92 * @param array $data
93 */
94 protected function addPreparedNotificationData($data)
95 {
96 $this->preparedNotificationData[] = $data;
97 }
98
99 /**
100 * @return void
101 */
102 abstract public function sendPreparedNotifications();
103
104 /**
105 * @param array $appointmentArray
106 * @param Notification $notification
107 * @param bool $logNotification
108 * @param null $bookingKey
109 *
110 * @return mixed
111 */
112 abstract public function sendNotification(
113 $appointmentArray,
114 $notification,
115 $logNotification,
116 $bookingKey = null,
117 $allBookings = null
118 );
119
120
121 /**
122 * @throws NotFoundException
123 * @throws QueryExecutionException
124 * @throws InvalidArgumentException
125 * @throws ContainerException
126 * @throws Exception
127 */
128 abstract public function sendBirthdayGreetingNotifications();
129
130 /**
131 *
132 * @param string $name
133 * @param string $type
134 *
135 * @return Collection
136 *
137 * @throws QueryExecutionException
138 * @throws InvalidArgumentException
139 */
140 protected function getByNameAndType($name, $type)
141 {
142 /** @var NotificationRepository $notificationRepo */
143 $notificationRepo = $this->container->get('domain.notification.repository');
144 /** @var NotificationsToEntitiesRepository $notificationEntitiesRepo */
145 $notificationEntitiesRepo = $this->container->get('domain.notificationEntities.repository');
146
147 /** @var Collection $notifications */
148 $notifications = $notificationRepo->getByNameAndType($name, $type);
149 /** @var Notification $notification */
150 foreach ($notifications->getItems() as $notification) {
151 if ($notification->getCustomName() !== null) {
152 $notification->setEntityIds($notificationEntitiesRepo->getEntities($notification->getId()->getValue()));
153 }
154 }
155
156 return $notifications;
157 }
158
159 /**
160 *
161 * @param int $id
162 *
163 * @return Notification
164 *
165 * @throws QueryExecutionException
166 * @throws NotFoundException
167 */
168 public function getById($id)
169 {
170 /** @var NotificationRepository $notificationRepo */
171 $notificationRepo = $this->container->get('domain.notification.repository');
172
173 return $notificationRepo->getById($id);
174 }
175
176 /**
177 * @param array $appointmentArray
178 * @param bool $forcedStatusChange - True when appointment status is changed to 'pending' because minimum capacity
179 * condition is not satisfied
180 * @param bool $logNotification
181 * @param bool $isBackend
182 *
183 * @throws ContainerValueNotFoundException
184 * @throws QueryExecutionException
185 * @throws InvalidArgumentException
186 */
187 public function sendAppointmentStatusNotifications($appointmentArray, $forcedStatusChange, $logNotification, $isBackend = false)
188 {
189 /** @var BookingApplicationService $bookingAS */
190 $bookingAS = $this->container->get('application.booking.booking.service');
191
192 // Notify provider
193 /** @var Collection $providerNotifications */
194 $providerNotifications = $this->getByNameAndType(
195 "provider_{$appointmentArray['type']}_{$appointmentArray['status']}",
196 $this->type
197 );
198
199 $sendDefault = $this->sendDefault($providerNotifications, $appointmentArray);
200
201 $appointmentArray['sendCF'] = true;
202
203 $dontSend = $appointmentArray['type'] === Entities::EVENT && $appointmentArray['status'] === BookingStatus::REJECTED
204 && DateTimeService::getNowDateTimeObject() > DateTimeService::getCustomDateTimeObject($appointmentArray['periods'][count($appointmentArray['periods']) - 1]['periodStart']);
205
206 /** @var Notification $providerNotification */
207 foreach ($providerNotifications->getItems() as $providerNotification) {
208 if ($providerNotification && $providerNotification->getStatus()->getValue() === NotificationStatus::ENABLED && !$dontSend) {
209 if (!$this->checkCustom($providerNotification, $appointmentArray, $sendDefault)) {
210 continue;
211 }
212 $this->sendNotification(
213 $appointmentArray,
214 $providerNotification,
215 $logNotification
216 );
217 }
218 }
219
220 // Notify customers
221 if ($appointmentArray['notifyParticipants']) {
222
223 /** @var Collection $customerNotifications */
224 $customerNotifications = $this->getByNameAndType(
225 "customer_{$appointmentArray['type']}_{$appointmentArray['status']}",
226 $this->type
227 );
228
229 $sendDefault = $this->sendDefault($customerNotifications, $appointmentArray);
230
231 foreach ($customerNotifications->getItems() as $customerNotification) {
232 if ($customerNotification->getStatus()->getValue() === NotificationStatus::ENABLED && !$dontSend) {
233 if (!$this->checkCustom($customerNotification, $appointmentArray, $sendDefault)) {
234 continue;
235 }
236 // If appointment status is changed to 'pending' because minimum capacity condition is not satisfied,
237 // return all 'approved' bookings and send them notification that appointment is now 'pending'.
238 if ($forcedStatusChange === true) {
239 $appointmentArray['bookings'] = $bookingAS->filterApprovedBookings($appointmentArray['bookings']);
240 }
241
242 $appointmentArray['isBackend'] = $isBackend;
243 // Notify each customer from customer bookings
244 foreach (array_keys($appointmentArray['bookings']) as $bookingKey) {
245 if (!$appointmentArray['bookings'][$bookingKey]['isChangedStatus'] ||
246 (
247 isset($appointmentArray['bookings'][$bookingKey]['skipNotification']) &&
248 $appointmentArray['bookings'][$bookingKey]['skipNotification']
249 )
250 ) {
251 continue;
252 }
253
254 $this->sendNotification(
255 $appointmentArray,
256 $customerNotification,
257 $logNotification,
258 $bookingKey
259 );
260 }
261 }
262 }
263 }
264 }
265
266 /**
267 * @param array $appointmentArray
268 * @param array $bookingsArray
269 * @param bool $forcedStatusChange
270 *
271 * @throws QueryExecutionException
272 * @throws InvalidArgumentException
273 */
274 public function sendAppointmentEditedNotifications($appointmentArray, $bookingsArray, $forcedStatusChange)
275 {
276 /** @var BookingApplicationService $bookingAS */
277 $bookingAS = $this->container->get('application.booking.booking.service');
278
279 // Notify customers
280 if ($appointmentArray['notifyParticipants']) {
281 // If appointment status is 'pending', remove all 'approved' bookings because they can't receive
282 // notification that booking is 'approved' until appointment status is changed to 'approved'
283 if ($appointmentArray['status'] === 'pending') {
284 $bookingsArray = $bookingAS->removeBookingsByStatuses($bookingsArray, ['approved']);
285 }
286
287 // If appointment status is changed, because minimum capacity condition is satisfied or not,
288 // remove all 'approved' bookings because notification is already sent to them.
289 if ($forcedStatusChange === true) {
290 $bookingsArray = $bookingAS->removeBookingsByStatuses($bookingsArray, ['approved']);
291 }
292
293 if (!$appointmentArray['employee_changed']) {
294 $appointmentArray['bookings'] = $bookingsArray;
295 }
296
297 foreach (array_keys($appointmentArray['bookings']) as $bookingKey) {
298 /** @var Collection $customerNotifications */
299 $customerNotifications =
300 $this->getByNameAndType(
301 "customer_appointment_{$appointmentArray['bookings'][$bookingKey]['status']}",
302 $this->type
303 );
304
305 $sendDefault = $this->sendDefault($customerNotifications, $appointmentArray);
306 foreach ($customerNotifications->getItems() as $customerNotification) {
307 if ($customerNotification->getStatus()->getValue() === NotificationStatus::ENABLED) {
308 if (!$this->checkCustom($customerNotification, $appointmentArray, $sendDefault)) {
309 continue;
310 }
311 if ((
312 !$appointmentArray['bookings'][$bookingKey]['isChangedStatus'] &&
313 !$appointmentArray['employee_changed']
314 ) || (
315 isset($appointmentArray['bookings'][$bookingKey]['skipNotification']) &&
316 $appointmentArray['bookings'][$bookingKey]['skipNotification']
317 )
318 ) {
319 continue;
320 }
321
322 if (!$appointmentArray['employee_changed']) {
323 $this->sendNotification(
324 $appointmentArray,
325 $customerNotification,
326 true,
327 $bookingKey
328 );
329 }
330 }
331 }
332 }
333 }
334 if ($appointmentArray['employee_changed']) {
335 // Notify provider
336 /** @var Collection $providerNotifications */
337 $providerNotifications = $this->getByNameAndType(
338 "provider_{$appointmentArray['type']}_{$appointmentArray['status']}",
339 $this->type
340 );
341
342 $sendDefault = $this->sendDefault($providerNotifications, $appointmentArray);
343
344 foreach ($providerNotifications->getItems() as $providerNotification) {
345 if ($providerNotification->getStatus()->getValue() === NotificationStatus::ENABLED) {
346 if (!$this->checkCustom($providerNotification, $appointmentArray, $sendDefault)) {
347 continue;
348 }
349 $this->sendNotification(
350 $appointmentArray,
351 $providerNotification,
352 true
353 );
354 }
355 }
356 }
357 }
358
359 /**
360 * @param $appointmentArray
361 *
362 * @throws QueryExecutionException
363 * @throws InvalidArgumentException
364 */
365 public function sendAppointmentRescheduleNotifications($appointmentArray)
366 {
367 // Notify customers
368 if ($appointmentArray['notifyParticipants']) {
369
370 /** @var Collection $customerNotifications */
371 $customerNotifications = $this->getByNameAndType(
372 "customer_{$appointmentArray['type']}_rescheduled",
373 $this->type
374 );
375
376 $sendDefault = $this->sendDefault($customerNotifications, $appointmentArray);
377 foreach ($customerNotifications->getItems() as $customerNotification) {
378 if ($customerNotification->getStatus()->getValue() === NotificationStatus::ENABLED) {
379 if (!$this->checkCustom($customerNotification, $appointmentArray, $sendDefault)) {
380 continue;
381 }
382 // Notify each customer from customer bookings
383 foreach (array_keys($appointmentArray['bookings']) as $bookingKey) {
384 $this->sendNotification(
385 $appointmentArray,
386 $customerNotification,
387 true,
388 $bookingKey
389 );
390 }
391 }
392 }
393 }
394
395 if (empty($appointmentArray['employee_changed'])) {
396 // Notify provider
397 /** @var Collection $providerNotifications */
398 $providerNotifications = $this->getByNameAndType(
399 "provider_{$appointmentArray['type']}_rescheduled",
400 $this->type
401 );
402
403 $sendDefault = $this->sendDefault($providerNotifications, $appointmentArray);
404 foreach ($providerNotifications->getItems() as $providerNotification) {
405 if ($providerNotification->getStatus()->getValue() === NotificationStatus::ENABLED) {
406 if (!$this->checkCustom($providerNotification, $appointmentArray, $sendDefault)) {
407 continue;
408 }
409 $this->sendNotification(
410 $appointmentArray,
411 $providerNotification,
412 true
413 );
414 }
415 }
416 }
417 }
418
419 /**
420 * @param $appointmentArray
421 * @param $appointmentRescheduled
422 *
423 * @throws QueryExecutionException
424 * @throws InvalidArgumentException
425 */
426 public function sendAppointmentUpdatedNotifications($appointmentArray, $appointmentRescheduled = null)
427 {
428 // Notify customers
429 if ($appointmentArray['notifyParticipants'] && !$appointmentRescheduled) {
430
431 /** @var Collection $customerNotifications */
432 $customerNotifications = $this->getByNameAndType(
433 "customer_{$appointmentArray['type']}_updated",
434 $this->type
435 );
436
437 $sendDefault = $this->sendDefault($customerNotifications, $appointmentArray);
438 foreach ($customerNotifications->getItems() as $customerNotification) {
439 if ($customerNotification->getStatus()->getValue() === NotificationStatus::ENABLED) {
440 if (!$this->checkCustom($customerNotification, $appointmentArray, $sendDefault)) {
441 continue;
442 }
443 // Notify each customer from customer bookings
444 foreach (array_keys($appointmentArray['bookings']) as $bookingKey) {
445 if ($appointmentArray['bookings'][$bookingKey]['status'] === BookingStatus::APPROVED && $appointmentArray['status'] === BookingStatus::APPROVED &&
446 ($appointmentArray['bookings'][$bookingKey]['isUpdated'] || $appointmentArray['type'] === Entities::EVENT)) {
447 $this->sendNotification(
448 $appointmentArray,
449 $customerNotification,
450 true,
451 $bookingKey
452 );
453 }
454 }
455 }
456 }
457 }
458
459 if (!empty($appointmentArray['employee_changed'])) {
460 $appointmentArray['providerId'] = $appointmentArray['employee_changed'];
461 }
462
463 if ($appointmentArray['status'] === BookingStatus::APPROVED) {
464 /** @var Collection $providerNotifications */
465 $providerNotifications = $this->getByNameAndType(
466 "provider_{$appointmentArray['type']}_updated",
467 $this->type
468 );
469
470 $sendDefault = $this->sendDefault($providerNotifications, $appointmentArray);
471 foreach ($providerNotifications->getItems() as $providerNotification) {
472 if ($providerNotification->getStatus()->getValue() === NotificationStatus::ENABLED) {
473 if (!$this->checkCustom($providerNotification, $appointmentArray, $sendDefault)) {
474 continue;
475 }
476 $this->sendNotification(
477 $appointmentArray,
478 $providerNotification,
479 true
480 );
481 }
482 }
483 }
484 }
485
486 /**
487 * @param array $appointmentArray
488 * @param array $bookingArray
489 * @param bool $logNotification
490 *
491 * @throws QueryExecutionException
492 * @throws InvalidArgumentException
493 */
494 public function sendBookingAddedNotifications($appointmentArray, $bookingArray, $logNotification)
495 {
496
497 /** @var SettingsService $settingsService */
498 $settingsService = $this->container->get('domain.settings.service');
499
500 $defaultStatus = $appointmentArray['status'];
501
502 if ($appointmentArray['type'] !== Entities::EVENT && $defaultStatus === BookingStatus::APPROVED) {
503
504 /** @var ServiceRepository $serviceRepository */
505 $serviceRepository = $this->container->get('domain.bookable.service.repository');
506
507 $service = $serviceRepository->getById($appointmentArray['serviceId']);
508
509 $defaultStatus = ($service->getSettings() && !empty(json_decode($service->getSettings()->getValue(), true)['general']['defaultAppointmentStatus'])) ?
510 json_decode($service->getSettings()->getValue(), true)['general']['defaultAppointmentStatus'] :
511 $settingsService->getSetting('general', 'defaultAppointmentStatus');
512 }
513
514 $customerNotifications = $this->getByNameAndType(
515 "customer_{$appointmentArray['type']}_{$defaultStatus}",
516 $this->type
517 );
518
519 $sendDefault = $this->sendDefault($customerNotifications, $appointmentArray);
520
521 foreach ($customerNotifications->getItems() as $customerNotification) {
522 if ($customerNotification->getStatus()->getValue() === NotificationStatus::ENABLED) {
523 if (!$this->checkCustom($customerNotification, $appointmentArray, $sendDefault)) {
524 continue;
525 }
526
527 // Notify customer that scheduled the appointment
528 $this->sendNotification(
529 $appointmentArray,
530 $customerNotification,
531 $logNotification,
532 array_search($bookingArray['id'], array_column($appointmentArray['bookings'], 'id'), true)
533 );
534 }
535 }
536
537 // Notify provider
538 $providerNotifications = $this->getByNameAndType(
539 "provider_{$appointmentArray['type']}_{$appointmentArray['status']}",
540 $this->type
541 );
542
543 $sendDefault = $this->sendDefault($providerNotifications, $appointmentArray);
544 foreach ($providerNotifications->getItems() as $providerNotification) {
545 if ($providerNotification && $providerNotification->getStatus()->getValue() === NotificationStatus::ENABLED) {
546 if (!$this->checkCustom($providerNotification, $appointmentArray, $sendDefault)) {
547 continue;
548 }
549 $allBookings = null;
550 if ($appointmentArray['type'] === Entities::EVENT) {
551 $allBookings = $appointmentArray['bookings'];
552 $appointmentArray['bookings'] = [$bookingArray];
553 }
554 $this->sendNotification(
555 $appointmentArray,
556 $providerNotification,
557 $logNotification,
558 null,
559 $allBookings
560 );
561 }
562 }
563 }
564
565 /**
566 * Notify the customer when he changes his booking status.
567 *
568 * @param $appointmentArray
569 * @param $bookingArray
570 *
571 * @throws QueryExecutionException
572 * @throws InvalidArgumentException
573 */
574 public function sendCustomerBookingNotification($appointmentArray, $bookingArray)
575 {
576 // Notify customers
577 if ($appointmentArray['notifyParticipants']) {
578 $customerNotifications = $this->getByNameAndType("customer_{$appointmentArray['type']}_{$bookingArray['status']}", $this->type);
579
580 $sendDefault = $this->sendDefault($customerNotifications, $appointmentArray);
581 foreach ($customerNotifications->getItems() as $customerNotification) {
582 if ($customerNotification->getStatus()->getValue() === NotificationStatus::ENABLED) {
583 if (!$this->checkCustom($customerNotification, $appointmentArray, $sendDefault)) {
584 continue;
585 }
586 // Notify customer
587 $bookingKey = array_search(
588 $bookingArray['id'],
589 array_column($appointmentArray['bookings'], 'id'),
590 true
591 );
592
593 $this->sendNotification(
594 $appointmentArray,
595 $customerNotification,
596 true,
597 $bookingKey
598 );
599 }
600 }
601 }
602 }
603
604 /**
605 * Notify the provider when the customer cancels event booking.
606 *
607 * @param $eventArray
608 * @param $bookingArray
609 *
610 * @throws QueryExecutionException
611 * @throws InvalidArgumentException
612 */
613 public function sendProviderEventCancelledNotification($eventArray, $bookingArray)
614 {
615 $providerNotifications = $this->getByNameAndType(
616 "provider_event_canceled",
617 $this->type
618 );
619
620 $eventArray['bookings'] = [$bookingArray];
621
622 $sendDefault = $this->sendDefault($providerNotifications, $eventArray);
623 foreach ($providerNotifications->getItems() as $providerNotification) {
624 if ($providerNotification && $providerNotification->getStatus()->getValue() === NotificationStatus::ENABLED) {
625 if (!$this->checkCustom($providerNotification, $eventArray, $sendDefault)) {
626 continue;
627 }
628 $this->sendNotification(
629 $eventArray,
630 $providerNotification,
631 false,
632 null
633 );
634 }
635 }
636 }
637
638 /**
639 * Returns an array of next day reminder notifications that have to be sent to customers with cron
640 *
641 * @param string $entityType
642 *
643 * @return void
644 * @throws QueryExecutionException
645 * @throws InvalidArgumentException
646 * @throws Exception
647 */
648 public function sendNextDayReminderNotifications($entityType)
649 {
650 /** @var NotificationLogRepository $notificationLogRepo */
651 $notificationLogRepo = $this->container->get('domain.notificationLog.repository');
652
653 /** @var SettingsService $settingsService */
654 $settingsService = $this->container->get('domain.settings.service');
655
656 $customerNotifications = $this->getByNameAndType("customer_{$entityType}_next_day_reminder", $this->type);
657 $customerNotifications2 = $this->getByNameAndType("customer_{$entityType}_scheduled", $this->type);
658
659 foreach ($customerNotifications2->getItems() as $notification) {
660 $customerNotifications->addItem($notification);
661 }
662
663 $reminderStatuses = ['approved'];
664
665 if ($settingsService->getSetting('notifications', 'pendingReminder')) {
666 $reminderStatuses[] = 'pending';
667 }
668
669 $reservations = new Collection();
670
671 /** @var Notification $customerNotification */
672 foreach ($customerNotifications->getItems() as $customerNotification) {
673 // Check if notification is enabled and it is time to send notification
674 if ($customerNotification->getStatus()->getValue() === NotificationStatus::ENABLED &&
675 $customerNotification->getTime() &&
676 DateTimeService::getNowDateTimeObject() >=
677 DateTimeService::getCustomDateTimeObject($customerNotification->getTime()->getValue())
678 ) {
679 switch ($entityType) {
680 case Entities::APPOINTMENT:
681 $reservations = $notificationLogRepo->getCustomersNextDayAppointments(
682 $customerNotification->getId()->getValue(),
683 $customerNotification->getCustomName() === null,
684 $reminderStatuses
685 );
686
687 break;
688 case Entities::EVENT:
689 $reservations = $notificationLogRepo->getCustomersNextDayEvents($customerNotification->getId()->getValue(), $customerNotification->getCustomName() === null);
690
691 break;
692 }
693
694 $approvedReservations = new Collection();
695 foreach ($reservations->getItems() as $appointment) {
696 if ($appointment->getStatus()->getValue() === BookingStatus::APPROVED) {
697 $approvedReservations->addItem($appointment);
698 }
699 }
700
701 try {
702 $this->sendBookingsNotifications($customerNotification, $approvedReservations, true);
703 } catch (\Exception $e) {
704 }
705 }
706 }
707
708
709 /** @var Collection $providerNotifications */
710 $providerNotifications = $this->getByNameAndType("provider_{$entityType}_next_day_reminder", $this->type);
711 $providerNotifications2 = $this->getByNameAndType("provider_{$entityType}_scheduled", $this->type);
712
713 foreach ($providerNotifications2->getItems() as $notification) {
714 $providerNotifications->addItem($notification);
715 }
716
717 /** @var Notification $providerNotification */
718 foreach ($providerNotifications->getItems() as $providerNotification) {
719 // Check if notification is enabled and it is time to send notification
720 if ($providerNotification->getStatus()->getValue() === NotificationStatus::ENABLED &&
721 $providerNotification->getTime() &&
722 DateTimeService::getNowDateTimeObject() >=
723 DateTimeService::getCustomDateTimeObject($providerNotification->getTime()->getValue())
724 ) {
725 switch ($entityType) {
726 case Entities::APPOINTMENT:
727 $reservations = $notificationLogRepo->getProvidersNextDayAppointments(
728 $providerNotification->getId()->getValue(),
729 $providerNotification->getCustomName() === null,
730 $reminderStatuses
731 );
732
733 break;
734 case Entities::EVENT:
735 $reservations = $notificationLogRepo->getProvidersNextDayEvents($providerNotification->getId()->getValue(), $providerNotification->getCustomName() === null);
736
737 break;
738 }
739
740 foreach ((array)$reservations->toArray() as $reservationArray) {
741 if (!$this->checkCustom($providerNotification, $reservationArray, true)) {
742 continue;
743 }
744 if ($providerNotification->getCustomName() === null && !$this->checkShouldSend($reservationArray, true, NotificationSendTo::PROVIDER)) {
745 continue;
746 }
747
748 $bookingArray = $reservationArray['bookings'][count($reservationArray['bookings'])-1];
749 /** @var CustomerBooking $bookingObject */
750 $bookingObject = $bookingArray ? CustomerBookingFactory::create($bookingArray) : null;
751 $reservationStart = $entityType === Entities::APPOINTMENT ? $reservationArray['bookingStart'] : $reservationArray['periods'][0]['periodStart'];
752
753 if ($this->pastMinimumTimeBeforeBooking($providerNotification, $bookingObject, $reservationStart)) {
754 continue;
755 }
756
757 $reservationArray['sendCF'] = true;
758
759 try {
760 $this->sendNotification(
761 $reservationArray,
762 $providerNotification,
763 true
764 );
765 } catch (\Exception $e) {
766 }
767 }
768 }
769 }
770 }
771
772 /**
773 * @param int $entityId
774 * @param string $entityType
775 * @param int $userId
776 * @param string $userType
777 *
778 * @throws QueryExecutionException
779 * @throws InvalidArgumentException
780 */
781 public function invalidateSentScheduledNotifications($entityId, $entityType, $userId, $userType)
782 {
783 /** @var NotificationLogRepository $notificationLogRepo */
784 $notificationLogRepo = $this->container->get('domain.notificationLog.repository');
785
786 $templates = [
787 "{$userType}_{$entityType}_next_day_reminder",
788 "{$userType}_{$entityType}_scheduled",
789 "{$userType}_{$entityType}_scheduled_%",
790 ];
791
792 $notificationsIds = [];
793
794 foreach ($templates as $template) {
795 /** @var Collection $notifications */
796 $notifications = $this->getByNameAndType($template, $this->type);
797
798 $notificationsIds = array_merge($notificationsIds, $notifications->keys());
799 }
800
801 $notificationLogRepo->invalidateSentEmails($entityId, $entityType, $userId, array_unique($notificationsIds));
802 }
803
804 /**
805 * @param string $entityType
806 *
807 * @throws QueryExecutionException
808 * @throws InvalidArgumentException
809 */
810 public function sendScheduledNotifications($entityType)
811 {
812 /** @var SettingsService $settingsService */
813 $settingsService = $this->container->get('domain.settings.service');
814
815 /** @var Collection $notifications */
816 $notifications = $this->getByNameAndType("customer_{$entityType}_follow_up", $this->type);
817 $notifications2 = $this->getByNameAndType("customer_{$entityType}_scheduled_%", $this->type);
818 foreach ($notifications2->getItems() as $notification) {
819 $notifications->addItem($notification);
820 }
821 $notifications2 = $this->getByNameAndType("provider_{$entityType}_scheduled_%", $this->type);
822 foreach ($notifications2->getItems() as $notification) {
823 $notifications->addItem($notification);
824 }
825
826 $reminderStatuses = ['approved'];
827
828 if ($settingsService->getSetting('notifications', 'pendingReminder')) {
829 $reminderStatuses[] = 'pending';
830 }
831
832 /** @var Notification $notification */
833 foreach ($notifications->getItems() as $notification) {
834 if ($notification->getStatus()->getValue() === NotificationStatus::ENABLED) {
835 /** @var NotificationLogRepository $notificationLogRepo */
836 $notificationLogRepo = $this->container->get('domain.notificationLog.repository');
837
838 $reservations = new Collection();
839
840 switch ($entityType) {
841 case Entities::APPOINTMENT:
842 $reservations = $notificationLogRepo->getScheduledAppointments(
843 $notification,
844 $reminderStatuses
845 );
846
847 break;
848 case Entities::EVENT:
849 /** @var Collection $reservations */
850 $reservations = $notificationLogRepo->getScheduledEvents($notification);
851
852 /** @var EventApplicationService $eventAS */
853 $eventAS = $this->container->get('application.booking.event.service');
854
855 /** @var Collection $reservations */
856 $reservations = $reservations->length() ? $eventAS->getEventsByIds(
857 $reservations->keys(),
858 [
859 'fetchEventsPeriods' => true,
860 'fetchEventsTickets' => false,
861 'fetchEventsTags' => false,
862 'fetchEventsProviders' => true,
863 'fetchEventsImages' => false,
864 'fetchBookingsTickets' => false,
865 'fetchBookingsCoupons' => true,
866 'fetchApprovedBookings' => false,
867 'fetchBookingsPayments' => true,
868 'fetchBookingsUsers' => false,
869 'fetchBookings' => true,
870 ]
871 ) : new Collection();
872
873 break;
874 }
875
876 $approvedReservations = new Collection();
877 foreach ($reservations->getItems() as $appointment) {
878 if ($appointment->getStatus()->getValue() === BookingStatus::APPROVED) {
879 $approvedReservations->addItem($appointment);
880 }
881 }
882
883 try {
884 $this->sendBookingsNotifications($notification, $approvedReservations, $notification->getTimeBefore() !== null);
885 } catch (\Exception $e) {
886 }
887 }
888 }
889 }
890
891
892 /**
893 *
894 * @param Notification $notification
895 * @param CustomerBooking $booking
896 * @param string $appointmentStart
897 *
898 */
899 private function pastMinimumTimeBeforeBooking($notification, $booking, $appointmentStart)
900 {
901 if ($booking && $booking->getCreated() && $notification->getMinimumTimeBeforeBooking() && $notification->getMinimumTimeBeforeBooking()->getValue() &&
902 json_decode($notification->getMinimumTimeBeforeBooking()->getValue())) {
903 $minimumTime = json_decode($notification->getMinimumTimeBeforeBooking()->getValue(), true);
904 $seconds = 1;
905 switch ($minimumTime['period']) {
906 case 'minutes':
907 $seconds = 60;
908 break;
909 case 'hours':
910 $seconds = 60*60;
911 break;
912 case 'days':
913 $seconds = 24*60*60;
914 break;
915 case 'weeks':
916 $seconds = 7*24*60*60;
917 break;
918 case 'months':
919 $seconds = 30*7*24*60*60;
920 break;
921 }
922 $time = $minimumTime['amount']*$seconds;
923 if (DateTimeService::getCustomDateTimeObject($appointmentStart)->modify('-' . $time . ' second')
924 <= DateTimeService::getCustomDateTimeObject($booking->getCreated()->getValue()->format('Y-m-d H:i:s'))) {
925 return true;
926 }
927 }
928 return false;
929 }
930
931 /**
932 * Send passed notification for all passed bookings and save log in the database
933 *
934 * @param Notification $notification
935 * @param Collection $appointments
936 * @param bool $before
937 * @throws QueryExecutionException
938 * @throws InvalidArgumentException
939 */
940 private function sendBookingsNotifications($notification, $appointments, $before)
941 {
942 /** @var PaymentApplicationService $paymentAS */
943 $paymentAS = $this->container->get('application.payment.service');
944
945 /** @var array $appointmentArray */
946 foreach ($appointments->toArray() as $appointmentArray) {
947 if (!$this->checkCustom($notification, $appointmentArray, true)) {
948 continue;
949 }
950 if ($notification->getCustomName() === null && !$this->checkShouldSend($appointmentArray, $before, $notification->getSendTo()->getValue())) {
951 continue;
952 }
953
954 $appointmentArray['sendCF'] = true;
955
956 /** @var BookingApplicationService $bookingApplicationService */
957 $bookingApplicationService = $this->container->get('application.booking.booking.service');
958 $data = $appointmentArray;
959 $reservationObject = $bookingApplicationService->getReservationEntity($appointmentArray);
960
961 $reservationStart = $appointmentArray['type'] === Entities::APPOINTMENT ? $appointmentArray['bookingStart'] : $appointmentArray['periods'][0]['periodStart'];
962
963 if ($notification->getSendTo()->getValue() === NotificationSendTo::PROVIDER) {
964 /** @var CustomerBooking $bookingObject */
965 $bookingObject = $reservationObject->getBookings()->getItem($reservationObject->getBookings()->keys()[$reservationObject->getBookings()->length()-1]);
966
967 if ($this->pastMinimumTimeBeforeBooking($notification, $bookingObject, $reservationStart)) {
968 continue;
969 }
970
971 $this->sendNotification(
972 $appointmentArray,
973 $notification,
974 true
975 );
976 } else {
977 if ($appointmentArray['type'] === Entities::APPOINTMENT) {
978 $data['bookable'] = $reservationObject->getService()->toArray();
979 } else {
980 $data['bookable'] = $appointmentArray;
981 }
982
983 // Notify each customer from customer bookings
984 foreach (array_keys($appointmentArray['bookings']) as $bookingKey) {
985 /** @var CustomerBooking $bookingObject */
986 $bookingObject = $reservationObject->getBookings()->getItem($reservationObject->getBookings()->keys()[$bookingKey]);
987
988 if ($appointmentArray['type'] === 'event' && $bookingObject->getStatus()->getValue() !== BookingStatus::APPROVED) {
989 continue;
990 }
991
992 if ($notification->getContent() && $notification->getContent()->getValue() && strpos($notification->getContent()->getValue(), '%payment_link_') !== false) {
993 $data['booking'] = $bookingObject ? $bookingObject->toArray() : $appointmentArray['bookings'][$bookingKey];
994 $data['customer'] = $data['booking']['customer'];
995 $data[$appointmentArray['type']] = $appointmentArray;
996 $data['paymentId'] = $appointmentArray['bookings'][$bookingKey]['payments'][0]['id'];
997 $appointmentArray['bookings'][$bookingKey]['payments'][0]['paymentLinks'] = $paymentAS->createPaymentLink($data, $bookingKey);
998 }
999
1000 if ($this->pastMinimumTimeBeforeBooking($notification, $bookingObject, $reservationStart)) {
1001 continue;
1002 }
1003
1004 $this->sendNotification(
1005 $appointmentArray,
1006 $notification,
1007 true,
1008 $bookingKey
1009 );
1010 }
1011 }
1012 }
1013 }
1014
1015 /**
1016 * Check if schedule default notification should be sent
1017 *
1018 * @param array $appointmentArray
1019 * @param bool $before
1020 * @param string $sendTo
1021 *
1022 * @throws QueryExecutionException
1023 * @throws InvalidArgumentException
1024 *
1025 * return bool
1026 *
1027 */
1028 private function checkShouldSend($appointmentArray, $before, $sendTo)
1029 {
1030 $time = $before ? 'timeBefore' : 'timeAfter';
1031 $entityId = $appointmentArray['type'] === Entities::EVENT ? $appointmentArray['id'] : $appointmentArray['serviceId'];
1032 $notifications = $this->getByNameAndType("{$sendTo}_{$appointmentArray['type']}_scheduled_%", $this->type);
1033 $parentId = $appointmentArray['parentId'];
1034 return empty(
1035 array_filter(
1036 $notifications->toArray(),
1037 function ($a) use (&$entityId, &$time, &$parentId) {
1038 return $a['customName'] && $a[$time] && $a['sendOnlyMe'] &&
1039 ($a['entityIds'] === null || in_array($entityId, $a['entityIds']) || ($parentId && in_array($parentId, $a['entityIds'])));
1040 }
1041 )
1042 );
1043 }
1044
1045 /**
1046 * Check if custom notification should be sent
1047 *
1048 * @param Notification $notification
1049 * @param array $appointmentArray
1050 *
1051 * @return bool
1052 *
1053 */
1054 private function checkCustom($notification, $appointmentArray, $sendDefault)
1055 {
1056 if (!$sendDefault && !$notification->getCustomName()) {
1057 return false;
1058 }
1059 if ($notification->getCustomName() && $notification->getEntityIds()) {
1060 $entityId = $appointmentArray['type'] === Entities::EVENT ? $appointmentArray['id'] : $appointmentArray['serviceId'];
1061 if (!in_array($entityId, $notification->getEntityIds())) {
1062 if (!in_array($appointmentArray['parentId'], $notification->getEntityIds())) {
1063 //Shouldn't be sent
1064 return false;
1065 }
1066 }
1067 }
1068 return true;
1069 }
1070
1071 /**
1072 * Check if default notification should be sent
1073 *
1074 * @param Collection $notifications
1075 * @param array $appointmentArray
1076 *
1077 * @return bool
1078 *
1079 */
1080 private function sendDefault($notifications, $appointmentArray)
1081 {
1082 $entityId = $appointmentArray['type'] === Entities::EVENT ? $appointmentArray['id'] : $appointmentArray['serviceId'];
1083 $parentId = $appointmentArray['parentId'];
1084 return empty(
1085 array_filter(
1086 $notifications->toArray(),
1087 function ($a) use (&$entityId, &$parentId) {
1088 return $a['customName'] && $a['sendOnlyMe'] &&
1089 ($a['entityIds'] === null || in_array($entityId, $a['entityIds']) || ($parentId && in_array($parentId, $a['entityIds'])));
1090 }
1091 )
1092 );
1093 }
1094
1095 /**
1096 * @param array $data
1097 * @param bool $logNotification
1098 *
1099 * @throws QueryExecutionException
1100 * @throws InvalidArgumentException
1101 */
1102 public function sendPackageNotifications($data, $logNotification, $notifyCustomers = true)
1103 {
1104 /** @var Collection $customerNotifications */
1105 $customerNotifications = $this->getByNameAndType(
1106 "customer_package_" . $data['status'],
1107 $this->type
1108 );
1109
1110 $data['isForCustomer'] = true;
1111
1112 foreach ($customerNotifications->getItems() as $customerNotification) {
1113 if ($customerNotification->getStatus()->getValue() === NotificationStatus::ENABLED && $notifyCustomers) {
1114 $this->sendNotification(
1115 $data,
1116 $customerNotification,
1117 $logNotification
1118 );
1119 }
1120 }
1121
1122 /** @var Collection $providerNotifications */
1123 $providerNotifications = $this->getByNameAndType(
1124 "provider_package_" . $data['status'],
1125 $this->type
1126 );
1127
1128 $data['isForCustomer'] = false;
1129 foreach ($providerNotifications->getItems() as $providerNotification) {
1130 if ($providerNotification->getStatus()->getValue() === NotificationStatus::ENABLED) {
1131 $this->sendNotification(
1132 $data,
1133 $providerNotification,
1134 $logNotification
1135 );
1136 }
1137 }
1138 }
1139
1140 /**
1141 * @param array $data
1142 * @param bool $logNotification
1143 *
1144 * @throws QueryExecutionException
1145 * @throws InvalidArgumentException
1146 */
1147 public function sendCartNotifications($data, $logNotification, $notifyCustomers = true)
1148 {
1149 /** @var Collection $customerNotifications */
1150 $customerNotifications = $this->getByNameAndType(
1151 'customer_cart',
1152 $this->type
1153 );
1154
1155 $data['isForCustomer'] = true;
1156
1157 foreach ($customerNotifications->getItems() as $customerNotification) {
1158 if ($customerNotification->getStatus()->getValue() === NotificationStatus::ENABLED && $notifyCustomers) {
1159 $this->sendNotification(
1160 $data,
1161 $customerNotification,
1162 $logNotification
1163 );
1164 }
1165 }
1166
1167 /** @var Collection $providerNotifications */
1168 $providerNotifications = $this->getByNameAndType(
1169 'provider_cart',
1170 $this->type
1171 );
1172
1173 $data['isForCustomer'] = false;
1174
1175 foreach ($providerNotifications->getItems() as $providerNotification) {
1176 if ($providerNotification->getStatus()->getValue() === NotificationStatus::ENABLED) {
1177 $this->sendNotification(
1178 $data,
1179 $providerNotification,
1180 $logNotification
1181 );
1182 }
1183 }
1184 }
1185
1186 /**
1187 * Get User info for notification
1188 *
1189 * @param string $userType
1190 * @param array $entityData
1191 * @param int $bookingKey
1192 * @param array $emailData
1193 *
1194 * @return array
1195 * @throws QueryExecutionException
1196 */
1197 protected function getUsersInfo($userType, $entityData, $bookingKey, $emailData)
1198 {
1199 /** @var ProviderRepository $providerRepository */
1200 $providerRepository = $this->container->get('domain.users.providers.repository');
1201
1202 /** @var \AmeliaBooking\Application\Services\Settings\SettingsService $settingsAS*/
1203 $settingsAS = $this->container->get('application.settings.service');
1204
1205
1206 $usersInfo = [];
1207
1208 switch ($userType) {
1209 case (Entities::CUSTOMER):
1210 switch ($entityData['type']) {
1211 case (Entities::APPOINTMENT):
1212 case (Entities::EVENT):
1213 if ($bookingKey !== null) {
1214 $usersInfo[$entityData['bookings'][$bookingKey]['customerId']] = [
1215 'id' => $entityData['bookings'][$bookingKey]['customerId'],
1216 'email' => $emailData['customer_email'],
1217 'phone' => $emailData['customer_phone']
1218 ];
1219 }
1220
1221 break;
1222
1223 case (Entities::PACKAGE):
1224 case (Entities::APPOINTMENTS):
1225 $usersInfo[$entityData['customer']['id']] = [
1226 'id' => $entityData['customer']['id'],
1227 'email' => $entityData['customer']['email'],
1228 'phone' => $entityData['customer']['phone']
1229 ];
1230
1231 break;
1232 }
1233
1234
1235 break;
1236
1237 case (Entities::PROVIDER):
1238 switch ($entityData['type']) {
1239 case (Entities::APPOINTMENT):
1240 $usersInfo[$entityData['providerId']] = [
1241 'id' => $entityData['providerId'],
1242 'email' => $emailData['employee_email'],
1243 'phone' => $emailData['employee_phone']
1244 ];
1245
1246 break;
1247
1248 case (Entities::EVENT):
1249 foreach ((array)$entityData['providers'] as $provider) {
1250 $usersInfo[$provider['id']] = [
1251 'id' => $provider['id'],
1252 'email' => $provider['email'],
1253 'phone' => $provider['phone']
1254 ];
1255 }
1256 if ($entityData['organizerId']) {
1257 $organizer = $providerRepository->getById($entityData['organizerId'])->toArray();
1258 $usersInfo[$organizer['id']] = [
1259 'id' => $organizer['id'],
1260 'email' => $organizer['email'],
1261 'phone' => $organizer['phone']
1262 ];
1263 }
1264
1265 break;
1266
1267 case (Entities::PACKAGE):
1268 case (Entities::APPOINTMENTS):
1269 foreach ($entityData['recurring'] as $reservation) {
1270 $usersInfo[$reservation['appointment']['provider']['id']] = [
1271 'id' => $reservation['appointment']['provider']['id'],
1272 'email' => $reservation['appointment']['provider']['email'],
1273 'phone' => $reservation['appointment']['provider']['phone']
1274 ];
1275 }
1276 if (empty($entityData['recurring'])) {
1277 if (!empty($entityData['onlyOneEmployee'])) {
1278 $usersInfo[$entityData['onlyOneEmployee']['id']] = [
1279 'id' => $entityData['onlyOneEmployee']['id'],
1280 'email' => $entityData['onlyOneEmployee']['email'],
1281 'phone' => $entityData['onlyOneEmployee']['phone']
1282 ];
1283 }
1284 $emptyPackageEmployees = $settingsAS->getEmptyPackageEmployees();
1285 if (!empty($emptyPackageEmployees)) {
1286 foreach ($emptyPackageEmployees as $employee) {
1287 $usersInfo[$employee['id']] = [
1288 'id' => $employee['id'],
1289 'email' => $employee['email'],
1290 'phone' => $employee['phone']
1291 ];
1292 }
1293 }
1294 }
1295
1296 break;
1297 }
1298
1299 break;
1300 }
1301
1302 return $usersInfo;
1303 }
1304 }
1305