PluginProbe ʕ •ᴥ•ʔ
Booking for Appointments and Events Calendar – Amelia / 1.2.27
Booking for Appointments and Events Calendar – Amelia v1.2.27
2.4.9 2.4.8 2.4.7 2.4.6 2.4.5 2.4.4 2.4.3 2.4.2 2.4.1 2.4 trunk 1.2.1 1.2.10 1.2.11 1.2.12 1.2.13 1.2.14 1.2.15 1.2.16 1.2.17 1.2.18 1.2.19 1.2.2 1.2.20 1.2.21 1.2.22 1.2.23 1.2.24 1.2.25 1.2.26 1.2.27 1.2.28 1.2.29 1.2.3 1.2.30 1.2.31 1.2.32 1.2.33 1.2.34 1.2.35 1.2.36 1.2.37 1.2.38 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 2.0 2.0.1 2.0.2 2.1 2.1.1 2.1.2 2.1.3 2.2 2.2.1 2.3
ameliabooking / src / 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
1082 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.serviceId AS appointment_serviceId,
230 a.providerId AS appointment_providerId,
231 a.locationId AS appointment_locationId,
232 a.internalNotes AS appointment_internalNotes,
233 a.status AS appointment_status,
234 a.zoomMeeting AS appointment_zoom_meeting,
235 a.lessonSpace AS appointment_lesson_space,
236 a.googleMeetUrl AS appointment_google_meet_url,
237 a.microsoftTeamsUrl AS appointment_microsoft_teams_url,
238
239 cb.id AS booking_id,
240 cb.customerId AS booking_customerId,
241 cb.status AS booking_status,
242 cb.price AS booking_price,
243 cb.customFields AS booking_customFields,
244 cb.info AS booking_info,
245 cb.utcOffset AS booking_utcOffset,
246 cb.aggregatedPrice AS booking_aggregatedPrice,
247 cb.persons AS booking_persons,
248 cb.duration AS booking_duration,
249 cb.created AS booking_created,
250
251 p.id AS payment_id,
252 p.amount AS payment_amount,
253 p.dateTime AS payment_dateTime,
254 p.status AS payment_status,
255 p.gateway AS payment_gateway,
256 p.gatewayTitle AS payment_gatewayTitle,
257 p.data AS payment_data,
258
259 cbe.id AS bookingExtra_id,
260 cbe.extraId AS bookingExtra_extraId,
261 cbe.customerBookingId AS bookingExtra_customerBookingId,
262 cbe.quantity AS bookingExtra_quantity,
263 cbe.price AS bookingExtra_price,
264 cbe.aggregatedPrice AS bookingExtra_aggregatedPrice,
265
266 c.id AS coupon_id,
267 c.code AS coupon_code,
268 c.discount AS coupon_discount,
269 c.deduction AS coupon_deduction,
270 c.limit AS coupon_limit,
271 c.customerLimit AS coupon_customerLimit,
272 c.status AS coupon_status
273 FROM {$this->appointmentsTable} a
274 INNER JOIN {$this->bookingsTable} cb ON cb.appointmentId = a.id
275 LEFT JOIN {$paymentsTable} p ON p.customerBookingId = cb.id
276 LEFT JOIN {$customerBookingsExtrasTable} cbe ON cbe.customerBookingId = cb.id
277 LEFT JOIN {$couponsTable} c ON c.id = cb.couponId
278 WHERE a.bookingStart BETWEEN $startCurrentDate AND $endCurrentDate
279 {$whereStatuses}
280 AND a.notifyParticipants = 1 AND
281 a.id NOT IN (
282 SELECT nl.appointmentId
283 FROM {$this->table} nl
284 INNER JOIN {$this->notificationsTable} n ON nl.notificationId = n.id
285 WHERE n.id = {$notificationId} AND (nl.sent IS NULL OR nl.sent = 1) AND nl.appointmentId IS NOT NULL
286 )"
287 );
288
289 $rows = $statement->fetchAll();
290 } catch (\Exception $e) {
291 throw new QueryExecutionException('Unable to find appointments in ' . __CLASS__, $e->getCode(), $e);
292 }
293
294 return AppointmentFactory::createCollection($rows);
295 }
296
297 /**
298 * Return a collection of tomorrow events where customer notification is not sent and should be.
299 *
300 * @param $notificationId
301 *
302 * @return Collection
303 *
304 * @throws InvalidArgumentException
305 * @throws QueryExecutionException
306 * @throws \Exception
307 */
308 public function getCustomersNextDayEvents($notificationId, $nextDay = true)
309 {
310 $couponsTable = CouponsTable::getTableName();
311 $paymentsTable = PaymentsTable::getTableName();
312 $eventsTable = EventsTable::getTableName();
313
314 $eventsPeriodsTable = EventsPeriodsTable::getTableName();
315
316 $customerBookingsEventsPeriods = CustomerBookingsToEventsPeriodsTable::getTableName();
317
318 $eventsProvidersTable = EventsProvidersTable::getTableName();
319
320 $startDate = DateTimeService::getCustomDateTimeObjectInUtc(
321 DateTimeService::getNowDateTimeObject()->setTime(0, 0, 0)->format('Y-m-d H:i:s')
322 );
323 $endDate = DateTimeService::getCustomDateTimeObjectInUtc(
324 DateTimeService::getNowDateTimeObject()->setTime(23, 59, 59)->format('Y-m-d H:i:s')
325 );
326
327 if ($nextDay) {
328 $startDate = $startDate->modify('+1 day');
329 $endDate = $endDate->modify('+1 day');
330 }
331
332 $startCurrentDate = "STR_TO_DATE('" . $startDate->format('Y-m-d H:i:s') . "', '%Y-%m-%d %H:%i:%s')";
333 $endCurrentDate = "STR_TO_DATE('" . $endDate->format('Y-m-d H:i:s') . "', '%Y-%m-%d %H:%i:%s')";
334
335 try {
336 $statement = $this->connection->query(
337 "SELECT
338 e.id AS event_id,
339 e.name AS event_name,
340 e.status AS event_status,
341 e.bookingOpens AS event_bookingOpens,
342 e.bookingCloses AS event_bookingCloses,
343 e.recurringCycle AS event_recurringCycle,
344 e.recurringOrder AS event_recurringOrder,
345 e.recurringUntil AS event_recurringUntil,
346 e.maxCapacity AS event_maxCapacity,
347 e.price AS event_price,
348 e.description AS event_description,
349 e.color AS event_color,
350 e.show AS event_show,
351 e.locationId AS event_locationId,
352 e.customLocation AS event_customLocation,
353 e.parentId AS event_parentId,
354 e.created AS event_created,
355 e.notifyParticipants AS event_notifyParticipants,
356 e.zoomUserId AS event_zoomUserId,
357 e.deposit AS event_deposit,
358 e.depositPayment AS event_depositPayment,
359 e.depositPerPerson AS event_depositPerPerson,
360 e.organizerId AS event_organizerId,
361
362 ep.id AS event_periodId,
363 ep.periodStart AS event_periodStart,
364 ep.periodEnd AS event_periodEnd,
365 ep.zoomMeeting AS event_periodZoomMeeting,
366 ep.lessonSpace AS event_periodLessonSpace,
367 ep.googleMeetUrl AS event_googleMeetUrl,
368
369 cb.id AS booking_id,
370 cb.customerId AS booking_customerId,
371 cb.status AS booking_status,
372 cb.price AS booking_price,
373 cb.customFields AS booking_customFields,
374 cb.info AS booking_info,
375 cb.utcOffset AS booking_utcOffset,
376 cb.aggregatedPrice AS booking_aggregatedPrice,
377 cb.persons AS booking_persons,
378 cb.created AS booking_created,
379
380 p.id AS payment_id,
381 p.amount AS payment_amount,
382 p.dateTime AS payment_dateTime,
383 p.status AS payment_status,
384 p.gateway AS payment_gateway,
385 p.gatewayTitle AS payment_gatewayTitle,
386 p.data AS payment_data,
387
388 pu.id AS provider_id,
389 pu.firstName AS provider_firstName,
390 pu.lastName AS provider_lastName,
391 pu.email AS provider_email,
392 pu.note AS provider_note,
393 pu.description AS provider_description,
394 pu.phone AS provider_phone,
395 pu.gender AS provider_gender,
396 pu.pictureFullPath AS provider_pictureFullPath,
397 pu.pictureThumbPath AS provider_pictureThumbPath,
398 pu.translations AS provider_translations,
399
400 c.id AS coupon_id,
401 c.code AS coupon_code,
402 c.discount AS coupon_discount,
403 c.deduction AS coupon_deduction,
404 c.limit AS coupon_limit,
405 c.customerLimit AS coupon_customerLimit,
406 c.status AS coupon_status
407 FROM {$eventsTable} e
408 INNER JOIN {$eventsPeriodsTable} ep ON ep.eventId = e.id
409 INNER JOIN {$customerBookingsEventsPeriods} cbe ON cbe.eventPeriodId = ep.id
410 INNER JOIN {$this->bookingsTable} cb ON cb.id = cbe.customerBookingId
411 LEFT JOIN {$paymentsTable} p ON p.customerBookingId = cb.id
412 LEFT JOIN {$eventsProvidersTable} epr ON epr.eventId = e.id
413 LEFT JOIN {$this->usersTable} pu ON pu.id = epr.userId
414 LEFT JOIN {$couponsTable} c ON c.id = cb.couponId
415 WHERE ep.periodStart BETWEEN {$startCurrentDate} AND {$endCurrentDate}
416 AND cb.status = 'approved'
417 AND e.status = 'approved'
418 AND e.notifyParticipants = 1 AND
419 e.id NOT IN (
420 SELECT nl.eventId
421 FROM {$this->table} nl
422 INNER JOIN {$this->notificationsTable} n ON nl.notificationId = n.id
423 WHERE n.id = {$notificationId} AND (nl.sent IS NULL OR nl.sent = 1) AND nl.eventId IS NOT NULL
424 )"
425 );
426
427 $rows = $statement->fetchAll();
428 } catch (\Exception $e) {
429 throw new QueryExecutionException('Unable to find appointments in ' . __CLASS__, $e->getCode(), $e);
430 }
431
432 return EventFactory::createCollection($rows);
433 }
434
435 /**
436 * Return a collection of tomorrow appointments where provider notification is not sent and should be.
437 *
438 * @param int $notificationId
439 * @param bool $nextDay
440 * @param array $statuses
441 *
442 * @return Collection
443 * @throws InvalidArgumentException
444 * @throws QueryExecutionException
445 * @throws \Exception
446 */
447 public function getProvidersNextDayAppointments($notificationId, $nextDay, $statuses)
448 {
449 $couponsTable = CouponsTable::getTableName();
450
451 $customerBookingsExtrasTable = CustomerBookingsToExtrasTable::getTableName();
452
453 $paymentsTable = PaymentsTable::getTableName();
454
455 $startDate = DateTimeService::getCustomDateTimeObjectInUtc(
456 DateTimeService::getNowDateTimeObject()->setTime(0, 0, 0)->format('Y-m-d H:i:s')
457 );
458
459 $endDate = DateTimeService::getCustomDateTimeObjectInUtc(
460 DateTimeService::getNowDateTimeObject()->setTime(23, 59, 59)->format('Y-m-d H:i:s')
461 );
462
463 if ($nextDay) {
464 $startDate = $startDate->modify('+1 day');
465 $endDate = $endDate->modify('+1 day');
466 }
467
468 $startCurrentDate = "STR_TO_DATE('" . $startDate->format('Y-m-d H:i:s') . "', '%Y-%m-%d %H:%i:%s')";
469
470 $endCurrentDate = "STR_TO_DATE('" . $endDate->format('Y-m-d H:i:s') . "', '%Y-%m-%d %H:%i:%s')";
471
472 $whereStatuses = [];
473
474 foreach ($statuses as $key => $status) {
475 $whereStatuses[] = "cb.status = '$status'";
476 }
477
478 $whereStatuses = $whereStatuses ? 'AND (' . implode(' OR ', $whereStatuses) . ')' : '';
479
480 try {
481 $statement = $this->connection->query(
482 "SELECT
483 a.id AS appointment_id,
484 a.bookingStart AS appointment_bookingStart,
485 a.bookingEnd AS appointment_bookingEnd,
486 a.notifyParticipants AS appointment_notifyParticipants,
487 a.serviceId AS appointment_serviceId,
488 a.providerId AS appointment_providerId,
489 a.locationId AS appointment_locationId,
490 a.internalNotes AS appointment_internalNotes,
491 a.status AS appointment_status,
492 a.zoomMeeting AS appointment_zoom_meeting,
493 a.lessonSpace AS appointment_lesson_space,
494 a.googleMeetUrl AS appointment_google_meet_url,
495 a.microsoftTeamsUrl AS appointment_microsoft_teams_url,
496
497 cb.id AS booking_id,
498 cb.customerId AS booking_customerId,
499 cb.status AS booking_status,
500 cb.price AS booking_price,
501 cb.customFields AS booking_customFields,
502 cb.persons AS booking_persons,
503 cb.aggregatedPrice AS booking_aggregatedPrice,
504 cb.duration AS booking_duration,
505 cb.created AS booking_created,
506
507 p.id AS payment_id,
508 p.amount AS payment_amount,
509 p.dateTime AS payment_dateTime,
510 p.status AS payment_status,
511 p.gateway AS payment_gateway,
512 p.gatewayTitle AS payment_gatewayTitle,
513 p.data AS payment_data,
514
515 cbe.id AS bookingExtra_id,
516 cbe.extraId AS bookingExtra_extraId,
517 cbe.customerBookingId AS bookingExtra_customerBookingId,
518 cbe.quantity AS bookingExtra_quantity,
519 cbe.price AS bookingExtra_price,
520 cbe.aggregatedPrice AS bookingExtra_aggregatedPrice,
521
522 c.id AS coupon_id,
523 c.code AS coupon_code,
524 c.discount AS coupon_discount,
525 c.deduction AS coupon_deduction,
526 c.limit AS coupon_limit,
527 c.customerLimit AS coupon_customerLimit,
528 c.status AS coupon_status
529 FROM {$this->appointmentsTable} a
530 INNER JOIN {$this->bookingsTable} cb ON cb.appointmentId = a.id
531 LEFT JOIN {$paymentsTable} p ON p.customerBookingId = cb.id
532 LEFT JOIN {$customerBookingsExtrasTable} cbe ON cbe.customerBookingId = cb.id
533 LEFT JOIN {$couponsTable} c ON c.id = cb.couponId
534 WHERE a.bookingStart BETWEEN $startCurrentDate AND $endCurrentDate
535 {$whereStatuses}
536 AND a.id NOT IN (
537 SELECT nl.appointmentId
538 FROM {$this->table} nl
539 INNER JOIN {$this->notificationsTable} n ON nl.notificationId = n.id
540 WHERE n.id = {$notificationId} AND (nl.sent IS NULL OR nl.sent = 1) AND nl.appointmentId IS NOT NULL
541 )"
542 );
543
544 $rows = $statement->fetchAll();
545 } catch (\Exception $e) {
546 throw new QueryExecutionException('Unable to find appointments in ' . __CLASS__, $e->getCode(), $e);
547 }
548
549 return AppointmentFactory::createCollection($rows);
550 }
551
552 /**
553 * Return a collection of tomorrow events where provider notification is not sent and should be.
554 *
555 * @param $notificationId
556 *
557 * @return Collection
558 * @throws InvalidArgumentException
559 * @throws QueryExecutionException
560 * @throws \Exception
561 */
562 public function getProvidersNextDayEvents($notificationId, $nextDay)
563 {
564 $couponsTable = CouponsTable::getTableName();
565 $eventsTable = EventsTable::getTableName();
566 $eventsPeriodsTable = EventsPeriodsTable::getTableName();
567 $customerBookingsEventsPeriods = CustomerBookingsToEventsPeriodsTable::getTableName();
568 $eventsProvidersTable = EventsProvidersTable::getTableName();
569 $paymentsTable = PaymentsTable::getTableName();
570
571 $startDate = DateTimeService::getCustomDateTimeObjectInUtc(
572 DateTimeService::getNowDateTimeObject()->setTime(0, 0, 0)->format('Y-m-d H:i:s')
573 );
574 $endDate = DateTimeService::getCustomDateTimeObjectInUtc(
575 DateTimeService::getNowDateTimeObject()->setTime(23, 59, 59)->format('Y-m-d H:i:s')
576 );
577
578 if ($nextDay) {
579 $startDate = $startDate->modify('+1 day');
580 $endDate = $endDate->modify('+1 day');
581 }
582
583 $startCurrentDate = "STR_TO_DATE('" . $startDate->format('Y-m-d H:i:s') . "', '%Y-%m-%d %H:%i:%s')";
584 $endCurrentDate = "STR_TO_DATE('" . $endDate->format('Y-m-d H:i:s') . "', '%Y-%m-%d %H:%i:%s')";
585
586
587 try {
588 $statement = $this->connection->query(
589 "SELECT
590 e.id AS event_id,
591 e.name AS event_name,
592 e.status AS event_status,
593 e.bookingOpens AS event_bookingOpens,
594 e.bookingCloses AS event_bookingCloses,
595 e.recurringCycle AS event_recurringCycle,
596 e.recurringOrder AS event_recurringOrder,
597 e.recurringUntil AS event_recurringUntil,
598 e.maxCapacity AS event_maxCapacity,
599 e.price AS event_price,
600 e.description AS event_description,
601 e.color AS event_color,
602 e.show AS event_show,
603 e.locationId AS event_locationId,
604 e.customLocation AS event_customLocation,
605 e.parentId AS event_parentId,
606 e.created AS event_created,
607 e.notifyParticipants AS event_notifyParticipants,
608 e.zoomUserId AS event_zoomUserId,
609 e.deposit AS event_deposit,
610 e.depositPayment AS event_depositPayment,
611 e.depositPerPerson AS event_depositPerPerson,
612 e.organizerId AS event_organizerId,
613
614 ep.id AS event_periodId,
615 ep.periodStart AS event_periodStart,
616 ep.periodEnd AS event_periodEnd,
617 ep.zoomMeeting AS event_periodZoomMeeting,
618 ep.lessonSpace AS event_periodLessonSpace,
619 ep.googleMeetUrl AS event_googleMeetUrl,
620
621 pu.id AS provider_id,
622 pu.firstName AS provider_firstName,
623 pu.lastName AS provider_lastName,
624 pu.email AS provider_email,
625 pu.note AS provider_note,
626 pu.description AS provider_description,
627 pu.phone AS provider_phone,
628 pu.gender AS provider_gender,
629 pu.pictureFullPath AS provider_pictureFullPath,
630 pu.pictureThumbPath AS provider_pictureThumbPath,
631 pu.timeZone AS provider_timeZone,
632
633 cb.id AS booking_id,
634 cb.customerId AS booking_customerId,
635 cb.status AS booking_status,
636 cb.price AS booking_price,
637 cb.customFields AS booking_customFields,
638 cb.persons AS booking_persons,
639 cb.created AS booking_created,
640
641 p.id AS payment_id,
642 p.amount AS payment_amount,
643 p.dateTime AS payment_dateTime,
644 p.status AS payment_status,
645 p.gateway AS payment_gateway,
646 p.gatewayTitle AS payment_gatewayTitle,
647 p.data AS payment_data,
648
649 c.id AS coupon_id,
650 c.code AS coupon_code,
651 c.discount AS coupon_discount,
652 c.deduction AS coupon_deduction,
653 c.limit AS coupon_limit,
654 c.customerLimit AS coupon_customerLimit,
655 c.status AS coupon_status
656 FROM {$eventsTable} e
657 INNER JOIN {$eventsPeriodsTable} ep ON ep.eventId = e.id
658 INNER JOIN {$customerBookingsEventsPeriods} cbe ON cbe.eventPeriodId = ep.id
659 INNER JOIN {$this->bookingsTable} cb ON cb.id = cbe.customerBookingId
660 LEFT JOIN {$paymentsTable} p ON p.customerBookingId = cb.id
661 LEFT JOIN {$couponsTable} c ON c.id = cb.couponId
662 LEFT JOIN {$eventsProvidersTable} epr ON epr.eventId = e.id
663 LEFT JOIN {$this->usersTable} pu ON pu.id = epr.userId
664 WHERE ep.periodStart BETWEEN {$startCurrentDate} AND {$endCurrentDate}
665 AND cb.status = 'approved'
666 AND e.status = 'approved'
667 AND e.id NOT IN (
668 SELECT nl.eventId
669 FROM {$this->table} nl
670 INNER JOIN {$this->notificationsTable} n ON nl.notificationId = n.id
671 WHERE n.id = {$notificationId} AND (nl.sent IS NULL OR nl.sent = 1) AND nl.eventId IS NOT NULL
672 )"
673 );
674
675 $rows = $statement->fetchAll();
676 } catch (\Exception $e) {
677 throw new QueryExecutionException('Unable to find events in ' . __CLASS__, $e->getCode(), $e);
678 }
679
680 return EventFactory::createCollection($rows);
681 }
682
683 /**
684 * Return a collection of today's past appointments where follow up notification is not sent and should be.
685 *
686 * @param Notification $notification
687 * @param array $statuses
688 *
689 * @return Collection
690 * @throws InvalidArgumentException
691 * @throws QueryExecutionException
692 */
693 public function getScheduledAppointments($notification, $statuses = [])
694 {
695 $couponsTable = CouponsTable::getTableName();
696 $customerBookingsExtrasTable = CustomerBookingsToExtrasTable::getTableName();
697 $paymentsTable = PaymentsTable::getTableName();
698
699 try {
700 $currentDateTime = "STR_TO_DATE('" . DateTimeService::getNowDateTimeInUtc() . "', '%Y-%m-%d %H:%i:%s')";
701
702 $where = '';
703 if ($notification->getTimeAfter()) {
704 $timeAfter =
705 apply_filters(
706 'amelia_modify_scheduled_notification_time_after',
707 $notification->getTimeAfter()->getValue(),
708 $notification->toArray()
709 );
710 $lastTime = apply_filters('amelia_modify_scheduled_notification_last_time', $timeAfter + 259200, $notification->toArray());
711
712 $where =
713 "{$currentDateTime} BETWEEN DATE_ADD(a.bookingEnd, INTERVAL {$timeAfter} SECOND) AND DATE_ADD(a.bookingEnd, INTERVAL {$lastTime} SECOND)";
714 } elseif ($notification->getTimeBefore()) {
715 $timeBefore =
716 apply_filters(
717 'amelia_modify_scheduled_notification_time_before',
718 $notification->getTimeBefore()->getValue(),
719 $notification->toArray()
720 );
721 $where =
722 "({$currentDateTime} BETWEEN
723 DATE_SUB(a.bookingStart, INTERVAL {$timeBefore} SECOND) AND a.bookingStart) AND
724 (a.bookingStart >= DATE_ADD(cb.created, INTERVAL {$timeBefore} SECOND))";
725 }
726
727 $whereStatuses = [];
728
729 foreach ($statuses as $key => $status) {
730 $whereStatuses[] = "cb.status = '$status'";
731 }
732
733 $whereStatuses = $whereStatuses ? ($where ? ' AND ' : '') . '(' . implode(' OR ', $whereStatuses) . ')' : '';
734
735
736 $statement = $this->connection->query(
737 "SELECT
738 a.id AS appointment_id,
739 a.bookingStart AS appointment_bookingStart,
740 a.bookingEnd AS appointment_bookingEnd,
741 a.notifyParticipants AS appointment_notifyParticipants,
742 a.serviceId AS appointment_serviceId,
743 a.providerId AS appointment_providerId,
744 a.locationId AS appointment_locationId,
745 a.internalNotes AS appointment_internalNotes,
746 a.status AS appointment_status,
747 a.googleMeetUrl AS appointment_google_meet_url,
748 a.lessonSpace AS appointment_lesson_space,
749 a.zoomMeeting AS appointment_zoom_meeting,
750 a.microsoftTeamsUrl AS appointment_microsoft_teams_url,
751
752 cb.id AS booking_id,
753 cb.customerId AS booking_customerId,
754 cb.status AS booking_status,
755 cb.price AS booking_price,
756 cb.customFields AS booking_customFields,
757 cb.info AS booking_info,
758 cb.utcOffset AS booking_utcOffset,
759 cb.aggregatedPrice AS booking_aggregatedPrice,
760 cb.persons AS booking_persons,
761 cb.duration AS booking_duration,
762 cb.created AS booking_created,
763
764 p.id AS payment_id,
765 p.amount AS payment_amount,
766 p.dateTime AS payment_dateTime,
767 p.status AS payment_status,
768 p.gateway AS payment_gateway,
769 p.gatewayTitle AS payment_gatewayTitle,
770 p.data AS payment_data,
771
772 cbe.id AS bookingExtra_id,
773 cbe.extraId AS bookingExtra_extraId,
774 cbe.customerBookingId AS bookingExtra_customerBookingId,
775 cbe.quantity AS bookingExtra_quantity,
776 cbe.price AS bookingExtra_price,
777 cbe.aggregatedPrice AS bookingExtra_aggregatedPrice,
778
779 c.id AS coupon_id,
780 c.code AS coupon_code,
781 c.discount AS coupon_discount,
782 c.deduction AS coupon_deduction,
783 c.limit AS coupon_limit,
784 c.customerLimit AS coupon_customerLimit,
785 c.status AS coupon_status
786 FROM {$this->appointmentsTable} a
787 INNER JOIN {$this->bookingsTable} cb ON cb.appointmentId = a.id
788 LEFT JOIN {$paymentsTable} p ON p.customerBookingId = cb.id
789 LEFT JOIN {$customerBookingsExtrasTable} cbe ON cbe.customerBookingId = cb.id
790 LEFT JOIN {$couponsTable} c ON c.id = cb.couponId
791 WHERE {$where}
792 AND a.notifyParticipants = 1
793 {$whereStatuses}
794 AND a.id NOT IN (
795 SELECT nl.appointmentId
796 FROM {$this->table} nl
797 INNER JOIN {$this->notificationsTable} n ON nl.notificationId = n.id
798 WHERE n.id = {$notification->getId()->getValue()} AND (nl.sent IS NULL OR nl.sent = 1) AND nl.appointmentId IS NOT NULL
799 )"
800 );
801
802 $rows = $statement->fetchAll();
803 } catch (\Exception $e) {
804 throw new QueryExecutionException('Unable to find appointments in ' . __CLASS__, $e->getCode(), $e);
805 }
806
807 return AppointmentFactory::createCollection($rows);
808 }
809
810 /**
811 * Return a collection of today's past appointments where follow-up notification is not sent and should be.
812 *
813 * @param Notification $notification
814 *
815 * @return Collection
816 * @throws InvalidArgumentException
817 * @throws QueryExecutionException
818 */
819 public function getScheduledEvents($notification)
820 {
821 $eventsTable = EventsTable::getTableName();
822
823 $eventsPeriodsTable = EventsPeriodsTable::getTableName();
824
825 $customerBookingsEventsPeriods = CustomerBookingsToEventsPeriodsTable::getTableName();
826
827 $paymentsTable = PaymentsTable::getTableName();
828
829 $currentDateTime = "STR_TO_DATE('" . DateTimeService::getNowDateTimeInUtc() . "', '%Y-%m-%d %H:%i:%s')";
830
831 $where = "WHERE e.notifyParticipants = 1
832 AND cb.status = 'approved'
833 AND e.status = 'approved'
834 AND e.id NOT IN (
835 SELECT nl.eventId
836 FROM {$this->table} nl
837 INNER JOIN {$this->notificationsTable} n ON nl.notificationId = n.id
838 WHERE n.id = {$notification->getId()->getValue()} AND (nl.sent IS NULL OR nl.sent = 1) AND nl.eventId IS NOT NULL
839 )";
840
841 if ($notification->getTimeAfter()) {
842 $timeAfter = $notification->getTimeAfter()->getValue();
843
844 $lastTime = $timeAfter + 432000;
845
846 $where .=
847 " AND {$currentDateTime} BETWEEN DATE_ADD(ep.periodEnd, INTERVAL {$timeAfter} SECOND)
848 AND DATE_ADD(ep.periodEnd, INTERVAL {$lastTime} SECOND)";
849 } elseif ($notification->getTimeBefore()) {
850 $timeBefore = $notification->getTimeBefore()->getValue();
851
852 $where .=
853 " AND ({$currentDateTime} BETWEEN DATE_SUB(ep.periodStart, INTERVAL {$timeBefore} SECOND) AND ep.periodStart)
854 AND (ep.periodStart >= DATE_ADD(p.created, INTERVAL {$timeBefore} SECOND))";
855 }
856
857 try {
858 $statement = $this->connection->query(
859 "SELECT
860 e.id AS event_id,
861 e.name AS event_name,
862 e.status AS event_status,
863 e.bookingOpens AS event_bookingOpens,
864 e.bookingCloses AS event_bookingCloses,
865 e.recurringCycle AS event_recurringCycle,
866 e.recurringOrder AS event_recurringOrder,
867 e.recurringUntil AS event_recurringUntil,
868 e.recurringInterval AS event_recurringInterval,
869 e.bringingAnyone AS event_bringingAnyone,
870 e.bookMultipleTimes AS event_bookMultipleTimes,
871 e.maxCapacity AS event_maxCapacity,
872 e.price AS event_price,
873 e.description AS event_description,
874 e.color AS event_color,
875 e.show AS event_show,
876 e.locationId AS event_locationId,
877 e.customLocation AS event_customLocation,
878 e.parentId AS event_parentId,
879 e.created AS event_created,
880 e.notifyParticipants AS event_notifyParticipants,
881 e.zoomUserId AS event_zoomUserId,
882 e.deposit AS event_deposit,
883 e.depositPayment AS event_depositPayment,
884 e.depositPerPerson AS event_depositPerPerson,
885 e.organizerId AS event_organizerId,
886
887 ep.id AS event_periodId,
888 ep.periodStart AS event_periodStart,
889 ep.periodEnd AS event_periodEnd,
890 ep.lessonSpace AS event_periodLessonSpace,
891 ep.zoomMeeting AS event_periodZoomMeeting,
892 ep.googleMeetUrl AS event_googleMeetUrl,
893
894 cb.id AS booking_id,
895 cb.customerId AS booking_customerId,
896 cb.status AS booking_status,
897 cb.price AS booking_price,
898 cb.customFields AS booking_customFields,
899 cb.info AS booking_info,
900 cb.utcOffset AS booking_utcOffset,
901 cb.aggregatedPrice AS booking_aggregatedPrice,
902 cb.persons AS booking_persons,
903 cb.duration AS booking_duration,
904 cb.created AS booking_created
905 FROM {$eventsTable} e
906 INNER JOIN {$eventsPeriodsTable} ep ON ep.eventId = e.id
907 INNER JOIN {$customerBookingsEventsPeriods} cbe ON cbe.eventPeriodId = ep.id
908 INNER JOIN {$this->bookingsTable} cb ON cb.id = cbe.customerBookingId
909 LEFT JOIN {$paymentsTable} p ON p.customerBookingId = cb.id
910 {$where}"
911 );
912
913 $rows = $statement->fetchAll();
914 } catch (\Exception $e) {
915 throw new QueryExecutionException('Unable to find events in ' . __CLASS__, $e->getCode(), $e);
916 }
917
918 return EventFactory::createCollection($rows);
919 }
920
921 /**
922 * Returns a collection of customers that have birthday on today's date and where notification is not sent
923 *
924 * @param $notificationType
925 *
926 * @return Collection
927 * @throws InvalidArgumentException
928 * @throws QueryExecutionException
929 * @throws \Exception
930 */
931 public function getBirthdayCustomers($notificationType)
932 {
933 $currentDate = "STR_TO_DATE('" . DateTimeService::getNowDateTimeInUtc() . "', '%Y-%m-%d')";
934
935 $params = [
936 ':type' => AbstractUser::USER_ROLE_CUSTOMER,
937 ':statusVisible' => Status::VISIBLE,
938 ];
939
940 try {
941 $statement = $this->connection->prepare(
942 "SELECT * FROM {$this->usersTable} as u
943 WHERE
944 u.type = :type AND
945 u.status = :statusVisible AND
946 MONTH(u.birthday) = MONTH({$currentDate}) AND
947 DAY(u.birthday) = DAY({$currentDate}) AND
948 u.id NOT IN (
949 SELECT nl.userID
950 FROM {$this->table} nl
951 INNER JOIN {$this->notificationsTable} n ON nl.notificationId = n.id
952 WHERE n.name = 'customer_birthday_greeting' AND n.type = '{$notificationType}' AND
953 YEAR(nl.sentDateTime) = YEAR({$currentDate}) AND (nl.sent IS NULL OR nl.sent = 1)
954 )"
955 );
956
957 $statement->execute($params);
958
959 $rows = $statement->fetchAll();
960 } catch (\Exception $e) {
961 throw new QueryExecutionException('Unable to get data from ' . __CLASS__, $e->getCode(), $e);
962 }
963
964 $items = [];
965 foreach ($rows as $row) {
966 $items[] = call_user_func([UserFactory::class, 'create'], $row);
967 }
968
969 return new Collection($items);
970 }
971
972 /**
973 * Returns a collection of undelivered notifications
974 *
975 * @param string $type
976 *
977 * @return Collection
978 * @throws InvalidArgumentException
979 * @throws QueryExecutionException
980 */
981 public function getUndeliveredNotifications($type)
982 {
983 $params = [
984 ':type' => $type,
985 ];
986
987 $currentDateTime = "STR_TO_DATE('" . DateTimeService::getNowDateTimeInUtc() . "', '%Y-%m-%d %H:%i:%s')";
988
989 $pastDateTime =
990 "STR_TO_DATE('" .
991 DateTimeService::getNowDateTimeObjectInUtc()->modify('-1 day')->format('Y-m-d H:i:s') .
992 "', '%Y-%m-%d %H:%i:%s')";
993
994 try {
995 $statement = $this->connection->prepare(
996 "SELECT nl.* FROM {$this->table} nl
997 INNER JOIN {$this->notificationsTable} n ON nl.notificationId = n.id
998 WHERE
999 nl.sent = 0 AND
1000 {$currentDateTime} > DATE_ADD(nl.sentDateTime, INTERVAL 300 SECOND) AND
1001 {$pastDateTime} < nl.sentDateTime AND
1002 nl.data IS NOT NULL AND
1003 n.type = :type"
1004 );
1005
1006 $statement->execute($params);
1007
1008 $rows = $statement->fetchAll();
1009 } catch (\Exception $e) {
1010 throw new QueryExecutionException('Unable to get data from ' . __CLASS__, $e->getCode(), $e);
1011 }
1012
1013 $items = [];
1014
1015 foreach ($rows as $row) {
1016 $items[] = call_user_func([static::FACTORY, 'create'], $row);
1017 }
1018
1019 return new Collection($items);
1020 }
1021
1022 /**
1023 * @param int $userId
1024 * @param string $type
1025 * @param string $entityType
1026 * @param int $entityId
1027 *
1028 * @return Collection
1029 * @throws InvalidArgumentException
1030 * @throws QueryExecutionException
1031 */
1032 public function getSentNotificationsByUserAndEntity($userId, $type, $entityType, $entityId)
1033 {
1034 $entityColumn = '';
1035
1036 switch ($entityType) {
1037 case (Entities::APPOINTMENT):
1038 $entityColumn = 'nl.appointmentId';
1039
1040 break;
1041 case (Entities::EVENT):
1042 $entityColumn = 'nl.eventId';
1043
1044 break;
1045 case (Entities::PACKAGE):
1046 $entityColumn = 'nl.packageCustomerId';
1047
1048 break;
1049 }
1050
1051 $params = [
1052 ':entityId' => $entityId,
1053 ':userId' => $userId,
1054 ':type' => $type,
1055 ];
1056
1057 try {
1058 $statement = $this->connection->prepare(
1059 "SELECT * FROM {$this->table} nl
1060 WHERE nl.userId = :userId
1061 AND {$entityColumn} = :entityId
1062 AND nl.notificationId IN (SELECT id FROM {$this->notificationsTable} WHERE type = :type)
1063 ORDER BY nl.sentDateTime DESC"
1064 );
1065
1066 $statement->execute($params);
1067
1068 $rows = $statement->fetchAll();
1069 } catch (\Exception $e) {
1070 throw new QueryExecutionException('Unable to get data from ' . __CLASS__, $e->getCode(), $e);
1071 }
1072
1073 $items = [];
1074
1075 foreach ($rows as $row) {
1076 $items[] = call_user_func([static::FACTORY, 'create'], $row);
1077 }
1078
1079 return new Collection($items);
1080 }
1081 }
1082