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