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