PluginProbe ʕ •ᴥ•ʔ
Booking for Appointments and Events Calendar – Amelia / 1.2.20
Booking for Appointments and Events Calendar – Amelia v1.2.20
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 2 years ago NotificationSMSHistoryRepository.php 2 years ago NotificationsToEntitiesRepository.php 4 years ago
NotificationLogRepository.php
1062 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 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 = apply_filters('amelia_modify_scheduled_notification_time_after', $notification->getTimeAfter()->getValue(), $notification->toArray());
705 $lastTime = apply_filters('amelia_modify_scheduled_notification_last_time', $timeAfter + 259200, $notification->toArray());
706
707 $where = "{$currentDateTime} BETWEEN DATE_ADD(a.bookingEnd, INTERVAL {$timeAfter} SECOND) AND DATE_ADD(a.bookingEnd, INTERVAL {$lastTime} SECOND)";
708 } else if ($notification->getTimeBefore()) {
709 $timeBefore = apply_filters('amelia_modify_scheduled_notification_time_before', $notification->getTimeBefore()->getValue(), $notification->toArray());
710 $where = "({$currentDateTime} BETWEEN DATE_SUB(a.bookingStart, INTERVAL {$timeBefore} SECOND) AND a.bookingStart) AND (a.bookingStart >= DATE_ADD(cb.created, INTERVAL {$timeBefore} SECOND))";
711 }
712
713 $whereStatuses = [];
714
715 foreach ($statuses as $key => $status) {
716 $whereStatuses[] = "cb.status = '$status'";
717 }
718
719 $whereStatuses = $whereStatuses ? ($where ? ' AND ' : '') . '(' . implode(' OR ', $whereStatuses) . ')' : '';
720
721
722 $statement = $this->connection->query(
723 "SELECT
724 a.id AS appointment_id,
725 a.bookingStart AS appointment_bookingStart,
726 a.bookingEnd AS appointment_bookingEnd,
727 a.notifyParticipants AS appointment_notifyParticipants,
728 a.serviceId AS appointment_serviceId,
729 a.providerId AS appointment_providerId,
730 a.locationId AS appointment_locationId,
731 a.internalNotes AS appointment_internalNotes,
732 a.status AS appointment_status,
733 a.googleMeetUrl AS appointment_google_meet_url,
734 a.lessonSpace AS appointment_lesson_space,
735 a.zoomMeeting AS appointment_zoom_meeting,
736 a.microsoftTeamsUrl AS appointment_microsoft_teams_url,
737
738 cb.id AS booking_id,
739 cb.customerId AS booking_customerId,
740 cb.status AS booking_status,
741 cb.price AS booking_price,
742 cb.customFields AS booking_customFields,
743 cb.info AS booking_info,
744 cb.utcOffset AS booking_utcOffset,
745 cb.aggregatedPrice AS booking_aggregatedPrice,
746 cb.persons AS booking_persons,
747 cb.duration AS booking_duration,
748 cb.created AS booking_created,
749
750 p.id AS payment_id,
751 p.amount AS payment_amount,
752 p.dateTime AS payment_dateTime,
753 p.status AS payment_status,
754 p.gateway AS payment_gateway,
755 p.gatewayTitle AS payment_gatewayTitle,
756 p.data AS payment_data,
757
758 cbe.id AS bookingExtra_id,
759 cbe.extraId AS bookingExtra_extraId,
760 cbe.customerBookingId AS bookingExtra_customerBookingId,
761 cbe.quantity AS bookingExtra_quantity,
762 cbe.price AS bookingExtra_price,
763 cbe.aggregatedPrice AS bookingExtra_aggregatedPrice,
764
765 c.id AS coupon_id,
766 c.code AS coupon_code,
767 c.discount AS coupon_discount,
768 c.deduction AS coupon_deduction,
769 c.limit AS coupon_limit,
770 c.customerLimit AS coupon_customerLimit,
771 c.status AS coupon_status
772 FROM {$this->appointmentsTable} a
773 INNER JOIN {$this->bookingsTable} cb ON cb.appointmentId = a.id
774 LEFT JOIN {$paymentsTable} p ON p.customerBookingId = cb.id
775 LEFT JOIN {$customerBookingsExtrasTable} cbe ON cbe.customerBookingId = cb.id
776 LEFT JOIN {$couponsTable} c ON c.id = cb.couponId
777 WHERE {$where}
778 AND a.notifyParticipants = 1
779 {$whereStatuses}
780 AND a.id NOT IN (
781 SELECT nl.appointmentId
782 FROM {$this->table} nl
783 INNER JOIN {$this->notificationsTable} n ON nl.notificationId = n.id
784 WHERE n.id = {$notification->getId()->getValue()} AND (nl.sent IS NULL OR nl.sent = 1) AND nl.appointmentId IS NOT NULL
785 )"
786 );
787
788 $rows = $statement->fetchAll();
789 } catch (\Exception $e) {
790 throw new QueryExecutionException('Unable to find appointments in ' . __CLASS__, $e->getCode(), $e);
791 }
792
793 return AppointmentFactory::createCollection($rows);
794 }
795
796 /**
797 * Return a collection of today's past appointments where follow-up notification is not sent and should be.
798 *
799 * @param Notification $notification
800 *
801 * @return Collection
802 * @throws InvalidArgumentException
803 * @throws QueryExecutionException
804 */
805 public function getScheduledEvents($notification)
806 {
807 $eventsTable = EventsTable::getTableName();
808
809 $eventsPeriodsTable = EventsPeriodsTable::getTableName();
810
811 $customerBookingsEventsPeriods = CustomerBookingsToEventsPeriodsTable::getTableName();
812
813 $paymentsTable = PaymentsTable::getTableName();
814
815 $currentDateTime = "STR_TO_DATE('" . DateTimeService::getNowDateTimeInUtc() . "', '%Y-%m-%d %H:%i:%s')";
816
817 $where = "WHERE e.notifyParticipants = 1
818 AND cb.status = 'approved'
819 AND e.status = 'approved'
820 AND e.id NOT IN (
821 SELECT nl.eventId
822 FROM {$this->table} nl
823 INNER JOIN {$this->notificationsTable} n ON nl.notificationId = n.id
824 WHERE n.id = {$notification->getId()->getValue()} AND (nl.sent IS NULL OR nl.sent = 1) AND nl.eventId IS NOT NULL
825 )";
826
827 if ($notification->getTimeAfter()) {
828 $timeAfter = $notification->getTimeAfter()->getValue();
829
830 $lastTime = $timeAfter + 432000;
831
832 $where .= " AND {$currentDateTime} BETWEEN DATE_ADD(ep.periodEnd, INTERVAL {$timeAfter} SECOND) AND DATE_ADD(ep.periodEnd, INTERVAL {$lastTime} SECOND)";
833 } else if ($notification->getTimeBefore()) {
834 $timeBefore = $notification->getTimeBefore()->getValue();
835
836 $where .= " AND ({$currentDateTime} BETWEEN DATE_SUB(ep.periodStart, INTERVAL {$timeBefore} SECOND) AND ep.periodStart) AND (ep.periodStart >= DATE_ADD(p.created, INTERVAL {$timeBefore} SECOND))";
837 }
838
839 try {
840 $statement = $this->connection->query(
841 "SELECT
842 e.id AS event_id,
843 e.name AS event_name,
844 e.status AS event_status,
845 e.bookingOpens AS event_bookingOpens,
846 e.bookingCloses AS event_bookingCloses,
847 e.recurringCycle AS event_recurringCycle,
848 e.recurringOrder AS event_recurringOrder,
849 e.recurringUntil AS event_recurringUntil,
850 e.recurringInterval AS event_recurringInterval,
851 e.bringingAnyone AS event_bringingAnyone,
852 e.bookMultipleTimes AS event_bookMultipleTimes,
853 e.maxCapacity AS event_maxCapacity,
854 e.price AS event_price,
855 e.description AS event_description,
856 e.color AS event_color,
857 e.show AS event_show,
858 e.locationId AS event_locationId,
859 e.customLocation AS event_customLocation,
860 e.parentId AS event_parentId,
861 e.created AS event_created,
862 e.notifyParticipants AS event_notifyParticipants,
863 e.zoomUserId AS event_zoomUserId,
864 e.deposit AS event_deposit,
865 e.depositPayment AS event_depositPayment,
866 e.depositPerPerson AS event_depositPerPerson,
867 e.organizerId AS event_organizerId,
868
869 ep.id AS event_periodId,
870 ep.periodStart AS event_periodStart,
871 ep.periodEnd AS event_periodEnd,
872 ep.lessonSpace AS event_periodLessonSpace,
873 ep.zoomMeeting AS event_periodZoomMeeting,
874 ep.googleMeetUrl AS event_googleMeetUrl,
875
876 cb.id AS booking_id,
877 cb.customerId AS booking_customerId,
878 cb.status AS booking_status,
879 cb.price AS booking_price,
880 cb.customFields AS booking_customFields,
881 cb.info AS booking_info,
882 cb.utcOffset AS booking_utcOffset,
883 cb.aggregatedPrice AS booking_aggregatedPrice,
884 cb.persons AS booking_persons,
885 cb.duration AS booking_duration,
886 cb.created AS booking_created
887 FROM {$eventsTable} e
888 INNER JOIN {$eventsPeriodsTable} ep ON ep.eventId = e.id
889 INNER JOIN {$customerBookingsEventsPeriods} cbe ON cbe.eventPeriodId = ep.id
890 INNER JOIN {$this->bookingsTable} cb ON cb.id = cbe.customerBookingId
891 LEFT JOIN {$paymentsTable} p ON p.customerBookingId = cb.id
892 {$where}"
893 );
894
895 $rows = $statement->fetchAll();
896 } catch (\Exception $e) {
897 throw new QueryExecutionException('Unable to find events in ' . __CLASS__, $e->getCode(), $e);
898 }
899
900 return EventFactory::createCollection($rows);
901 }
902
903 /**
904 * Returns a collection of customers that have birthday on today's date and where notification is not sent
905 *
906 * @param $notificationType
907 *
908 * @return Collection
909 * @throws InvalidArgumentException
910 * @throws QueryExecutionException
911 * @throws \Exception
912 */
913 public function getBirthdayCustomers($notificationType)
914 {
915 $currentDate = "STR_TO_DATE('" . DateTimeService::getNowDateTimeInUtc() . "', '%Y-%m-%d')";
916
917 $params = [
918 ':type' => AbstractUser::USER_ROLE_CUSTOMER,
919 ':statusVisible' => Status::VISIBLE,
920 ];
921
922 try {
923 $statement = $this->connection->prepare(
924 "SELECT * FROM {$this->usersTable} as u
925 WHERE
926 u.type = :type AND
927 u.status = :statusVisible AND
928 MONTH(u.birthday) = MONTH({$currentDate}) AND
929 DAY(u.birthday) = DAY({$currentDate}) AND
930 u.id NOT IN (
931 SELECT nl.userID
932 FROM {$this->table} nl
933 INNER JOIN {$this->notificationsTable} n ON nl.notificationId = n.id
934 WHERE n.name = 'customer_birthday_greeting' AND n.type = '{$notificationType}' AND
935 YEAR(nl.sentDateTime) = YEAR({$currentDate}) AND (nl.sent IS NULL OR nl.sent = 1)
936 )"
937 );
938
939 $statement->execute($params);
940
941 $rows = $statement->fetchAll();
942 } catch (\Exception $e) {
943 throw new QueryExecutionException('Unable to get data from ' . __CLASS__, $e->getCode(), $e);
944 }
945
946 $items = [];
947 foreach ($rows as $row) {
948 $items[] = call_user_func([UserFactory::class, 'create'], $row);
949 }
950
951 return new Collection($items);
952 }
953
954 /**
955 * Returns a collection of undelivered notifications
956 *
957 * @param string $type
958 *
959 * @return Collection
960 * @throws InvalidArgumentException
961 * @throws QueryExecutionException
962 */
963 public function getUndeliveredNotifications($type)
964 {
965 $params = [
966 ':type' => $type,
967 ];
968
969 $currentDateTime = "STR_TO_DATE('" . DateTimeService::getNowDateTimeInUtc() . "', '%Y-%m-%d %H:%i:%s')";
970
971 $pastDateTime =
972 "STR_TO_DATE('" .
973 DateTimeService::getNowDateTimeObjectInUtc()->modify('-1 day')->format('Y-m-d H:i:s') .
974 "', '%Y-%m-%d %H:%i:%s')";
975
976 try {
977 $statement = $this->connection->prepare(
978 "SELECT nl.* FROM {$this->table} nl
979 INNER JOIN {$this->notificationsTable} n ON nl.notificationId = n.id
980 WHERE
981 nl.sent = 0 AND
982 {$currentDateTime} > DATE_ADD(nl.sentDateTime, INTERVAL 300 SECOND) AND
983 {$pastDateTime} < nl.sentDateTime AND
984 nl.data IS NOT NULL AND
985 n.type = :type"
986 );
987
988 $statement->execute($params);
989
990 $rows = $statement->fetchAll();
991 } catch (\Exception $e) {
992 throw new QueryExecutionException('Unable to get data from ' . __CLASS__, $e->getCode(), $e);
993 }
994
995 $items = [];
996
997 foreach ($rows as $row) {
998 $items[] = call_user_func([static::FACTORY, 'create'], $row);
999 }
1000
1001 return new Collection($items);
1002 }
1003
1004 /**
1005 * @param int $userId
1006 * @param string $type
1007 * @param string $entityType
1008 * @param int $entityId
1009 *
1010 * @return Collection
1011 * @throws InvalidArgumentException
1012 * @throws QueryExecutionException
1013 */
1014 public function getSentNotificationsByUserAndEntity($userId, $type, $entityType, $entityId)
1015 {
1016 $entityColumn = '';
1017
1018 switch ($entityType) {
1019 case (Entities::APPOINTMENT):
1020 $entityColumn = 'nl.appointmentId';
1021
1022 break;
1023 case (Entities::EVENT):
1024 $entityColumn = 'nl.eventId';
1025
1026 break;
1027 case (Entities::PACKAGE):
1028 $entityColumn = 'nl.packageCustomerId';
1029
1030 break;
1031 }
1032
1033 $params = [
1034 ':entityId' => $entityId,
1035 ':userId' => $userId,
1036 ':type' => $type,
1037 ];
1038
1039 try {
1040 $statement = $this->connection->prepare(
1041 "SELECT * FROM {$this->table} nl
1042 WHERE nl.userId = :userId AND {$entityColumn} = :entityId AND nl.notificationId IN (SELECT id FROM {$this->notificationsTable} WHERE type = :type)
1043 ORDER BY nl.sentDateTime DESC"
1044 );
1045
1046 $statement->execute($params);
1047
1048 $rows = $statement->fetchAll();
1049 } catch (\Exception $e) {
1050 throw new QueryExecutionException('Unable to get data from ' . __CLASS__, $e->getCode(), $e);
1051 }
1052
1053 $items = [];
1054
1055 foreach ($rows as $row) {
1056 $items[] = call_user_func([static::FACTORY, 'create'], $row);
1057 }
1058
1059 return new Collection($items);
1060 }
1061 }
1062