PluginProbe ʕ •ᴥ•ʔ
Booking for Appointments and Events Calendar – Amelia / 1.2.25
Booking for Appointments and Events Calendar – Amelia v1.2.25
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 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
1286 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 } elseif ($appointmentArray['type'] === Entities::EVENT &&
438 $settingsService->getSetting('general', 'defaultEventStatus') === BookingStatus::PENDING
439 ) {
440 $defaultStatus = $bookingArray['status'];
441 }
442
443 $customerNotifications = $this->getByNameAndType(
444 "customer_{$appointmentArray['type']}_{$defaultStatus}",
445 $this->type
446 );
447
448 $sendDefault = $this->sendDefault($customerNotifications, $appointmentArray);
449
450 foreach ($customerNotifications->getItems() as $customerNotification) {
451 if ($customerNotification->getStatus()->getValue() === NotificationStatus::ENABLED) {
452 if (!$this->checkCustom($customerNotification, $appointmentArray, $sendDefault)) {
453 continue;
454 }
455
456 // Notify customer that scheduled the appointment
457 $this->sendNotification(
458 $appointmentArray,
459 $customerNotification,
460 $logNotification,
461 $bookingArray ? array_search($bookingArray['id'], array_column($appointmentArray['bookings'], 'id'), true) : null,
462 null,
463 $invoice
464 );
465 }
466 }
467
468 // Notify provider
469 $providerNotifications = $this->getByNameAndType(
470 "provider_{$appointmentArray['type']}_{$defaultStatus}",
471 $this->type
472 );
473
474 $sendDefault = $this->sendDefault($providerNotifications, $appointmentArray);
475 foreach ($providerNotifications->getItems() as $providerNotification) {
476 if ($providerNotification && $providerNotification->getStatus()->getValue() === NotificationStatus::ENABLED) {
477 if (!$this->checkCustom($providerNotification, $appointmentArray, $sendDefault)) {
478 continue;
479 }
480 $allBookings = null;
481 if ($appointmentArray['type'] === Entities::EVENT) {
482 $allBookings = $appointmentArray['bookings'];
483 $appointmentArray['bookings'] = [$bookingArray];
484 }
485 $this->sendNotification(
486 $appointmentArray,
487 $providerNotification,
488 $logNotification,
489 null,
490 $allBookings
491 );
492 }
493 }
494 }
495
496 /**
497 * Notify the customer when he changes his booking status.
498 *
499 * @param $appointmentArray
500 * @param $bookingArray
501 *
502 * @throws QueryExecutionException
503 * @throws InvalidArgumentException
504 */
505 public function sendCustomerBookingNotification($appointmentArray, $bookingArray, $sendInvoice = false)
506 {
507 // Notify customers
508 if ($appointmentArray['notifyParticipants']) {
509 $customerNotifications = $this->getByNameAndType("customer_{$appointmentArray['type']}_{$bookingArray['status']}", $this->type);
510
511 $sendDefault = $this->sendDefault($customerNotifications, $appointmentArray);
512 foreach ($customerNotifications->getItems() as $customerNotification) {
513 if ($customerNotification->getStatus()->getValue() === NotificationStatus::ENABLED) {
514 if (!$this->checkCustom($customerNotification, $appointmentArray, $sendDefault)) {
515 continue;
516 }
517 // Notify customer
518 $bookingKey = array_search(
519 $bookingArray['id'],
520 array_column($appointmentArray['bookings'], 'id'),
521 true
522 );
523
524 $invoice = null;
525
526 if ($sendInvoice) {
527 /** @var AbstractInvoiceApplicationService $invoiceService */
528 $invoiceService = $this->container->get('application.invoice.service');
529
530 $invoice = $invoiceService->generateInvoice($appointmentArray['bookings'][$bookingKey]['payments'][0]['id']);
531 }
532
533 $this->sendNotification(
534 $appointmentArray,
535 $customerNotification,
536 true,
537 $bookingKey,
538 null,
539 $invoice
540 );
541 }
542 }
543 }
544 }
545
546 /**
547 * Notify the provider when he changes his booking status.
548 *
549 * @param $appointmentArray
550 * @param $bookingArray
551 *
552 * @throws QueryExecutionException
553 * @throws InvalidArgumentException
554 */
555 public function sendProviderBookingNotification($appointmentArray, $bookingArray)
556 {
557 // Notify provider
558 if ($appointmentArray['notifyParticipants'] && $bookingArray['status'] !== BookingStatus::REJECTED) {
559 $providerNotifications = $this->getByNameAndType("provider_{$appointmentArray['type']}_{$bookingArray['status']}", $this->type);
560
561 $sendDefault = $this->sendDefault($providerNotifications, $appointmentArray);
562 foreach ($providerNotifications->getItems() as $customerNotification) {
563 if ($customerNotification->getStatus()->getValue() === NotificationStatus::ENABLED) {
564 if (!$this->checkCustom($customerNotification, $appointmentArray, $sendDefault)) {
565 continue;
566 }
567 // Notify provider
568 $bookingKey = array_search(
569 $bookingArray['id'],
570 array_column($appointmentArray['bookings'], 'id'),
571 true
572 );
573
574 $this->sendNotification(
575 $appointmentArray,
576 $customerNotification,
577 true,
578 $bookingKey
579 );
580 }
581 }
582 }
583 }
584
585 /**
586 * Notify the provider when the customer cancels event booking.
587 *
588 * @param $eventArray
589 * @param $bookingArray
590 *
591 * @throws QueryExecutionException
592 * @throws InvalidArgumentException
593 */
594 public function sendProviderEventCancelledNotification($eventArray, $bookingArray)
595 {
596 $providerNotifications = $this->getByNameAndType(
597 "provider_event_canceled",
598 $this->type
599 );
600
601 $eventArray['bookings'] = [$bookingArray];
602
603 $sendDefault = $this->sendDefault($providerNotifications, $eventArray);
604 foreach ($providerNotifications->getItems() as $providerNotification) {
605 if ($providerNotification && $providerNotification->getStatus()->getValue() === NotificationStatus::ENABLED) {
606 if (!$this->checkCustom($providerNotification, $eventArray, $sendDefault)) {
607 continue;
608 }
609 $this->sendNotification(
610 $eventArray,
611 $providerNotification,
612 false,
613 null
614 );
615 }
616 }
617 }
618
619 /**
620 * Returns an array of next day reminder notifications that have to be sent to customers with cron
621 *
622 * @param string $entityType
623 *
624 * @return void
625 * @throws QueryExecutionException
626 * @throws InvalidArgumentException
627 * @throws Exception
628 */
629 public function sendNextDayReminderNotifications($entityType)
630 {
631 /** @var NotificationLogRepository $notificationLogRepo */
632 $notificationLogRepo = $this->container->get('domain.notificationLog.repository');
633
634 /** @var SettingsService $settingsService */
635 $settingsService = $this->container->get('domain.settings.service');
636
637 $customerNotifications = $this->getByNameAndType("customer_{$entityType}_next_day_reminder", $this->type);
638 $customerNotifications2 = $this->getByNameAndType("customer_{$entityType}_scheduled", $this->type);
639
640 foreach ($customerNotifications2->getItems() as $notification) {
641 $customerNotifications->addItem($notification);
642 }
643
644 $reminderStatuses = ['approved'];
645
646 if ($settingsService->getSetting('notifications', 'pendingReminder')) {
647 $reminderStatuses[] = 'pending';
648 }
649
650 $reservations = new Collection();
651
652 /** @var Notification $customerNotification */
653 foreach ($customerNotifications->getItems() as $customerNotification) {
654 // Check if notification is enabled and it is time to send notification
655 if ($customerNotification->getStatus()->getValue() === NotificationStatus::ENABLED &&
656 $customerNotification->getTime() &&
657 DateTimeService::getNowDateTimeObject() >=
658 DateTimeService::getCustomDateTimeObject($customerNotification->getTime()->getValue())
659 ) {
660 switch ($entityType) {
661 case Entities::APPOINTMENT:
662 $reservations = $notificationLogRepo->getCustomersNextDayAppointments(
663 $customerNotification->getId()->getValue(),
664 $customerNotification->getCustomName() === null,
665 $reminderStatuses
666 );
667
668 break;
669 case Entities::EVENT:
670 $reservations = $notificationLogRepo->getCustomersNextDayEvents($customerNotification->getId()->getValue(), $customerNotification->getCustomName() === null);
671
672 break;
673 }
674
675 $approvedReservations = new Collection();
676 foreach ($reservations->getItems() as $appointment) {
677 if ($appointment->getStatus()->getValue() === BookingStatus::APPROVED) {
678 $approvedReservations->addItem($appointment);
679 }
680 }
681
682 try {
683 $this->sendBookingsNotifications($customerNotification, $approvedReservations, true);
684 } catch (\Exception $e) {
685 }
686 }
687 }
688
689
690 /** @var Collection $providerNotifications */
691 $providerNotifications = $this->getByNameAndType("provider_{$entityType}_next_day_reminder", $this->type);
692 $providerNotifications2 = $this->getByNameAndType("provider_{$entityType}_scheduled", $this->type);
693
694 foreach ($providerNotifications2->getItems() as $notification) {
695 $providerNotifications->addItem($notification);
696 }
697
698 /** @var Notification $providerNotification */
699 foreach ($providerNotifications->getItems() as $providerNotification) {
700 // Check if notification is enabled and it is time to send notification
701 if ($providerNotification->getStatus()->getValue() === NotificationStatus::ENABLED &&
702 $providerNotification->getTime() &&
703 DateTimeService::getNowDateTimeObject() >=
704 DateTimeService::getCustomDateTimeObject($providerNotification->getTime()->getValue())
705 ) {
706 switch ($entityType) {
707 case Entities::APPOINTMENT:
708 $reservations = $notificationLogRepo->getProvidersNextDayAppointments(
709 $providerNotification->getId()->getValue(),
710 $providerNotification->getCustomName() === null,
711 $reminderStatuses
712 );
713
714 break;
715 case Entities::EVENT:
716 $reservations = $notificationLogRepo->getProvidersNextDayEvents($providerNotification->getId()->getValue(), $providerNotification->getCustomName() === null);
717
718 break;
719 }
720
721 foreach ((array)$reservations->toArray() as $reservationArray) {
722 if (!$this->checkCustom($providerNotification, $reservationArray, true)) {
723 continue;
724 }
725 if ($providerNotification->getCustomName() === null && !$this->checkShouldSend($reservationArray, true, NotificationSendTo::PROVIDER)) {
726 continue;
727 }
728
729 $bookingArray = $reservationArray['bookings'][count($reservationArray['bookings'])-1];
730 /** @var CustomerBooking $bookingObject */
731 $bookingObject = $bookingArray ? CustomerBookingFactory::create($bookingArray) : null;
732 $reservationStart = $entityType === Entities::APPOINTMENT ? $reservationArray['bookingStart'] : $reservationArray['periods'][0]['periodStart'];
733
734 if ($this->pastMinimumTimeBeforeBooking($providerNotification, $bookingObject, $reservationStart)) {
735 continue;
736 }
737
738 $reservationArray['sendCF'] = true;
739
740 try {
741 $this->sendNotification(
742 $reservationArray,
743 $providerNotification,
744 true
745 );
746 } catch (\Exception $e) {
747 }
748 }
749 }
750 }
751 }
752
753 /**
754 * @param int $entityId
755 * @param string $entityType
756 * @param int $userId
757 * @param string $userType
758 *
759 * @throws QueryExecutionException
760 * @throws InvalidArgumentException
761 */
762 public function invalidateSentScheduledNotifications($entityId, $entityType, $userId, $userType)
763 {
764 /** @var NotificationLogRepository $notificationLogRepo */
765 $notificationLogRepo = $this->container->get('domain.notificationLog.repository');
766
767 $templates = [
768 "{$userType}_{$entityType}_next_day_reminder",
769 "{$userType}_{$entityType}_scheduled",
770 "{$userType}_{$entityType}_scheduled_%",
771 ];
772
773 $notificationsIds = [];
774
775 foreach ($templates as $template) {
776 /** @var Collection $notifications */
777 $notifications = $this->getByNameAndType($template, $this->type);
778
779 $notificationsIds = array_merge($notificationsIds, $notifications->keys());
780 }
781
782 $notificationLogRepo->invalidateSentEmails($entityId, $entityType, $userId, array_unique($notificationsIds));
783 }
784
785 /**
786 * @param string $entityType
787 *
788 * @throws QueryExecutionException
789 * @throws InvalidArgumentException
790 */
791 public function sendScheduledNotifications($entityType)
792 {
793 /** @var SettingsService $settingsService */
794 $settingsService = $this->container->get('domain.settings.service');
795
796 /** @var Collection $notifications */
797 $notifications = $this->getByNameAndType("customer_{$entityType}_follow_up", $this->type);
798 $notifications2 = $this->getByNameAndType("customer_{$entityType}_scheduled_%", $this->type);
799 foreach ($notifications2->getItems() as $notification) {
800 $notifications->addItem($notification);
801 }
802 $notifications2 = $this->getByNameAndType("provider_{$entityType}_scheduled_%", $this->type);
803 foreach ($notifications2->getItems() as $notification) {
804 $notifications->addItem($notification);
805 }
806
807 $reminderStatuses = ['approved'];
808
809 if ($settingsService->getSetting('notifications', 'pendingReminder')) {
810 $reminderStatuses[] = 'pending';
811 }
812
813 /** @var Notification $notification */
814 foreach ($notifications->getItems() as $notification) {
815 if ($notification->getStatus()->getValue() === NotificationStatus::ENABLED) {
816 /** @var NotificationLogRepository $notificationLogRepo */
817 $notificationLogRepo = $this->container->get('domain.notificationLog.repository');
818
819 $reservations = new Collection();
820
821 switch ($entityType) {
822 case Entities::APPOINTMENT:
823 $reservations = $notificationLogRepo->getScheduledAppointments(
824 $notification,
825 $reminderStatuses
826 );
827
828 break;
829 case Entities::EVENT:
830 /** @var Collection $reservations */
831 $reservations = $notificationLogRepo->getScheduledEvents($notification);
832
833 /** @var EventApplicationService $eventAS */
834 $eventAS = $this->container->get('application.booking.event.service');
835
836 /** @var Collection $reservations */
837 $reservations = $reservations->length() ? $eventAS->getEventsByIds(
838 $reservations->keys(),
839 [
840 'fetchEventsPeriods' => true,
841 'fetchEventsProviders' => true,
842 'fetchBookings' => true,
843 'fetchBookingsCoupons' => true,
844 'fetchBookingsPayments' => true,
845 ]
846 ) : new Collection();
847
848 break;
849 }
850
851 $approvedReservations = new Collection();
852 foreach ($reservations->getItems() as $appointment) {
853 if ($appointment->getStatus()->getValue() === BookingStatus::APPROVED) {
854 $approvedReservations->addItem($appointment);
855 }
856 }
857
858 try {
859 $this->sendBookingsNotifications($notification, $approvedReservations, $notification->getTimeBefore() !== null);
860 } catch (\Exception $e) {
861 }
862 }
863 }
864 }
865
866
867 /**
868 *
869 * @param Notification $notification
870 * @param CustomerBooking $booking
871 * @param string $appointmentStart
872 *
873 */
874 private function pastMinimumTimeBeforeBooking($notification, $booking, $appointmentStart)
875 {
876 if ($booking && $booking->getCreated() && $notification->getMinimumTimeBeforeBooking() && $notification->getMinimumTimeBeforeBooking()->getValue() &&
877 json_decode($notification->getMinimumTimeBeforeBooking()->getValue())) {
878 $minimumTime = json_decode($notification->getMinimumTimeBeforeBooking()->getValue(), true);
879 $seconds = 1;
880 switch ($minimumTime['period']) {
881 case 'minutes':
882 $seconds = 60;
883 break;
884 case 'hours':
885 $seconds = 60*60;
886 break;
887 case 'days':
888 $seconds = 24*60*60;
889 break;
890 case 'weeks':
891 $seconds = 7*24*60*60;
892 break;
893 case 'months':
894 $seconds = 30*7*24*60*60;
895 break;
896 }
897 $time = $minimumTime['amount']*$seconds;
898 if (DateTimeService::getCustomDateTimeObject($appointmentStart)->modify('-' . $time . ' second')
899 <= DateTimeService::getCustomDateTimeObject($booking->getCreated()->getValue()->format('Y-m-d H:i:s'))) {
900 return true;
901 }
902 }
903 return false;
904 }
905
906 /**
907 * Send passed notification for all passed bookings and save log in the database
908 *
909 * @param Notification $notification
910 * @param Collection $appointments
911 * @param bool $before
912 * @throws QueryExecutionException
913 * @throws InvalidArgumentException
914 */
915 private function sendBookingsNotifications($notification, $appointments, $before)
916 {
917 /** @var PaymentApplicationService $paymentAS */
918 $paymentAS = $this->container->get('application.payment.service');
919
920 /** @var array $appointmentArray */
921 foreach ($appointments->toArray() as $appointmentArray) {
922 if (!$this->checkCustom($notification, $appointmentArray, true)) {
923 continue;
924 }
925 if ($notification->getCustomName() === null && !$this->checkShouldSend($appointmentArray, $before, $notification->getSendTo()->getValue())) {
926 continue;
927 }
928
929 $appointmentArray['sendCF'] = true;
930
931 /** @var BookingApplicationService $bookingApplicationService */
932 $bookingApplicationService = $this->container->get('application.booking.booking.service');
933 $data = $appointmentArray;
934 $reservationObject = $bookingApplicationService->getReservationEntity($appointmentArray);
935
936 $reservationStart = $appointmentArray['type'] === Entities::APPOINTMENT ? $appointmentArray['bookingStart'] : $appointmentArray['periods'][0]['periodStart'];
937
938 if ($notification->getSendTo()->getValue() === NotificationSendTo::PROVIDER) {
939 /** @var CustomerBooking $bookingObject */
940 $bookingObject = $reservationObject->getBookings()->getItem($reservationObject->getBookings()->keys()[$reservationObject->getBookings()->length()-1]);
941
942 if ($this->pastMinimumTimeBeforeBooking($notification, $bookingObject, $reservationStart)) {
943 continue;
944 }
945
946 $this->sendNotification(
947 $appointmentArray,
948 $notification,
949 true
950 );
951 } else {
952 if ($appointmentArray['type'] === Entities::APPOINTMENT) {
953 $data['bookable'] = $reservationObject->getService()->toArray();
954 } else {
955 $data['bookable'] = $appointmentArray;
956 }
957
958 // Notify each customer from customer bookings
959 foreach (array_keys($appointmentArray['bookings']) as $bookingKey) {
960 /** @var CustomerBooking $bookingObject */
961 $bookingObject = $reservationObject->getBookings()->getItem($reservationObject->getBookings()->keys()[$bookingKey]);
962
963 if ($appointmentArray['type'] === 'event' && $bookingObject->getStatus()->getValue() !== BookingStatus::APPROVED) {
964 continue;
965 }
966
967 if ($notification->getContent() && $notification->getContent()->getValue() && strpos($notification->getContent()->getValue(), '%payment_link_') !== false) {
968 $data['booking'] = $bookingObject ? $bookingObject->toArray() : $appointmentArray['bookings'][$bookingKey];
969 $data['customer'] = $data['booking']['customer'];
970 $data[$appointmentArray['type']] = $appointmentArray;
971 $data['paymentId'] = $appointmentArray['bookings'][$bookingKey]['payments'][0]['id'];
972 $appointmentArray['bookings'][$bookingKey]['payments'][0]['paymentLinks'] = $paymentAS->createPaymentLink($data, $bookingKey);
973 }
974
975 if ($this->pastMinimumTimeBeforeBooking($notification, $bookingObject, $reservationStart)) {
976 continue;
977 }
978
979 $this->sendNotification(
980 $appointmentArray,
981 $notification,
982 true,
983 $bookingKey
984 );
985 }
986 }
987 }
988 }
989
990 /**
991 * Check if schedule default notification should be sent
992 *
993 * @param array $appointmentArray
994 * @param bool $before
995 * @param string $sendTo
996 *
997 * @throws QueryExecutionException
998 * @throws InvalidArgumentException
999 *
1000 * return bool
1001 *
1002 */
1003 private function checkShouldSend($appointmentArray, $before, $sendTo)
1004 {
1005 $time = $before ? 'timeBefore' : 'timeAfter';
1006 $entityId = $appointmentArray['type'] === Entities::EVENT ? $appointmentArray['id'] : $appointmentArray['serviceId'];
1007 $notifications = $this->getByNameAndType("{$sendTo}_{$appointmentArray['type']}_scheduled_%", $this->type);
1008 $parentId = $appointmentArray['parentId'];
1009 return empty(
1010 array_filter(
1011 $notifications->toArray(),
1012 function ($a) use (&$entityId, &$time, &$parentId) {
1013 return $a['customName'] && $a[$time] && $a['sendOnlyMe'] &&
1014 ($a['entityIds'] === null || in_array($entityId, $a['entityIds']) || ($parentId && in_array($parentId, $a['entityIds'])));
1015 }
1016 )
1017 );
1018 }
1019
1020 /**
1021 * Check if custom notification should be sent
1022 *
1023 * @param Notification $notification
1024 * @param array $appointmentArray
1025 *
1026 * @return bool
1027 *
1028 */
1029 public function checkCustom($notification, $appointmentArray, $sendDefault)
1030 {
1031 if (!$sendDefault && !$notification->getCustomName()) {
1032 return false;
1033 }
1034 if ($notification->getCustomName() && $notification->getEntityIds()) {
1035 $entityId = $appointmentArray['type'] === Entities::EVENT ? $appointmentArray['id'] : $appointmentArray['serviceId'];
1036 if (!in_array($entityId, $notification->getEntityIds())) {
1037 if (!in_array($appointmentArray['parentId'], $notification->getEntityIds())) {
1038 //Shouldn't be sent
1039 return false;
1040 }
1041 }
1042 }
1043 return true;
1044 }
1045
1046 /**
1047 * Check if default notification should be sent
1048 *
1049 * @param Collection $notifications
1050 * @param array $appointmentArray
1051 *
1052 * @return bool
1053 *
1054 */
1055 public function sendDefault($notifications, $appointmentArray)
1056 {
1057 $entityId = $appointmentArray['type'] === Entities::EVENT ? $appointmentArray['id'] : $appointmentArray['serviceId'];
1058 $parentId = $appointmentArray['parentId'];
1059 return empty(
1060 array_filter(
1061 $notifications->toArray(),
1062 function ($a) use (&$entityId, &$parentId) {
1063 return $a['customName'] && $a['sendOnlyMe'] &&
1064 ($a['entityIds'] === null || in_array($entityId, $a['entityIds']) || ($parentId && in_array($parentId, $a['entityIds'])));
1065 }
1066 )
1067 );
1068 }
1069
1070 /**
1071 * @param array $data
1072 * @param bool $logNotification
1073 *
1074 * @throws QueryExecutionException
1075 * @throws InvalidArgumentException
1076 */
1077 public function sendPackageNotifications($data, $logNotification, $notifyCustomers = true, $invoice = null)
1078 {
1079 /** @var Collection $customerNotifications */
1080 $customerNotifications = $this->getByNameAndType(
1081 "customer_package_" . $data['status'],
1082 $this->type
1083 );
1084
1085 $data['isForCustomer'] = true;
1086
1087 foreach ($customerNotifications->getItems() as $customerNotification) {
1088 if ($customerNotification->getStatus()->getValue() === NotificationStatus::ENABLED && $notifyCustomers) {
1089 $this->sendNotification(
1090 $data,
1091 $customerNotification,
1092 $logNotification,
1093 null,
1094 null,
1095 $invoice
1096 );
1097 }
1098 }
1099
1100 /** @var Collection $providerNotifications */
1101 $providerNotifications = $this->getByNameAndType(
1102 "provider_package_" . $data['status'],
1103 $this->type
1104 );
1105
1106 $data['isForCustomer'] = false;
1107 foreach ($providerNotifications->getItems() as $providerNotification) {
1108 if ($providerNotification->getStatus()->getValue() === NotificationStatus::ENABLED) {
1109 $this->sendNotification(
1110 $data,
1111 $providerNotification,
1112 $logNotification
1113 );
1114 }
1115 }
1116 }
1117
1118 /**
1119 * @param array $data
1120 * @param bool $logNotification
1121 *
1122 * @throws QueryExecutionException
1123 * @throws InvalidArgumentException
1124 */
1125 public function sendCartNotifications($data, $logNotification, $notifyCustomers = true, $invoice = null)
1126 {
1127 /** @var Collection $customerNotifications */
1128 $customerNotifications = $this->getByNameAndType(
1129 'customer_cart',
1130 $this->type
1131 );
1132
1133 $data['isForCustomer'] = true;
1134
1135 foreach ($customerNotifications->getItems() as $customerNotification) {
1136 if ($customerNotification->getStatus()->getValue() === NotificationStatus::ENABLED && $notifyCustomers) {
1137 $this->sendNotification(
1138 $data,
1139 $customerNotification,
1140 $logNotification,
1141 null,
1142 null,
1143 $invoice
1144 );
1145 }
1146 }
1147
1148 /** @var Collection $providerNotifications */
1149 $providerNotifications = $this->getByNameAndType(
1150 'provider_cart',
1151 $this->type
1152 );
1153
1154 $data['isForCustomer'] = false;
1155
1156 foreach ($providerNotifications->getItems() as $providerNotification) {
1157 if ($providerNotification->getStatus()->getValue() === NotificationStatus::ENABLED) {
1158 $this->sendNotification(
1159 $data,
1160 $providerNotification,
1161 $logNotification
1162 );
1163 }
1164 }
1165 }
1166
1167 /**
1168 * Get User info for notification
1169 *
1170 * @param string $userType
1171 * @param array $entityData
1172 * @param int $bookingKey
1173 * @param array $emailData
1174 *
1175 * @return array
1176 * @throws QueryExecutionException
1177 */
1178 protected function getUsersInfo($userType, $entityData, $bookingKey, $emailData)
1179 {
1180 /** @var ProviderRepository $providerRepository */
1181 $providerRepository = $this->container->get('domain.users.providers.repository');
1182
1183 /** @var \AmeliaBooking\Application\Services\Settings\SettingsService $settingsAS*/
1184 $settingsAS = $this->container->get('application.settings.service');
1185
1186
1187 $usersInfo = [];
1188
1189 switch ($userType) {
1190 case (Entities::CUSTOMER):
1191 switch ($entityData['type']) {
1192 case (Entities::APPOINTMENT):
1193 case (Entities::EVENT):
1194 if ($bookingKey !== null) {
1195 $usersInfo[$entityData['bookings'][$bookingKey]['customerId']] = [
1196 'id' => $entityData['bookings'][$bookingKey]['customerId'],
1197 'email' => $emailData['customer_email'],
1198 'phone' => $emailData['customer_phone']
1199 ];
1200 }
1201
1202 break;
1203
1204 case (Entities::PACKAGE):
1205 case (Entities::APPOINTMENTS):
1206 $usersInfo[$entityData['customer']['id']] = [
1207 'id' => $entityData['customer']['id'],
1208 'email' => $entityData['customer']['email'],
1209 'phone' => $entityData['customer']['phone']
1210 ];
1211
1212 break;
1213 }
1214
1215
1216 break;
1217
1218 case (Entities::PROVIDER):
1219 switch ($entityData['type']) {
1220 case (Entities::APPOINTMENT):
1221 $usersInfo[$entityData['providerId']] = [
1222 'id' => $entityData['providerId'],
1223 'email' => $emailData['employee_email'],
1224 'phone' => $emailData['employee_phone']
1225 ];
1226
1227 break;
1228
1229 case (Entities::EVENT):
1230 foreach ((array)$entityData['providers'] as $provider) {
1231 $usersInfo[$provider['id']] = [
1232 'id' => $provider['id'],
1233 'email' => $provider['email'],
1234 'phone' => $provider['phone']
1235 ];
1236 }
1237 if ($entityData['organizerId']) {
1238 $organizer = $providerRepository->getById($entityData['organizerId'])->toArray();
1239 $usersInfo[$organizer['id']] = [
1240 'id' => $organizer['id'],
1241 'email' => $organizer['email'],
1242 'phone' => $organizer['phone']
1243 ];
1244 }
1245
1246 break;
1247
1248 case (Entities::PACKAGE):
1249 case (Entities::APPOINTMENTS):
1250 foreach ($entityData['recurring'] as $reservation) {
1251 $usersInfo[$reservation['appointment']['provider']['id']] = [
1252 'id' => $reservation['appointment']['provider']['id'],
1253 'email' => $reservation['appointment']['provider']['email'],
1254 'phone' => $reservation['appointment']['provider']['phone']
1255 ];
1256 }
1257 if (empty($entityData['recurring'])) {
1258 if (!empty($entityData['onlyOneEmployee'])) {
1259 $usersInfo[$entityData['onlyOneEmployee']['id']] = [
1260 'id' => $entityData['onlyOneEmployee']['id'],
1261 'email' => $entityData['onlyOneEmployee']['email'],
1262 'phone' => $entityData['onlyOneEmployee']['phone']
1263 ];
1264 }
1265 $emptyPackageEmployees = $settingsAS->getEmptyPackageEmployees();
1266 if (!empty($emptyPackageEmployees)) {
1267 foreach ($emptyPackageEmployees as $employee) {
1268 $usersInfo[$employee['id']] = [
1269 'id' => $employee['id'],
1270 'email' => $employee['email'],
1271 'phone' => $employee['phone']
1272 ];
1273 }
1274 }
1275 }
1276
1277 break;
1278 }
1279
1280 break;
1281 }
1282
1283 return $usersInfo;
1284 }
1285 }
1286