PluginProbe
Bookit — Booking & Appointment Calendar / 2.6.0.5
Bookit — Booking & Appointment Calendar v2.6.0.5
2.6.0.5 2.6.0.4 2.6.0.3 2.6.0.2 2.6.0.1 2.6.0 trunk 1.2 1.2.2 1.2.3 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 All 62 releases
bookit / includes / classes / database / Appointments.php

Appointments.php in Bookit — Booking & Appointment Calendar 2.6.0.5, at includes/classes/database/Appointments.php

571 lines 18.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Bookit\Classes\Database;
4
5 use Bookit\Classes\Vendor\DatabaseModel;
6
7 class Appointments extends DatabaseModel {
8
9 public static $pending = 'pending';
10 public static $approved = 'approved';
11 public static $cancelled = 'cancelled';
12 public static $complete = 'complete';
13 public static $delete = 'delete'; // if deleted
14 public static $statusList = array( 'pending', 'approved', 'cancelled', 'delete' );
15 /**
16 * Create Table
17 */
18 public static function create_table() {
19 global $wpdb;
20 require_once ABSPATH . 'wp-admin/includes/upgrade.php';
21
22 $table_name = self::_table();
23 $primary_key = self::$primary_key;
24
25 $sql = "CREATE TABLE {$table_name} (
26 id INT UNSIGNED NOT NULL AUTO_INCREMENT,
27 service_id INT UNSIGNED NOT NULL,
28 staff_id INT UNSIGNED NOT NULL,
29 customer_id INT UNSIGNED NOT NULL,
30 date_timestamp INT NOT NULL,
31 start_time INT NOT NULL,
32 end_time INT NOT NULL,
33 price DECIMAL(10,2) NOT NULL DEFAULT 0.00,
34 status VARCHAR(20) NOT NULL DEFAULT 'pending',
35 notes longtext DEFAULT NULL,
36 created_from ENUM('front', 'back') NOT NULL DEFAULT 'front',
37 created_at DATETIME NOT NULL,
38 updated_at DATETIME NOT NULL,
39 PRIMARY KEY ({$primary_key}),
40 INDEX `idx_service_id` (`service_id`),
41 INDEX `idx_staff_id` (`staff_id`),
42 INDEX `idx_customer_id` (`customer_id`),
43 INDEX `idx_date_timestamp` (`date_timestamp`),
44 INDEX `idx_start_time` (`start_time`),
45 INDEX `idx_end_time` (`end_time`),
46 INDEX `idx_status` (`status`)
47 ) {$wpdb->get_charset_collate()};";
48
49 maybe_create_table( $table_name, $sql );
50 }
51
52 /**
53 * Create Appointment with payment
54 */
55 public static function create_appointment( $data ) {
56
57 $appointment_data = array(
58 'staff_id' => $data['staff_id'],
59 'customer_id' => $data['customer_id'],
60 'service_id' => $data['service_id'],
61 'status' => $data['status'],
62 'date_timestamp' => $data['date_timestamp'],
63 'start_time' => $data['start_time'],
64 'end_time' => $data['end_time'],
65 'price' => number_format( (float) $data['clear_price'], 2, '.', '' ),
66 'notes' => $data['notes'],
67 'created_at' => wp_date( 'Y-m-d H:i:s' ),
68 'updated_at' => wp_date( 'Y-m-d H:i:s' ),
69 );
70
71 self::insert( $appointment_data );
72 $appointment_id = self::insert_id();
73
74 /** create payment **/
75 if ( 0 == (float) $appointment_data['price'] ) {
76 $data['payment_method'] = Payments::$freeType;
77 $data['payment_status'] = Payments::$completeType;
78 }
79
80 $payment_data = array(
81 'appointment_id' => $appointment_id,
82 'type' => ( ! empty( $data['payment_method'] ) ) ? $data['payment_method'] : Payments::$defaultType,
83 'status' => ( ! empty( $data['payment_status'] ) ) ? $data['payment_status'] : Payments::$defaultStatus,
84 'total' => $appointment_data['price'],
85 'created_at' => wp_date( 'Y-m-d H:i:s' ),
86 'updated_at' => wp_date( 'Y-m-d H:i:s' ),
87 );
88
89 Payments::insert( $payment_data );
90
91 return $appointment_id;
92 }
93
94 /**
95 * Update Appointment with payment
96 */
97 public static function update_appointment( $data, $id ) {
98
99 $appointment = array(
100 'staff_id' => $data['staff_id'],
101 'service_id' => $data['service_id'],
102 'date_timestamp' => $data['date_timestamp'],
103 'start_time' => $data['start_time'],
104 'end_time' => $data['end_time'],
105 'price' => number_format( (float) $data['price'], 2, '.', '' ),
106 'status' => $data['status'],
107 'notes' => $data['notes'],
108 'created_from' => $data['created_from'],
109 'updated_at' => wp_date( 'Y-m-d H:i:s' ),
110 );
111
112 self::update( $appointment, array( 'id' => $id ) );
113
114 /** update payment **/
115 if ( Payments::$freeType == $data['payment_method'] ) {
116 $data['payment_status'] = Payments::$completeType;
117 }
118
119 $payment_data = array(
120 'type' => $data['payment_method'],
121 'status' => $data['payment_status'],
122 'total' => $appointment['price'],
123 'updated_at' => wp_date( 'Y-m-d H:i:s' ),
124 );
125 Payments::update( $payment_data, array( 'appointment_id' => $id ) );
126 }
127
128 /**
129 * Change Appointment status to delete and delete payment
130 */
131 public static function delete_appointment( $id ) {
132
133 $payment = Payments::get( 'appointment_id', $id );
134 $notes['payment'] = $payment;
135
136 /** update appointment , add payment info before delete */
137 $appointment = array(
138 'status' => self::$delete,
139 'notes' => serialize( $notes ),
140 'updated_at' => wp_date( 'Y-m-d H:i:s' ),
141 );
142 self::update( $appointment, array( 'id' => $id ) );
143
144 /** delete payment **/
145 Payments::delete( $payment->id );
146 }
147
148
149 /**
150 * Get Customer Appointments
151 * @param $customer_id int
152 * @return mixed
153 */
154 public static function customer_appointments( int $customer_id ) {
155 global $wpdb;
156 $sql = sprintf(
157 'SELECT `%1$s`.*
158 FROM `%1$s`
159 WHERE customer_id = %%d ORDER BY `%1$s`.id ASC',
160 esc_sql( self::_table() )
161 );
162 return $wpdb->get_results( $wpdb->prepare( $sql, $customer_id ) );
163 }
164
165 /**
166 * Get Category Appointments
167 * @param $category_id int
168 * @return mixed
169 */
170 public static function category_appointments( $category_id ) {
171 global $wpdb;
172 $sql = sprintf(
173 'SELECT `%1$s`.*
174 FROM `%1$s`
175 LEFT JOIN `%2$s` ON `%1$s`.service_id = `%2$s`.id
176 WHERE `%2$s`.category_id = %%d',
177 esc_sql( self::_table() ),
178 esc_sql( Services::_table() )
179 );
180 return $wpdb->get_results( $wpdb->prepare( $sql, intval( $category_id ) ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
181 }
182 /**
183 * Get Service Appointments
184 * @param $service_id int
185 * @return mixed
186 */
187 public static function service_appointments( int $service_id ) {
188 global $wpdb;
189 $sql = sprintf(
190 'SELECT `%1$s`.*
191 FROM `%1$s`
192 WHERE service_id = %%d ORDER BY `%1$s`.id ASC',
193 esc_sql( self::_table() )
194 );
195 return $wpdb->get_results( $wpdb->prepare( $sql, $service_id ) );
196 }
197
198 /**
199 * Get Staff Appointments
200 * @param $staff_id int
201 * @return mixed
202 */
203 public static function staff_appointments( $staff_id ) {
204 global $wpdb;
205 $sql = sprintf(
206 'SELECT `%1$s`.*
207 FROM `%1$s`
208 WHERE staff_id = %%d ORDER BY `%1$s`.id ASC',
209 esc_sql( self::_table() )
210 );
211
212 return $wpdb->get_results( $wpdb->prepare( $sql, $staff_id ) );
213 }
214
215 /**
216 * Get Rows with Pagination
217 *
218 * @param $limit
219 * @param $offset
220 * @param string $status
221 * @param string $sort
222 * @param string $order
223 *
224 * @return mixed
225 */
226 public static function get_paged( $limit, $offset, $status = '', $sort = '', $order = '', $filter = array() ) {
227 global $wpdb;
228
229 $search_sql = '';
230 $search_values = array();
231 if ( ! empty( $filter['search'] ) ) {
232 $like = '%' . $wpdb->esc_like( $filter['search'] ) . '%';
233 $ct = esc_sql( Customers::_table() );
234 $search_sql = " AND (`{$ct}`.phone LIKE %s OR `{$ct}`.full_name LIKE %s OR `{$ct}`.email LIKE %s)";
235 $search_values = array( $like, $like, $like );
236 }
237
238 $at = esc_sql( self::_table() );
239 $ct = esc_sql( Customers::_table() );
240 $st = esc_sql( Staff::_table() );
241 $svt = esc_sql( Services::_table() );
242 $pt = esc_sql( Payments::_table() );
243 $del = esc_sql( self::$delete );
244 $pk = empty( $sort ) ? esc_sql( static::$primary_key ) : esc_sql( $sort );
245 $od = empty( $order ) ? 'DESC' : esc_sql( $order );
246 $lim = intval( $limit );
247 $off = intval( $offset );
248
249 $status_sql = ! empty( $status ) ? " AND `{$at}`.status = '" . esc_sql( $status ) . "'" : '';
250 $start_sql = ! empty( $filter['start'] ) ? " AND `{$at}`.start_time >= " . intval( $filter['start'] ) : '';
251 $end_sql = ! empty( $filter['end'] ) ? " AND `{$at}`.end_time <= " . intval( $filter['end'] ) : '';
252
253 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
254 $sql = "SELECT `{$at}`.*,
255 `{$pt}`.type as payment_method,
256 `{$pt}`.status as payment_status,
257 `{$pt}`.total as total,
258 `{$pt}`.notes as payment_notes,
259 `{$ct}`.full_name as customer_name,
260 `{$ct}`.email as customer_email,
261 `{$ct}`.phone as customer_phone,
262 `{$st}`.full_name as staff_name,
263 `{$svt}`.title as service_name
264 FROM `{$at}`
265 LEFT JOIN `{$ct}` ON `{$at}`.customer_id = `{$ct}`.id
266 LEFT JOIN `{$st}` ON `{$at}`.staff_id = `{$st}`.id
267 LEFT JOIN `{$svt}` ON `{$at}`.service_id = `{$svt}`.id
268 LEFT JOIN `{$pt}` ON `{$at}`.id = `{$pt}`.appointment_id
269 WHERE `{$at}`.status != '{$del}'
270 {$status_sql} {$start_sql} {$end_sql} {$search_sql}
271 ORDER BY `{$at}`.`{$pk}` {$od}
272 LIMIT {$lim} OFFSET {$off}";
273
274 if ( ! empty( $search_values ) ) {
275 return $wpdb->get_results( $wpdb->prepare( $sql, $search_values ), ARRAY_A ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
276 }
277
278 return $wpdb->get_results( $sql, ARRAY_A ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
279 }
280
281 /**
282 * Export All Rows
283 *
284 * @return mixed
285 */
286 public static function export_all() {
287 global $wpdb;
288 return $wpdb->get_results(
289 sprintf(
290 'SELECT `%1$s`.*,
291 `%5$s`.type as payment_method,
292 `%5$s`.status as payment_status,
293 `%5$s`.total as total,
294 `%2$s`.full_name as customer,
295 `%2$s`.phone as customer_phone,
296 `%3$s`.full_name as staff,
297 `%4$s`.title as service
298 FROM `%1$s`
299 LEFT JOIN `%2$s` ON `%1$s`.customer_id = `%2$s`.id
300 LEFT JOIN `%3$s` ON `%1$s`.staff_id = `%3$s`.id
301 LEFT JOIN `%4$s` ON `%1$s`.service_id = `%4$s`.id
302 LEFT JOIN `%5$s` ON `%1$s`.id = `%5$s`.appointment_id
303 ORDER BY `%1$s`.`%6$s` DESC',
304 esc_sql( self::_table() ), // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
305 esc_sql( Customers::_table() ), // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
306 esc_sql( Staff::_table() ), // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
307 esc_sql( Services::_table() ), // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
308 esc_sql( Payments::_table() ), // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
309 esc_sql( static::$primary_key ) // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
310 ),
311 ARRAY_A
312 );
313 }
314
315 /**
316 * Check Appointment
317 * @param $data
318 * @return mixed
319 */
320 public static function checkAppointment( $data ) {
321 global $wpdb;
322 $sql = sprintf(
323 'SELECT `%1$s`.id FROM `%1$s`
324 WHERE staff_id = %%d
325 AND service_id = %%d
326 AND status != "%2$s"
327 AND status != "%3$s"
328 AND date_timestamp = %%d
329 AND ( start_time <= %%d AND end_time >= %%d )',
330 esc_sql( self::_table() ),
331 esc_sql( self::$cancelled ),
332 esc_sql( self::$delete )
333 );
334 return $wpdb->get_var(
335 $wpdb->prepare(
336 $sql, // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
337 intval( $data['staff_id'] ),
338 intval( $data['service_id'] ),
339 intval( $data['date_timestamp'] ),
340 intval( $data['start_time'] ),
341 intval( $data['end_time'] )
342 )
343 );
344 }
345
346 /**
347 * Get Months Appointments
348 * @param $data
349 * @return mixed
350 */
351 public static function month_appointments( $data ) {
352 global $wpdb;
353 $sql = sprintf(
354 'SELECT date_timestamp, COUNT(*) appointments FROM `%s`
355 WHERE service_id = %%d AND status NOT IN ( "%2$s", "%3$s" )
356 AND ( ( date_timestamp = %%d AND start_time >= %%d ) OR date_timestamp BETWEEN %%d AND %%d )
357 GROUP BY date_timestamp',
358 esc_sql( self::_table() ),
359 esc_sql( self::$cancelled ),
360 esc_sql( self::$delete )
361 );
362 return $wpdb->get_results(
363 $wpdb->prepare(
364 $sql, // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
365 intval( $data['service_id'] ),
366 intval( $data['today_timestamp'] ),
367 intval( $data['now_timestamp'] ),
368 intval( $data['start_timestamp'] ),
369 intval( $data['end_timestamp'] )
370 )
371 );
372 }
373
374 /**
375 * Get Day Appointments
376 * @param $data
377 * @return mixed
378 */
379 public static function day_appointments( $data ) {
380 global $wpdb;
381 $sql = sprintf(
382 'SELECT `id`, `staff_id`, `start_time`, `end_time`, `status` FROM `%s` WHERE date_timestamp = %%d %s %s AND status NOT IN ( "%4$s", "%5$s" ) ORDER BY `%1$s`.`%6$s`',
383 esc_sql( self::_table() ),
384 ( ! empty( $data['service_id'] ) ) ? sprintf( "AND service_id = %d", intval( $data['service_id'] ) ) : '',
385 ( ! empty( $data['staff_id'] ) ) ? sprintf( "AND staff_id = %d", intval( $data['staff_id'] ) ) : '',
386 esc_sql( self::$cancelled ),
387 esc_sql( self::$delete ),
388 'start_time'
389 );
390 return $wpdb->get_results(
391 $wpdb->prepare(
392 $sql, // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
393 intval( $data['date_timestamp'] )
394 )
395 );
396 }
397
398 /**
399 * Get Pending Appointments Count
400 * @return mixed
401 */
402 public static function pending_appointments() {
403 global $wpdb;
404 $date_utc = new \DateTime( 'now', new \DateTimeZone( 'UTC' ) );
405 $sql = sprintf(
406 'SELECT COUNT(*) FROM `%s` WHERE status = "%s" AND start_time >= %%d',
407 esc_sql( self::_table() ),
408 esc_sql( self::$pending )
409 );
410 return $wpdb->get_var(
411 $wpdb->prepare(
412 $sql, // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
413 $date_utc->getTimestamp()
414 )
415 );
416 }
417
418 /**
419 * Get Total Count of Rows by status
420 * @param $status
421 * @return mixed
422 */
423 public static function get_appointments_count( $status, $filter = array() ) {
424 global $wpdb;
425
426 $search_sql = '';
427 $search_values = array();
428 if ( ! empty( $filter['search'] ) ) {
429 $like = '%' . $wpdb->esc_like( $filter['search'] ) . '%';
430 $ct = esc_sql( Customers::_table() );
431 $search_sql = " AND (`{$ct}`.phone LIKE %s OR `{$ct}`.full_name LIKE %s OR `{$ct}`.email LIKE %s)";
432 $search_values = array( $like, $like, $like );
433 }
434
435 $at = esc_sql( self::_table() );
436 $ct = esc_sql( Customers::_table() );
437 $del = esc_sql( self::$delete );
438 $status_sql = ! empty( $status ) ? " AND status = '" . esc_sql( $status ) . "'" : '';
439 $start_sql = ! empty( $filter['start'] ) ? " AND `{$at}`.start_time >= " . intval( $filter['start'] ) : '';
440 $end_sql = ! empty( $filter['end'] ) ? " AND `{$at}`.end_time <= " . intval( $filter['end'] ) : '';
441
442 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
443 $sql = "SELECT COUNT(*) FROM `{$at}` LEFT JOIN `{$ct}` ON `{$at}`.customer_id = `{$ct}`.id WHERE `{$at}`.status != '{$del}' {$status_sql} {$start_sql} {$end_sql} {$search_sql}";
444
445 if ( ! empty( $search_values ) ) {
446 return $wpdb->get_var( $wpdb->prepare( $sql, $search_values ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
447 }
448
449 return $wpdb->get_var( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
450 }
451
452 /**
453 * Change Appointment Status
454 * @param $id
455 * @param $status
456 */
457 public static function change_status( $id, $status ) {
458 $data = array( 'status' => $status );
459 $where = array( 'id' => $id );
460 self::update( $data, $where );
461 }
462
463 /**
464 * Change Payment Status | used just for pro version
465 * @param $id
466 * @param $payment_status
467 */
468 public static function change_payment_status( $id, $payment_status ) {
469 Payments::change_payment_status( $id, $payment_status );
470 }
471
472 /**
473 * Get Admin Day Appointments
474 * @param $data
475 * @return mixed
476 */
477 public static function get_full_appointment_by_id( $id ) {
478 global $wpdb;
479 $sql = sprintf(
480 'SELECT %1$s.*,
481 %2$s.email as customer_email,
482 %2$s.full_name as customer_name,
483 %2$s.phone as customer_phone,
484 %3$s.id as staff_id,
485 %3$s.email as staff_email,
486 %3$s.full_name as staff_name,
487 %3$s.phone as staff_phone,
488 %4$s.total as total,
489 %4$s.type as payment_method,
490 %4$s.status as payment_status,
491 `%4$s`.id as payment_id,
492 `%5$s`.title as service_name
493 FROM `%1$s`
494 LEFT JOIN `%2$s` ON `%1$s`.customer_id = `%2$s`.id
495 LEFT JOIN `%3$s` ON `%1$s`.staff_id = `%3$s`.id
496 LEFT JOIN `%4$s` ON `%1$s`.id = `%4$s`.appointment_id
497 LEFT JOIN `%5$s` ON `%1$s`.service_id = `%5$s`.id
498 WHERE `%1$s`.id = %%d',
499 esc_sql( self::_table() ),
500 esc_sql( Customers::_table() ),
501 esc_sql( Staff::_table() ),
502 esc_sql( Payments::_table() ),
503 esc_sql( Services::_table() )
504 );
505
506 return $wpdb->get_row( $wpdb->prepare( $sql, intval( $id ) ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
507 }
508
509 /**
510 * Get appointments short data filter by date range
511 * @return array
512 */
513 public static function appointments_by_date_full( $start, $end, $filter_data = array() ) {
514 global $wpdb;
515 $sql = sprintf(
516 'SELECT
517 `%1$s`.*,
518 `%2$s`.title as service,
519 `%2$s`.icon_id as icon,
520 `%3$s`.email as customer_email,
521 `%3$s`.full_name as customer_name,
522 `%3$s`.phone as customer_phone,
523 `%4$s`.full_name as staff_name,
524 `%5$s`.type as payment_method,
525 `%5$s`.status as payment_status,
526 `%5$s`.total as total
527 FROM `%1$s`
528 LEFT JOIN `%2$s` ON `%1$s`.service_id = `%2$s`.id
529 LEFT JOIN `%3$s` ON `%1$s`.customer_id = `%3$s`.id
530 LEFT JOIN `%4$s` ON `%1$s`.staff_id = `%4$s`.id
531 LEFT JOIN `%5$s` ON `%1$s`.id = `%5$s`.appointment_id
532 WHERE `%1$s`.status != "%6$s" AND date_timestamp BETWEEN %%d AND %%d
533 %7$s %8$s %9$s
534 ORDER BY `%1$s`.start_time',
535 esc_sql( self::_table() ),
536 esc_sql( Services::_table() ),
537 esc_sql( Customers::_table() ),
538 esc_sql( Staff::_table() ),
539 esc_sql( Payments::_table() ),
540 esc_sql( self::$delete ),
541 ( ! empty( $filter_data['service_ids'] ) ) ? sprintf( "AND service_id IN ( %s )", esc_sql( $filter_data['service_ids'] ) ) : '',
542 ( ! empty( $filter_data['staff_id'] ) ) ? sprintf( "AND staff_id = %d", intval( $filter_data['staff_id'] ) ) : '',
543 ( ! empty( $filter_data['status'] ) ) ? sprintf( "AND `%s`.status = '%s'", esc_sql( self::_table() ), esc_sql( $filter_data['status'] ) ) : ''
544 );
545
546 return $wpdb->get_results( $wpdb->prepare( $sql, intval( $start ), intval( $end ) ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
547 }
548
549 /**
550 * Get total appointments for service | staff
551 * @return array
552 */
553 public static function get_total_active_assosiated_appointments( $service_ids = '', $staff_ids = '', $customer_ids = '' ) {
554 global $wpdb;
555
556 $sql = sprintf(
557 'SELECT COUNT(`%1$s`.id)
558 FROM `%1$s`
559 WHERE start_time > %%d
560 %2$s %3$s %4$s',
561 esc_sql( self::_table() ),
562 ( ! empty( $service_ids ) ) ? sprintf( "AND service_id IN ( %s )", esc_sql( $service_ids ) ) : '',
563 ( ! empty( $staff_ids ) ) ? sprintf( "AND staff_id IN ( %s )", esc_sql( $staff_ids ) ) : '',
564 ( ! empty( $customer_ids ) ) ? sprintf( "AND customer_id IN ( %s )", esc_sql( $customer_ids ) ) : ''
565 );
566
567 $now = current_time( 'timestamp' ); // phpcs:ignore WordPress.DateTime.CurrentTimeTimestamp.Requested
568 return $wpdb->get_var( $wpdb->prepare( $sql, intval( $now ) ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
569 }
570 }
571