PluginProbe ʕ •ᴥ•ʔ
Booking for Appointments and Events Calendar – Amelia / 1.2.31
Booking for Appointments and Events Calendar – Amelia v1.2.31
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 / Infrastructure / Repository / Notification / NotificationLogRepository.php
ameliabooking / src / Infrastructure / Repository / Notification Last commit date
NotificationLogRepository.php 1 year ago NotificationRepository.php 1 year ago NotificationSMSHistoryRepository.php 1 year ago NotificationsToEntitiesRepository.php 1 year ago
NotificationLogRepository.php
1084 lines
1 <?php
2
3 namespace AmeliaBooking\Infrastructure\Repository\Notification;
4
5 use AmeliaBooking\Domain\Collection\Collection;
6 use AmeliaBooking\Domain\Common\Exceptions\InvalidArgumentException;
7 use AmeliaBooking\Domain\Entity\Entities;
8 use AmeliaBooking\Domain\Entity\Notification\Notification;
9 use AmeliaBooking\Domain\Entity\User\AbstractUser;
10 use AmeliaBooking\Domain\Factory\Booking\Appointment\AppointmentFactory;
11 use AmeliaBooking\Domain\Factory\Booking\Event\EventFactory;
12 use AmeliaBooking\Domain\Factory\Notification\NotificationLogFactory;
13 use AmeliaBooking\Domain\Factory\User\UserFactory;
14 use AmeliaBooking\Domain\Services\DateTime\DateTimeService;
15 use AmeliaBooking\Domain\ValueObjects\String\Status;
16 use AmeliaBooking\Infrastructure\Common\Exceptions\QueryExecutionException;
17 use AmeliaBooking\Infrastructure\Connection;
18 use AmeliaBooking\Infrastructure\Repository\AbstractRepository;
19 use AmeliaBooking\Infrastructure\WP\InstallActions\DB\Booking\CustomerBookingsToEventsPeriodsTable;
20 use AmeliaBooking\Infrastructure\WP\InstallActions\DB\Booking\CustomerBookingsToExtrasTable;
21 use AmeliaBooking\Infrastructure\WP\InstallActions\DB\Booking\EventsPeriodsTable;
22 use AmeliaBooking\Infrastructure\WP\InstallActions\DB\Booking\EventsProvidersTable;
23 use AmeliaBooking\Infrastructure\WP\InstallActions\DB\Booking\EventsTable;
24 use AmeliaBooking\Infrastructure\WP\InstallActions\DB\Coupon\CouponsTable;
25 use AmeliaBooking\Infrastructure\WP\InstallActions\DB\Payment\PaymentsTable;
26
27 /**
28 * Class NotificationLogRepository
29 *
30 * @package AmeliaBooking\Infrastructure\Repository\Notification
31 */
32 class NotificationLogRepository extends AbstractRepository
33 {
34 public const FACTORY = NotificationLogFactory::class;
35
36 /** @var string */
37 protected $notificationsTable;
38
39 /** @var string */
40 protected $appointmentsTable;
41
42 /** @var string */
43 protected $bookingsTable;
44
45 /** @var string */
46 protected $usersTable;
47
48 /**
49 * NotificationLogRepository constructor.
50 *
51 * @param Connection $connection
52 * @param string $table
53 * @param string $notificationsTable
54 * @param string $appointmentsTable
55 * @param string $bookingsTable
56 * @param string $usersTable
57 */
58 public function __construct(
59 Connection $connection,
60 $table,
61 $notificationsTable,
62 $appointmentsTable,
63 $bookingsTable,
64 $usersTable
65 ) {
66 parent::__construct($connection, $table);
67 $this->notificationsTable = $notificationsTable;
68 $this->appointmentsTable = $appointmentsTable;
69 $this->bookingsTable = $bookingsTable;
70 $this->usersTable = $usersTable;
71 }
72
73 /**
74 * @param Notification $notification
75 * @param int|null $userId
76 * @param int|null $appointmentId
77 * @param int|null $eventId
78 * @param int|null $packageCustomerId
79 * @param string|null $data
80 *
81 * @return int
82 *
83 * @throws QueryExecutionException
84 * @throws \Exception
85 */
86 public function add($notification, $userId, $appointmentId = null, $eventId = null, $packageCustomerId = null, $data = null)
87 {
88 $notificationData = $notification->toArray();
89
90 $params = [
91 ':notificationId' => $notificationData['id'],
92 ':userId' => $userId,
93 ':appointmentId' => $appointmentId,
94 ':packageCustomerId' => $packageCustomerId,
95 ':eventId' => $eventId,
96 ':sentDateTime' => DateTimeService::getNowDateTimeInUtc(),
97 ':data' => $data,
98 ];
99
100 try {
101 $statement = $this->connection->prepare(
102 "INSERT INTO {$this->table}
103 (`notificationId`, `userId`, `appointmentId`, `eventId`, `packageCustomerId`, `sentDateTime`, `sent`, `data`)
104 VALUES (:notificationId, :userId, :appointmentId, :eventId, :packageCustomerId, :sentDateTime, 0, :data)"
105 );
106
107 $res = $statement->execute($params);
108
109 if (!$res) {
110 throw new QueryExecutionException('Unable to add data in ' . __CLASS__);
111 }
112
113 return $this->connection->lastInsertId();
114 } catch (\Exception $e) {
115 throw new QueryExecutionException('Unable to add data in ' . __CLASS__, $e->getCode(), $e);
116 }
117 }
118
119 /**
120 * @param int $entityId
121 * @param string $entityType
122 * @param int $userId
123 * @param array $notificationsIds
124 *
125 * @return void
126 * @throws QueryExecutionException
127 */
128 public function invalidateSentEmails($entityId, $entityType, $userId, $notificationsIds)
129 {
130 if (empty($notificationsIds)) {
131 return;
132 }
133
134 $params = [
135 ":$entityType" . 'Id' => $entityId,
136 ];
137
138 $userQuery = '';
139
140 if ($userId) {
141 $params[':userId'] = $userId;
142
143 $userQuery = ' AND userId = :userId';
144 }
145
146 $queryNotificationsIds = [];
147
148 foreach ($notificationsIds as $index => $value) {
149 $param = ':notificationId' . $index;
150
151 $queryNotificationsIds[] = $param;
152
153 $params[$param] = $value;
154 }
155
156 try {
157 $statement = $this->connection->prepare(
158 "UPDATE {$this->table} SET
159 `sent` = -1
160 WHERE
161 {$entityType}Id = :{$entityType}Id
162 AND notificationId IN (" . implode(', ', $queryNotificationsIds) . ')'
163 . $userQuery
164 );
165
166 $res = $statement->execute($params);
167
168 if (!$res) {
169 throw new QueryExecutionException('Unable to save data in ' . __CLASS__);
170 }
171 } catch (\Exception $e) {
172 throw new QueryExecutionException('Unable to save data in ' . __CLASS__, $e->getCode(), $e);
173 }
174 }
175
176 /**
177 * Return a collection of tomorrow appointments where customer notification is not sent and should be.
178 *
179 * @param int $notificationId
180 * @param bool $nextDay
181 * @param array $statuses
182 *
183 * @return Collection
184 *
185 * @throws InvalidArgumentException
186 * @throws QueryExecutionException
187 * @throws \Exception
188 */
189 public function getCustomersNextDayAppointments($notificationId, $nextDay = true, $statuses = [])
190 {
191 $couponsTable = CouponsTable::getTableName();
192
193 $customerBookingsExtrasTable = CustomerBookingsToExtrasTable::getTableName();
194
195 $paymentsTable = PaymentsTable::getTableName();
196
197 $startDate = DateTimeService::getCustomDateTimeObjectInUtc(
198 DateTimeService::getNowDateTimeObject()->setTime(0, 0, 0)->format('Y-m-d H:i:s')
199 );
200
201 $endDate = DateTimeService::getCustomDateTimeObjectInUtc(
202 DateTimeService::getNowDateTimeObject()->setTime(23, 59, 59)->format('Y-m-d H:i:s')
203 );
204
205 if ($nextDay) {
206 $startDate = $startDate->modify('+1 day');
207 $endDate = $endDate->modify('+1 day');
208 }
209
210 $startCurrentDate = "STR_TO_DATE('" . $startDate->format('Y-m-d H:i:s') . "', '%Y-%m-%d %H:%i:%s')";
211
212 $endCurrentDate = "STR_TO_DATE('" . $endDate->format('Y-m-d H:i:s') . "', '%Y-%m-%d %H:%i:%s')";
213
214 $whereStatuses = [];
215
216 foreach ($statuses as $key => $status) {
217 $whereStatuses[] = "cb.status = '$status'";
218 }
219
220 $whereStatuses = $whereStatuses ? 'AND (' . implode(' OR ', $whereStatuses) . ')' : '';
221
222 try {
223 $statement = $this->connection->query(
224 "SELECT
225 a.id AS appointment_id,
226 a.bookingStart AS appointment_bookingStart,
227 a.bookingEnd AS appointment_bookingEnd,
228 a.notifyParticipants AS appointment_notifyParticipants,
229 a.createPaymentLinks AS appointment_createPaymentLinks,
230 a.serviceId AS appointment_serviceId,
231 a.providerId AS appointment_providerId,
232 a.locationId AS appointment_locationId,
233 a.internalNotes AS appointment_internalNotes,
234 a.status AS appointment_status,
235 a.zoomMeeting AS appointment_zoom_meeting,
236 a.lessonSpace AS appointment_lesson_space,
237 a.googleMeetUrl AS appointment_google_meet_url,
238 a.microsoftTeamsUrl AS appointment_microsoft_teams_url,
239
240 cb.id AS booking_id,
241 cb.customerId AS booking_customerId,
242 cb.status AS booking_status,
243 cb.price AS booking_price,
244 cb.customFields AS booking_customFields,
245 cb.info AS booking_info,
246 cb.utcOffset AS booking_utcOffset,
247 cb.aggregatedPrice AS booking_aggregatedPrice,
248 cb.persons AS booking_persons,
249 cb.duration AS booking_duration,
250 cb.created AS booking_created,
251
252 p.id AS payment_id,
253 p.amount AS payment_amount,
254 p.dateTime AS payment_dateTime,
255 p.status AS payment_status,
256 p.gateway AS payment_gateway,
257 p.gatewayTitle AS payment_gatewayTitle,
258 p.data AS payment_data,
259
260 cbe.id AS bookingExtra_id,
261 cbe.extraId AS bookingExtra_extraId,
262 cbe.customerBookingId AS bookingExtra_customerBookingId,
263 cbe.quantity AS bookingExtra_quantity,
264 cbe.price AS bookingExtra_price,
265 cbe.aggregatedPrice AS bookingExtra_aggregatedPrice,
266
267 c.id AS coupon_id,
268 c.code AS coupon_code,
269 c.discount AS coupon_discount,
270 c.deduction AS coupon_deduction,
271 c.limit AS coupon_limit,
272 c.customerLimit AS coupon_customerLimit,
273 c.status AS coupon_status
274 FROM {$this->appointmentsTable} a
275 INNER JOIN {$this->bookingsTable} cb ON cb.appointmentId = a.id
276 LEFT JOIN {$paymentsTable} p ON p.customerBookingId = cb.id
277 LEFT JOIN {$customerBookingsExtrasTable} cbe ON cbe.customerBookingId = cb.id
278 LEFT JOIN {$couponsTable} c ON c.id = cb.couponId
279 WHERE a.bookingStart BETWEEN $startCurrentDate AND $endCurrentDate
280 {$whereStatuses}
281 AND a.notifyParticipants = 1 AND
282 a.id NOT IN (
283 SELECT nl.appointmentId
284 FROM {$this->table} nl
285 INNER JOIN {$this->notificationsTable} n ON nl.notificationId = n.id
286 WHERE n.id = {$notificationId} AND (nl.sent IS NULL OR nl.sent = 1) AND nl.appointmentId IS NOT NULL
287 )"
288 );
289
290 $rows = $statement->fetchAll();
291 } catch (\Exception $e) {
292 throw new QueryExecutionException('Unable to find appointments in ' . __CLASS__, $e->getCode(), $e);
293 }
294
295 return AppointmentFactory::createCollection($rows);
296 }
297
298 /**
299 * Return a collection of tomorrow events where customer notification is not sent and should be.
300 *
301 * @param $notificationId
302 *
303 * @return Collection
304 *
305 * @throws InvalidArgumentException
306 * @throws QueryExecutionException
307 * @throws \Exception
308 */
309 public function getCustomersNextDayEvents($notificationId, $nextDay = true)
310 {
311 $couponsTable = CouponsTable::getTableName();
312 $paymentsTable = PaymentsTable::getTableName();
313 $eventsTable = EventsTable::getTableName();
314
315 $eventsPeriodsTable = EventsPeriodsTable::getTableName();
316
317 $customerBookingsEventsPeriods = CustomerBookingsToEventsPeriodsTable::getTableName();
318
319 $eventsProvidersTable = EventsProvidersTable::getTableName();
320
321 $startDate = DateTimeService::getCustomDateTimeObjectInUtc(
322 DateTimeService::getNowDateTimeObject()->setTime(0, 0, 0)->format('Y-m-d H:i:s')
323 );
324 $endDate = DateTimeService::getCustomDateTimeObjectInUtc(
325 DateTimeService::getNowDateTimeObject()->setTime(23, 59, 59)->format('Y-m-d H:i:s')
326 );
327
328 if ($nextDay) {
329 $startDate = $startDate->modify('+1 day');
330 $endDate = $endDate->modify('+1 day');
331 }
332
333 $startCurrentDate = "STR_TO_DATE('" . $startDate->format('Y-m-d H:i:s') . "', '%Y-%m-%d %H:%i:%s')";
334 $endCurrentDate = "STR_TO_DATE('" . $endDate->format('Y-m-d H:i:s') . "', '%Y-%m-%d %H:%i:%s')";
335
336 try {
337 $statement = $this->connection->query(
338 "SELECT
339 e.id AS event_id,
340 e.name AS event_name,
341 e.status AS event_status,
342 e.bookingOpens AS event_bookingOpens,
343 e.bookingCloses AS event_bookingCloses,
344 e.recurringCycle AS event_recurringCycle,
345 e.recurringOrder AS event_recurringOrder,
346 e.recurringUntil AS event_recurringUntil,
347 e.maxCapacity AS event_maxCapacity,
348 e.price AS event_price,
349 e.description AS event_description,
350 e.color AS event_color,
351 e.show AS event_show,
352 e.locationId AS event_locationId,
353 e.customLocation AS event_customLocation,
354 e.parentId AS event_parentId,
355 e.created AS event_created,
356 e.notifyParticipants AS event_notifyParticipants,
357 e.zoomUserId AS event_zoomUserId,
358 e.deposit AS event_deposit,
359 e.depositPayment AS event_depositPayment,
360 e.depositPerPerson AS event_depositPerPerson,
361 e.organizerId AS event_organizerId,
362
363 ep.id AS event_periodId,
364 ep.periodStart AS event_periodStart,
365 ep.periodEnd AS event_periodEnd,
366 ep.zoomMeeting AS event_periodZoomMeeting,
367 ep.lessonSpace AS event_periodLessonSpace,
368 ep.googleMeetUrl AS event_googleMeetUrl,
369
370 cb.id AS booking_id,
371 cb.customerId AS booking_customerId,
372 cb.status AS booking_status,
373 cb.price AS booking_price,
374 cb.customFields AS booking_customFields,
375 cb.info AS booking_info,
376 cb.utcOffset AS booking_utcOffset,
377 cb.aggregatedPrice AS booking_aggregatedPrice,
378 cb.persons AS booking_persons,
379 cb.created AS booking_created,
380
381 p.id AS payment_id,
382 p.amount AS payment_amount,
383 p.dateTime AS payment_dateTime,
384 p.status AS payment_status,
385 p.gateway AS payment_gateway,
386 p.gatewayTitle AS payment_gatewayTitle,
387 p.data AS payment_data,
388
389 pu.id AS provider_id,
390 pu.firstName AS provider_firstName,
391 pu.lastName AS provider_lastName,
392 pu.email AS provider_email,
393 pu.note AS provider_note,
394 pu.description AS provider_description,
395 pu.phone AS provider_phone,
396 pu.gender AS provider_gender,
397 pu.pictureFullPath AS provider_pictureFullPath,
398 pu.pictureThumbPath AS provider_pictureThumbPath,
399 pu.translations AS provider_translations,
400
401 c.id AS coupon_id,
402 c.code AS coupon_code,
403 c.discount AS coupon_discount,
404 c.deduction AS coupon_deduction,
405 c.limit AS coupon_limit,
406 c.customerLimit AS coupon_customerLimit,
407 c.status AS coupon_status
408 FROM {$eventsTable} e
409 INNER JOIN {$eventsPeriodsTable} ep ON ep.eventId = e.id
410 INNER JOIN {$customerBookingsEventsPeriods} cbe ON cbe.eventPeriodId = ep.id
411 INNER JOIN {$this->bookingsTable} cb ON cb.id = cbe.customerBookingId
412 LEFT JOIN {$paymentsTable} p ON p.customerBookingId = cb.id
413 LEFT JOIN {$eventsProvidersTable} epr ON epr.eventId = e.id
414 LEFT JOIN {$this->usersTable} pu ON pu.id = epr.userId
415 LEFT JOIN {$couponsTable} c ON c.id = cb.couponId
416 WHERE ep.periodStart BETWEEN {$startCurrentDate} AND {$endCurrentDate}
417 AND cb.status = 'approved'
418 AND e.status = 'approved'
419 AND e.notifyParticipants = 1 AND
420 e.id NOT IN (
421 SELECT nl.eventId
422 FROM {$this->table} nl
423 INNER JOIN {$this->notificationsTable} n ON nl.notificationId = n.id
424 WHERE n.id = {$notificationId} AND (nl.sent IS NULL OR nl.sent = 1) AND nl.eventId IS NOT NULL
425 )"
426 );
427
428 $rows = $statement->fetchAll();
429 } catch (\Exception $e) {
430 throw new QueryExecutionException('Unable to find appointments in ' . __CLASS__, $e->getCode(), $e);
431 }
432
433 return EventFactory::createCollection($rows);
434 }
435
436 /**
437 * Return a collection of tomorrow appointments where provider notification is not sent and should be.
438 *
439 * @param int $notificationId
440 * @param bool $nextDay
441 * @param array $statuses
442 *
443 * @return Collection
444 * @throws InvalidArgumentException
445 * @throws QueryExecutionException
446 * @throws \Exception
447 */
448 public function getProvidersNextDayAppointments($notificationId, $nextDay, $statuses)
449 {
450 $couponsTable = CouponsTable::getTableName();
451
452 $customerBookingsExtrasTable = CustomerBookingsToExtrasTable::getTableName();
453
454 $paymentsTable = PaymentsTable::getTableName();
455
456 $startDate = DateTimeService::getCustomDateTimeObjectInUtc(
457 DateTimeService::getNowDateTimeObject()->setTime(0, 0, 0)->format('Y-m-d H:i:s')
458 );
459
460 $endDate = DateTimeService::getCustomDateTimeObjectInUtc(
461 DateTimeService::getNowDateTimeObject()->setTime(23, 59, 59)->format('Y-m-d H:i:s')
462 );
463
464 if ($nextDay) {
465 $startDate = $startDate->modify('+1 day');
466 $endDate = $endDate->modify('+1 day');
467 }
468
469 $startCurrentDate = "STR_TO_DATE('" . $startDate->format('Y-m-d H:i:s') . "', '%Y-%m-%d %H:%i:%s')";
470
471 $endCurrentDate = "STR_TO_DATE('" . $endDate->format('Y-m-d H:i:s') . "', '%Y-%m-%d %H:%i:%s')";
472
473 $whereStatuses = [];
474
475 foreach ($statuses as $key => $status) {
476 $whereStatuses[] = "cb.status = '$status'";
477 }
478
479 $whereStatuses = $whereStatuses ? 'AND (' . implode(' OR ', $whereStatuses) . ')' : '';
480
481 try {
482 $statement = $this->connection->query(
483 "SELECT
484 a.id AS appointment_id,
485 a.bookingStart AS appointment_bookingStart,
486 a.bookingEnd AS appointment_bookingEnd,
487 a.notifyParticipants AS appointment_notifyParticipants,
488 a.serviceId AS appointment_serviceId,
489 a.providerId AS appointment_providerId,
490 a.locationId AS appointment_locationId,
491 a.internalNotes AS appointment_internalNotes,
492 a.status AS appointment_status,
493 a.zoomMeeting AS appointment_zoom_meeting,
494 a.lessonSpace AS appointment_lesson_space,
495 a.googleMeetUrl AS appointment_google_meet_url,
496 a.microsoftTeamsUrl AS appointment_microsoft_teams_url,
497
498 cb.id AS booking_id,
499 cb.customerId AS booking_customerId,
500 cb.status AS booking_status,
501 cb.price AS booking_price,
502 cb.customFields AS booking_customFields,
503 cb.persons AS booking_persons,
504 cb.aggregatedPrice AS booking_aggregatedPrice,
505 cb.duration AS booking_duration,
506 cb.created AS booking_created,
507
508 p.id AS payment_id,
509 p.amount AS payment_amount,
510 p.dateTime AS payment_dateTime,
511 p.status AS payment_status,
512 p.gateway AS payment_gateway,
513 p.gatewayTitle AS payment_gatewayTitle,
514 p.data AS payment_data,
515
516 cbe.id AS bookingExtra_id,
517 cbe.extraId AS bookingExtra_extraId,
518 cbe.customerBookingId AS bookingExtra_customerBookingId,
519 cbe.quantity AS bookingExtra_quantity,
520 cbe.price AS bookingExtra_price,
521 cbe.aggregatedPrice AS bookingExtra_aggregatedPrice,
522
523 c.id AS coupon_id,
524 c.code AS coupon_code,
525 c.discount AS coupon_discount,
526 c.deduction AS coupon_deduction,
527 c.limit AS coupon_limit,
528 c.customerLimit AS coupon_customerLimit,
529 c.status AS coupon_status
530 FROM {$this->appointmentsTable} a
531 INNER JOIN {$this->bookingsTable} cb ON cb.appointmentId = a.id
532 LEFT JOIN {$paymentsTable} p ON p.customerBookingId = cb.id
533 LEFT JOIN {$customerBookingsExtrasTable} cbe ON cbe.customerBookingId = cb.id
534 LEFT JOIN {$couponsTable} c ON c.id = cb.couponId
535 WHERE a.bookingStart BETWEEN $startCurrentDate AND $endCurrentDate
536 {$whereStatuses}
537 AND a.id NOT IN (
538 SELECT nl.appointmentId
539 FROM {$this->table} nl
540 INNER JOIN {$this->notificationsTable} n ON nl.notificationId = n.id
541 WHERE n.id = {$notificationId} AND (nl.sent IS NULL OR nl.sent = 1) AND nl.appointmentId IS NOT NULL
542 )"
543 );
544
545 $rows = $statement->fetchAll();
546 } catch (\Exception $e) {
547 throw new QueryExecutionException('Unable to find appointments in ' . __CLASS__, $e->getCode(), $e);
548 }
549
550 return AppointmentFactory::createCollection($rows);
551 }
552
553 /**
554 * Return a collection of tomorrow events where provider notification is not sent and should be.
555 *
556 * @param $notificationId
557 *
558 * @return Collection
559 * @throws InvalidArgumentException
560 * @throws QueryExecutionException
561 * @throws \Exception
562 */
563 public function getProvidersNextDayEvents($notificationId, $nextDay)
564 {
565 $couponsTable = CouponsTable::getTableName();
566 $eventsTable = EventsTable::getTableName();
567 $eventsPeriodsTable = EventsPeriodsTable::getTableName();
568 $customerBookingsEventsPeriods = CustomerBookingsToEventsPeriodsTable::getTableName();
569 $eventsProvidersTable = EventsProvidersTable::getTableName();
570 $paymentsTable = PaymentsTable::getTableName();
571
572 $startDate = DateTimeService::getCustomDateTimeObjectInUtc(
573 DateTimeService::getNowDateTimeObject()->setTime(0, 0, 0)->format('Y-m-d H:i:s')
574 );
575 $endDate = DateTimeService::getCustomDateTimeObjectInUtc(
576 DateTimeService::getNowDateTimeObject()->setTime(23, 59, 59)->format('Y-m-d H:i:s')
577 );
578
579 if ($nextDay) {
580 $startDate = $startDate->modify('+1 day');
581 $endDate = $endDate->modify('+1 day');
582 }
583
584 $startCurrentDate = "STR_TO_DATE('" . $startDate->format('Y-m-d H:i:s') . "', '%Y-%m-%d %H:%i:%s')";
585 $endCurrentDate = "STR_TO_DATE('" . $endDate->format('Y-m-d H:i:s') . "', '%Y-%m-%d %H:%i:%s')";
586
587
588 try {
589 $statement = $this->connection->query(
590 "SELECT
591 e.id AS event_id,
592 e.name AS event_name,
593 e.status AS event_status,
594 e.bookingOpens AS event_bookingOpens,
595 e.bookingCloses AS event_bookingCloses,
596 e.recurringCycle AS event_recurringCycle,
597 e.recurringOrder AS event_recurringOrder,
598 e.recurringUntil AS event_recurringUntil,
599 e.maxCapacity AS event_maxCapacity,
600 e.price AS event_price,
601 e.description AS event_description,
602 e.color AS event_color,
603 e.show AS event_show,
604 e.locationId AS event_locationId,
605 e.customLocation AS event_customLocation,
606 e.parentId AS event_parentId,
607 e.created AS event_created,
608 e.notifyParticipants AS event_notifyParticipants,
609 e.zoomUserId AS event_zoomUserId,
610 e.deposit AS event_deposit,
611 e.depositPayment AS event_depositPayment,
612 e.depositPerPerson AS event_depositPerPerson,
613 e.organizerId AS event_organizerId,
614
615 ep.id AS event_periodId,
616 ep.periodStart AS event_periodStart,
617 ep.periodEnd AS event_periodEnd,
618 ep.zoomMeeting AS event_periodZoomMeeting,
619 ep.lessonSpace AS event_periodLessonSpace,
620 ep.googleMeetUrl AS event_googleMeetUrl,
621
622 pu.id AS provider_id,
623 pu.firstName AS provider_firstName,
624 pu.lastName AS provider_lastName,
625 pu.email AS provider_email,
626 pu.note AS provider_note,
627 pu.description AS provider_description,
628 pu.phone AS provider_phone,
629 pu.gender AS provider_gender,
630 pu.pictureFullPath AS provider_pictureFullPath,
631 pu.pictureThumbPath AS provider_pictureThumbPath,
632 pu.timeZone AS provider_timeZone,
633
634 cb.id AS booking_id,
635 cb.customerId AS booking_customerId,
636 cb.status AS booking_status,
637 cb.price AS booking_price,
638 cb.customFields AS booking_customFields,
639 cb.persons AS booking_persons,
640 cb.created AS booking_created,
641
642 p.id AS payment_id,
643 p.amount AS payment_amount,
644 p.dateTime AS payment_dateTime,
645 p.status AS payment_status,
646 p.gateway AS payment_gateway,
647 p.gatewayTitle AS payment_gatewayTitle,
648 p.data AS payment_data,
649
650 c.id AS coupon_id,
651 c.code AS coupon_code,
652 c.discount AS coupon_discount,
653 c.deduction AS coupon_deduction,
654 c.limit AS coupon_limit,
655 c.customerLimit AS coupon_customerLimit,
656 c.status AS coupon_status
657 FROM {$eventsTable} e
658 INNER JOIN {$eventsPeriodsTable} ep ON ep.eventId = e.id
659 INNER JOIN {$customerBookingsEventsPeriods} cbe ON cbe.eventPeriodId = ep.id
660 INNER JOIN {$this->bookingsTable} cb ON cb.id = cbe.customerBookingId
661 LEFT JOIN {$paymentsTable} p ON p.customerBookingId = cb.id
662 LEFT JOIN {$couponsTable} c ON c.id = cb.couponId
663 LEFT JOIN {$eventsProvidersTable} epr ON epr.eventId = e.id
664 LEFT JOIN {$this->usersTable} pu ON pu.id = epr.userId
665 WHERE ep.periodStart BETWEEN {$startCurrentDate} AND {$endCurrentDate}
666 AND cb.status = 'approved'
667 AND e.status = 'approved'
668 AND e.id NOT IN (
669 SELECT nl.eventId
670 FROM {$this->table} nl
671 INNER JOIN {$this->notificationsTable} n ON nl.notificationId = n.id
672 WHERE n.id = {$notificationId} AND (nl.sent IS NULL OR nl.sent = 1) AND nl.eventId IS NOT NULL
673 )"
674 );
675
676 $rows = $statement->fetchAll();
677 } catch (\Exception $e) {
678 throw new QueryExecutionException('Unable to find events in ' . __CLASS__, $e->getCode(), $e);
679 }
680
681 return EventFactory::createCollection($rows);
682 }
683
684 /**
685 * Return a collection of today's past appointments where follow up notification is not sent and should be.
686 *
687 * @param Notification $notification
688 * @param array $statuses
689 *
690 * @return Collection
691 * @throws InvalidArgumentException
692 * @throws QueryExecutionException
693 */
694 public function getScheduledAppointments($notification, $statuses = [])
695 {
696 $couponsTable = CouponsTable::getTableName();
697 $customerBookingsExtrasTable = CustomerBookingsToExtrasTable::getTableName();
698 $paymentsTable = PaymentsTable::getTableName();
699
700 try {
701 $currentDateTime = "STR_TO_DATE('" . DateTimeService::getNowDateTimeInUtc() . "', '%Y-%m-%d %H:%i:%s')";
702
703 $where = '';
704 if ($notification->getTimeAfter()) {
705 $timeAfter =
706 apply_filters(
707 'amelia_modify_scheduled_notification_time_after',
708 $notification->getTimeAfter()->getValue(),
709 $notification->toArray()
710 );
711 $lastTime = apply_filters('amelia_modify_scheduled_notification_last_time', $timeAfter + 259200, $notification->toArray());
712
713 $where =
714 "{$currentDateTime} BETWEEN DATE_ADD(a.bookingEnd, INTERVAL {$timeAfter} SECOND) AND DATE_ADD(a.bookingEnd, INTERVAL {$lastTime} SECOND)";
715 } elseif ($notification->getTimeBefore()) {
716 $timeBefore =
717 apply_filters(
718 'amelia_modify_scheduled_notification_time_before',
719 $notification->getTimeBefore()->getValue(),
720 $notification->toArray()
721 );
722 $where =
723 "({$currentDateTime} BETWEEN
724 DATE_SUB(a.bookingStart, INTERVAL {$timeBefore} SECOND) AND a.bookingStart) AND
725 (a.bookingStart >= DATE_ADD(cb.created, INTERVAL {$timeBefore} SECOND))";
726 }
727
728 $whereStatuses = [];
729
730 foreach ($statuses as $key => $status) {
731 $whereStatuses[] = "cb.status = '$status'";
732 }
733
734 $whereStatuses = $whereStatuses ? ($where ? ' AND ' : '') . '(' . implode(' OR ', $whereStatuses) . ')' : '';
735
736
737 $statement = $this->connection->query(
738 "SELECT
739 a.id AS appointment_id,
740 a.bookingStart AS appointment_bookingStart,
741 a.bookingEnd AS appointment_bookingEnd,
742 a.notifyParticipants AS appointment_notifyParticipants,
743 a.createPaymentLinks AS appointment_createPaymentLinks,
744 a.serviceId AS appointment_serviceId,
745 a.providerId AS appointment_providerId,
746 a.locationId AS appointment_locationId,
747 a.internalNotes AS appointment_internalNotes,
748 a.status AS appointment_status,
749 a.googleMeetUrl AS appointment_google_meet_url,
750 a.lessonSpace AS appointment_lesson_space,
751 a.zoomMeeting AS appointment_zoom_meeting,
752 a.microsoftTeamsUrl AS appointment_microsoft_teams_url,
753
754 cb.id AS booking_id,
755 cb.customerId AS booking_customerId,
756 cb.status AS booking_status,
757 cb.price AS booking_price,
758 cb.customFields AS booking_customFields,
759 cb.info AS booking_info,
760 cb.utcOffset AS booking_utcOffset,
761 cb.aggregatedPrice AS booking_aggregatedPrice,
762 cb.persons AS booking_persons,
763 cb.duration AS booking_duration,
764 cb.created AS booking_created,
765
766 p.id AS payment_id,
767 p.amount AS payment_amount,
768 p.dateTime AS payment_dateTime,
769 p.status AS payment_status,
770 p.gateway AS payment_gateway,
771 p.gatewayTitle AS payment_gatewayTitle,
772 p.data AS payment_data,
773
774 cbe.id AS bookingExtra_id,
775 cbe.extraId AS bookingExtra_extraId,
776 cbe.customerBookingId AS bookingExtra_customerBookingId,
777 cbe.quantity AS bookingExtra_quantity,
778 cbe.price AS bookingExtra_price,
779 cbe.aggregatedPrice AS bookingExtra_aggregatedPrice,
780
781 c.id AS coupon_id,
782 c.code AS coupon_code,
783 c.discount AS coupon_discount,
784 c.deduction AS coupon_deduction,
785 c.limit AS coupon_limit,
786 c.customerLimit AS coupon_customerLimit,
787 c.status AS coupon_status
788 FROM {$this->appointmentsTable} a
789 INNER JOIN {$this->bookingsTable} cb ON cb.appointmentId = a.id
790 LEFT JOIN {$paymentsTable} p ON p.customerBookingId = cb.id
791 LEFT JOIN {$customerBookingsExtrasTable} cbe ON cbe.customerBookingId = cb.id
792 LEFT JOIN {$couponsTable} c ON c.id = cb.couponId
793 WHERE {$where}
794 AND a.notifyParticipants = 1
795 {$whereStatuses}
796 AND a.id NOT IN (
797 SELECT nl.appointmentId
798 FROM {$this->table} nl
799 INNER JOIN {$this->notificationsTable} n ON nl.notificationId = n.id
800 WHERE n.id = {$notification->getId()->getValue()} AND (nl.sent IS NULL OR nl.sent = 1) AND nl.appointmentId IS NOT NULL
801 )"
802 );
803
804 $rows = $statement->fetchAll();
805 } catch (\Exception $e) {
806 throw new QueryExecutionException('Unable to find appointments in ' . __CLASS__, $e->getCode(), $e);
807 }
808
809 return AppointmentFactory::createCollection($rows);
810 }
811
812 /**
813 * Return a collection of today's past appointments where follow-up notification is not sent and should be.
814 *
815 * @param Notification $notification
816 *
817 * @return Collection
818 * @throws InvalidArgumentException
819 * @throws QueryExecutionException
820 */
821 public function getScheduledEvents($notification)
822 {
823 $eventsTable = EventsTable::getTableName();
824
825 $eventsPeriodsTable = EventsPeriodsTable::getTableName();
826
827 $customerBookingsEventsPeriods = CustomerBookingsToEventsPeriodsTable::getTableName();
828
829 $paymentsTable = PaymentsTable::getTableName();
830
831 $currentDateTime = "STR_TO_DATE('" . DateTimeService::getNowDateTimeInUtc() . "', '%Y-%m-%d %H:%i:%s')";
832
833 $where = "WHERE e.notifyParticipants = 1
834 AND cb.status = 'approved'
835 AND e.status = 'approved'
836 AND e.id NOT IN (
837 SELECT nl.eventId
838 FROM {$this->table} nl
839 INNER JOIN {$this->notificationsTable} n ON nl.notificationId = n.id
840 WHERE n.id = {$notification->getId()->getValue()} AND (nl.sent IS NULL OR nl.sent = 1) AND nl.eventId IS NOT NULL
841 )";
842
843 if ($notification->getTimeAfter()) {
844 $timeAfter = $notification->getTimeAfter()->getValue();
845
846 $lastTime = $timeAfter + 432000;
847
848 $where .=
849 " AND {$currentDateTime} BETWEEN DATE_ADD(ep.periodEnd, INTERVAL {$timeAfter} SECOND)
850 AND DATE_ADD(ep.periodEnd, INTERVAL {$lastTime} SECOND)";
851 } elseif ($notification->getTimeBefore()) {
852 $timeBefore = $notification->getTimeBefore()->getValue();
853
854 $where .=
855 " AND ({$currentDateTime} BETWEEN DATE_SUB(ep.periodStart, INTERVAL {$timeBefore} SECOND) AND ep.periodStart)
856 AND (ep.periodStart >= DATE_ADD(p.created, INTERVAL {$timeBefore} SECOND))";
857 }
858
859 try {
860 $statement = $this->connection->query(
861 "SELECT
862 e.id AS event_id,
863 e.name AS event_name,
864 e.status AS event_status,
865 e.bookingOpens AS event_bookingOpens,
866 e.bookingCloses AS event_bookingCloses,
867 e.recurringCycle AS event_recurringCycle,
868 e.recurringOrder AS event_recurringOrder,
869 e.recurringUntil AS event_recurringUntil,
870 e.recurringInterval AS event_recurringInterval,
871 e.bringingAnyone AS event_bringingAnyone,
872 e.bookMultipleTimes AS event_bookMultipleTimes,
873 e.maxCapacity AS event_maxCapacity,
874 e.price AS event_price,
875 e.description AS event_description,
876 e.color AS event_color,
877 e.show AS event_show,
878 e.locationId AS event_locationId,
879 e.customLocation AS event_customLocation,
880 e.parentId AS event_parentId,
881 e.created AS event_created,
882 e.notifyParticipants AS event_notifyParticipants,
883 e.zoomUserId AS event_zoomUserId,
884 e.deposit AS event_deposit,
885 e.depositPayment AS event_depositPayment,
886 e.depositPerPerson AS event_depositPerPerson,
887 e.organizerId AS event_organizerId,
888
889 ep.id AS event_periodId,
890 ep.periodStart AS event_periodStart,
891 ep.periodEnd AS event_periodEnd,
892 ep.lessonSpace AS event_periodLessonSpace,
893 ep.zoomMeeting AS event_periodZoomMeeting,
894 ep.googleMeetUrl AS event_googleMeetUrl,
895
896 cb.id AS booking_id,
897 cb.customerId AS booking_customerId,
898 cb.status AS booking_status,
899 cb.price AS booking_price,
900 cb.customFields AS booking_customFields,
901 cb.info AS booking_info,
902 cb.utcOffset AS booking_utcOffset,
903 cb.aggregatedPrice AS booking_aggregatedPrice,
904 cb.persons AS booking_persons,
905 cb.duration AS booking_duration,
906 cb.created AS booking_created
907 FROM {$eventsTable} e
908 INNER JOIN {$eventsPeriodsTable} ep ON ep.eventId = e.id
909 INNER JOIN {$customerBookingsEventsPeriods} cbe ON cbe.eventPeriodId = ep.id
910 INNER JOIN {$this->bookingsTable} cb ON cb.id = cbe.customerBookingId
911 LEFT JOIN {$paymentsTable} p ON p.customerBookingId = cb.id
912 {$where}"
913 );
914
915 $rows = $statement->fetchAll();
916 } catch (\Exception $e) {
917 throw new QueryExecutionException('Unable to find events in ' . __CLASS__, $e->getCode(), $e);
918 }
919
920 return EventFactory::createCollection($rows);
921 }
922
923 /**
924 * Returns a collection of customers that have birthday on today's date and where notification is not sent
925 *
926 * @param $notificationType
927 *
928 * @return Collection
929 * @throws InvalidArgumentException
930 * @throws QueryExecutionException
931 * @throws \Exception
932 */
933 public function getBirthdayCustomers($notificationType)
934 {
935 $currentDate = "STR_TO_DATE('" . DateTimeService::getNowDateTimeInUtc() . "', '%Y-%m-%d')";
936
937 $params = [
938 ':type' => AbstractUser::USER_ROLE_CUSTOMER,
939 ':statusVisible' => Status::VISIBLE,
940 ];
941
942 try {
943 $statement = $this->connection->prepare(
944 "SELECT * FROM {$this->usersTable} as u
945 WHERE
946 u.type = :type AND
947 u.status = :statusVisible AND
948 MONTH(u.birthday) = MONTH({$currentDate}) AND
949 DAY(u.birthday) = DAY({$currentDate}) AND
950 u.id NOT IN (
951 SELECT nl.userID
952 FROM {$this->table} nl
953 INNER JOIN {$this->notificationsTable} n ON nl.notificationId = n.id
954 WHERE n.name = 'customer_birthday_greeting' AND n.type = '{$notificationType}' AND
955 YEAR(nl.sentDateTime) = YEAR({$currentDate}) AND (nl.sent IS NULL OR nl.sent = 1)
956 )"
957 );
958
959 $statement->execute($params);
960
961 $rows = $statement->fetchAll();
962 } catch (\Exception $e) {
963 throw new QueryExecutionException('Unable to get data from ' . __CLASS__, $e->getCode(), $e);
964 }
965
966 $items = [];
967 foreach ($rows as $row) {
968 $items[] = call_user_func([UserFactory::class, 'create'], $row);
969 }
970
971 return new Collection($items);
972 }
973
974 /**
975 * Returns a collection of undelivered notifications
976 *
977 * @param string $type
978 *
979 * @return Collection
980 * @throws InvalidArgumentException
981 * @throws QueryExecutionException
982 */
983 public function getUndeliveredNotifications($type)
984 {
985 $params = [
986 ':type' => $type,
987 ];
988
989 $currentDateTime = "STR_TO_DATE('" . DateTimeService::getNowDateTimeInUtc() . "', '%Y-%m-%d %H:%i:%s')";
990
991 $pastDateTime =
992 "STR_TO_DATE('" .
993 DateTimeService::getNowDateTimeObjectInUtc()->modify('-1 day')->format('Y-m-d H:i:s') .
994 "', '%Y-%m-%d %H:%i:%s')";
995
996 try {
997 $statement = $this->connection->prepare(
998 "SELECT nl.* FROM {$this->table} nl
999 INNER JOIN {$this->notificationsTable} n ON nl.notificationId = n.id
1000 WHERE
1001 nl.sent = 0 AND
1002 {$currentDateTime} > DATE_ADD(nl.sentDateTime, INTERVAL 300 SECOND) AND
1003 {$pastDateTime} < nl.sentDateTime AND
1004 nl.data IS NOT NULL AND
1005 n.type = :type"
1006 );
1007
1008 $statement->execute($params);
1009
1010 $rows = $statement->fetchAll();
1011 } catch (\Exception $e) {
1012 throw new QueryExecutionException('Unable to get data from ' . __CLASS__, $e->getCode(), $e);
1013 }
1014
1015 $items = [];
1016
1017 foreach ($rows as $row) {
1018 $items[] = call_user_func([static::FACTORY, 'create'], $row);
1019 }
1020
1021 return new Collection($items);
1022 }
1023
1024 /**
1025 * @param int $userId
1026 * @param string $type
1027 * @param string $entityType
1028 * @param int $entityId
1029 *
1030 * @return Collection
1031 * @throws InvalidArgumentException
1032 * @throws QueryExecutionException
1033 */
1034 public function getSentNotificationsByUserAndEntity($userId, $type, $entityType, $entityId)
1035 {
1036 $entityColumn = '';
1037
1038 switch ($entityType) {
1039 case (Entities::APPOINTMENT):
1040 $entityColumn = 'nl.appointmentId';
1041
1042 break;
1043 case (Entities::EVENT):
1044 $entityColumn = 'nl.eventId';
1045
1046 break;
1047 case (Entities::PACKAGE):
1048 $entityColumn = 'nl.packageCustomerId';
1049
1050 break;
1051 }
1052
1053 $params = [
1054 ':entityId' => $entityId,
1055 ':userId' => $userId,
1056 ':type' => $type,
1057 ];
1058
1059 try {
1060 $statement = $this->connection->prepare(
1061 "SELECT * FROM {$this->table} nl
1062 WHERE nl.userId = :userId
1063 AND {$entityColumn} = :entityId
1064 AND nl.notificationId IN (SELECT id FROM {$this->notificationsTable} WHERE type = :type)
1065 ORDER BY nl.sentDateTime DESC"
1066 );
1067
1068 $statement->execute($params);
1069
1070 $rows = $statement->fetchAll();
1071 } catch (\Exception $e) {
1072 throw new QueryExecutionException('Unable to get data from ' . __CLASS__, $e->getCode(), $e);
1073 }
1074
1075 $items = [];
1076
1077 foreach ($rows as $row) {
1078 $items[] = call_user_func([static::FACTORY, 'create'], $row);
1079 }
1080
1081 return new Collection($items);
1082 }
1083 }
1084