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