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