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