PluginProbe
Booktics – Appointment Booking Calendar for Service Businesses / 1.0.3
Booktics – Appointment Booking Calendar for Service Businesses v1.0.3
1.0.29 1.0.28 1.0.27 1.0.26 1.0.25 1.0.24 1.0.23 1.0.22 1.0.21 1.0.20 1.0.19 trunk 1.0.0 1.0.1 1.0.10 1.0.11 1.0.12 1.0.13 1.0.14 1.0.15 1.0.16 1.0.17 1.0.18 1.0.2 1.0.3 All 31 releases
booktics / trunk / core / dashboard / controllers / dashboard-controller.php

dashboard-controller.php in Booktics – Appointment Booking Calendar for Service Businesses 1.0.3, at trunk/core/dashboard/controllers/dashboard-controller.php

494 lines 15.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Booktics\Dashboard\Controllers;
4
5 use Booktics\Abstracts\Base_Rest_Controller;
6 use Booktics\Abstracts\Booktics_Database;
7 use Booktics\Models\Appointment_Model;
8 use Booktics\Models\Customer_Model;
9 use Booktics\Models\Order_Model;
10 use WP_HTTP_Response;
11 use WP_REST_Request;
12 use WP_REST_Server;
13
14 /**
15 * Dashboard REST Controller
16 */
17 class Dashboard_Controller extends Base_Rest_Controller {
18
19 /**
20 * Database instance
21 *
22 * @var Booktics_Database
23 */
24 protected $db;
25
26 /**
27 * Supported report types
28 */
29 public const REPORT_TYPES = array(
30 'total_bookings',
31 'total_revenue',
32 'total_customers',
33 'booking_reports',
34 );
35
36 /**
37 * Constructor for DashboardController
38 *
39 * @param Booktics_Database $db
40 */
41 public function __construct() {
42 $this->db = new Booktics_Database();
43 }
44
45 /**
46 * Register all routes
47 *
48 * @return void
49 */
50 public function register_routes(): void {
51 register_rest_route(
52 'booktics/v1',
53 '/dashboard',
54 array(
55 'methods' => WP_REST_Server::READABLE,
56 'callback' => array( $this, 'get_dashboard_data' ),
57 'permission_callback' => array( $this, 'check_permission' ),
58 'args' => array(
59 'total_bookings' => array(
60 'type' => 'object',
61 'required' => false,
62 'properties' => array(
63 'start_date' => array(
64 'type' => 'string',
65 'format' => 'date',
66 ),
67 'end_date' => array(
68 'type' => 'string',
69 'format' => 'date',
70 ),
71 ),
72 ),
73 'total_revenue' => array(
74 'type' => 'object',
75 'required' => false,
76 'properties' => array(
77 'start_date' => array(
78 'type' => 'string',
79 'format' => 'date',
80 ),
81 'end_date' => array(
82 'type' => 'string',
83 'format' => 'date',
84 ),
85 ),
86 ),
87 'total_customers' => array(
88 'type' => 'object',
89 'required' => false,
90 'properties' => array(
91 'start_date' => array(
92 'type' => 'string',
93 'format' => 'date',
94 ),
95 'end_date' => array(
96 'type' => 'string',
97 'format' => 'date',
98 ),
99 ),
100 ),
101 'booking_reports' => array(
102 'type' => 'object',
103 'required' => false,
104 'properties' => array(
105 'start_date' => array(
106 'type' => 'string',
107 'format' => 'date',
108 ),
109 'end_date' => array(
110 'type' => 'string',
111 'format' => 'date',
112 ),
113 ),
114 ),
115 ),
116 )
117 );
118 }
119
120 /**
121 * Get dashboard data
122 *
123 * @param WP_REST_Request $request
124 *
125 * @return WP_HTTP_Response
126 */
127 public function get_dashboard_data( WP_REST_Request $request ) {
128 $data = array();
129
130 foreach ( self::REPORT_TYPES as $report_type ) {
131 $report_data = $request->get_param( $report_type );
132
133 if ( is_array( $report_data ) && isset( $report_data['start_date'], $report_data['end_date'] ) ) {
134 $date_range = $report_data['start_date'] . ' to ' . $report_data['end_date'];
135 $data[ $report_type ] = $this->get_report_data( $report_type, $date_range );
136 }
137 }
138
139 if ( empty( $data ) ) {
140 $data = new \stdClass();
141 }
142
143 return $this->response( $data );
144 }
145
146 /**
147 * Get report data based on type
148 *
149 * @param string $report_type
150 * @param string $date_range
151 *
152 * @return mixed
153 */
154 private function get_report_data( $report_type, $date_range ) {
155 switch ( $report_type ) {
156 case 'total_bookings':
157 return $this->get_total_bookings( $date_range );
158 case 'total_revenue':
159 return $this->get_total_revenue( $date_range );
160 case 'total_customers':
161 return $this->get_total_customers( $date_range );
162 case 'booking_reports':
163 return $this->get_booking_reports( $date_range );
164 default:
165 return null;
166 }
167 }
168
169 /**
170 * Get total bookings count for a date range
171 *
172 * @param string $date_range
173 *
174 * @return array
175 */
176 private function get_total_bookings( $date_range ) {
177 $appointments = new Appointment_Model();
178
179 $dates = $this->parse_date_range( $date_range );
180 if ( ! $dates ) {
181 return array(
182 'total' => 0,
183 'percentage' => '0.00',
184 'trend' => 'neutral',
185 );
186 }
187
188 // Get current period bookings
189 $current_query = array(
190 'date_query' => array(
191 'after' => $dates['start'],
192 'before' => $dates['end'],
193 'inclusive' => true,
194 ),
195 );
196 $current_results = $appointments->all( $current_query );
197 $current_bookings = count( $current_results );
198
199 // Calculate previous period dates
200 $previous_dates = $this->get_previous_period_dates( $dates );
201
202 // Get previous period bookings
203 $previous_query = array(
204 'date_query' => array(
205 'after' => $previous_dates['start'],
206 'before' => $previous_dates['end'],
207 ),
208 'status' => 'confirmed',
209 );
210 $previous_results = $appointments->all( $previous_query );
211 $previous_bookings = count( $previous_results );
212
213 // Calculate percentage change
214 $percentage_change = 0;
215 $trend = 'neutral';
216 if ( $previous_bookings != 0 ) {
217 $percentage_change = ( ( $current_bookings - $previous_bookings ) / $previous_bookings ) * 100;
218 $trend = $this->get_trend( $percentage_change );
219 }
220
221 return array(
222 'total' => $current_bookings,
223 'percentage_changed' => number_format( $percentage_change, 2 ),
224 'trend' => $trend,
225 );
226 }
227
228 /**
229 * Get total revenue for a date range
230 *
231 * @param string $date_range
232 *
233 * @return array
234 */
235 private function get_total_revenue( $date_range ) {
236 $orders = new Order_Model( $this->db );
237
238 $dates = $this->parse_date_range( $date_range );
239 if ( ! $dates ) {
240 return array(
241 'total' => '0.00',
242 'percentage_changed' => '0.00',
243 'trend' => 'neutral',
244 );
245 }
246
247 global $wpdb;
248 $table_name = $orders->get_table_name();
249
250 // Get current period revenue
251 $current_revenue = (float) $wpdb->get_var(
252 $wpdb->prepare(
253 'SELECT SUM(total) as total FROM ' . $wpdb->prefix . $wpdb->_real_escape( $table_name ) . ' WHERE created_at BETWEEN %s AND %s AND payment_status = %s',
254 $dates['start'] . ' 00:00:00',
255 $dates['end'] . ' 23:59:59',
256 'completed'
257 )
258 ) ?: 0;
259 // Calculate previous period dates
260 $previous_dates = $this->get_previous_period_dates( $dates );
261
262 // Get previous period revenue
263 $previous_revenue = (float) $wpdb->get_var(
264 $wpdb->prepare(
265 'SELECT SUM(total) as total FROM ' . $wpdb->prefix . $wpdb->_real_escape( $table_name ) . ' ' .
266 'WHERE created_at BETWEEN %s AND %s AND payment_status = %s',
267 $previous_dates['start'] . ' 00:00:00',
268 $previous_dates['end'] . ' 23:59:59',
269 'completed'
270 )
271 ) ?: 0;
272
273 // Calculate percentage change
274 $percentage_change = 0;
275 $trend = 'neutral';
276
277 if ( $previous_revenue != 0 ) {
278 $percentage_change = ( ( $current_revenue - $previous_revenue ) / $previous_revenue ) * 100;
279 $trend = $this->get_trend( $percentage_change );
280 }
281
282 return array(
283 'total' => number_format( $current_revenue, 2 ),
284 'percentage_changed' => number_format( $percentage_change, 2 ),
285 'trend' => $trend,
286 );
287 }
288
289 /**
290 * Get total customers count for a date range
291 *
292 * @param string $date_range
293 *
294 * @return array
295 */
296 private function get_total_customers( $date_range ) {
297 $customers = new Customer_Model();
298
299 $dates = $this->parse_date_range( $date_range );
300 if ( ! $dates ) {
301 return array(
302 'total' => 0,
303 'percentage_changed' => '0.00',
304 'trend' => 'neutral',
305 );
306 }
307
308 // Get current period customers
309 $current_query = array(
310 'date_query' => array(
311 'after' => $dates['start'] . ' 00:00:00',
312 'before' => $dates['end'] . ' 23:59:59',
313 ),
314 'role' => 'booktics_customer',
315 'meta_query' => array(
316 'key' => 'status',
317 'value' => 1,
318 'compare' => '=',
319 ),
320 );
321 $current_results = $customers->all( $current_query );
322 $current_customers = count( $current_results['items'] );
323
324 // Calculate previous period dates
325 $previous_dates = $this->get_previous_period_dates( $dates );
326
327 // Get previous period customers
328 $previous_query = array(
329 'date_query' => array(
330 'after' => $previous_dates['start'] . ' 00:00:00',
331 'before' => $previous_dates['end'] . ' 23:59:59',
332 ),
333 'role' => 'booktics_customer',
334 'meta_query' => array(
335 'key' => 'status',
336 'value' => 1,
337 'compare' => '=',
338 ),
339 );
340 $previous_results = $customers->all( $previous_query );
341 $previous_customers = count( $previous_results['items'] );
342
343 // Calculate percentage change
344 $percentage_change = 0;
345 $trend = 'neutral';
346 if ( $previous_customers != 0 ) {
347 $percentage_change = ( ( $current_customers - $previous_customers ) / $previous_customers ) * 100;
348 $trend = $this->get_trend( $percentage_change );
349 }
350
351 return array(
352 'total' => $current_customers,
353 'percentage_changed' => number_format( $percentage_change, 2 ),
354 'trend' => $trend,
355 );
356 }
357
358 /**
359 * Get booking reports by date with completed and cancelled counts
360 *
361 * @param string $date_range
362 *
363 * @return array
364 */
365 private function get_booking_reports( $date_range ) {
366 $appointments = new Appointment_Model();
367
368 $dates = $this->parse_date_range( $date_range );
369 if ( ! $dates ) {
370 return array();
371 }
372
373 // Generate date range array
374 $start_date = new \DateTime( $dates['start'] );
375 $end_date = new \DateTime( $dates['end'] );
376 $interval = new \DateInterval( 'P1D' );
377 $period = new \DatePeriod( $start_date, $interval, $end_date );
378
379 $reports = array();
380 foreach ( $period as $date ) {
381 $date_str = $date->format( 'Y-m-d' );
382 $reports[ $date_str ] = array(
383 'completed' => 0,
384 'cancelled' => 0,
385 );
386 }
387
388 // Query appointments using PostModel's methods
389 $query = array(
390 'post_type' => $appointments->get_post_type(),
391 'post_status' => array( 'active', 'pending' ),
392 // Using the statuses from PostModel's $accepted_statuses
393 'date_query' => array(
394 array(
395 'after' => $dates['start'],
396 'before' => $dates['end'],
397 'inclusive' => true,
398 ),
399 ),
400 'meta_query' => array(
401 array(
402 'key' => 'status',
403 'value' => array( 'completed', 'cancelled' ),
404 'compare' => 'IN',
405 ),
406 ),
407 );
408
409 $appointments = Appointment_Model::all( $query );
410
411 foreach ( $appointments as $appointment ) {
412 $date_str = $appointment->date;
413 if ( isset( $reports[ $date_str ] ) ) {
414 if ( $appointment->status === 'completed' ) {
415 ++$reports[ $date_str ]['completed'];
416 } elseif ( $appointment->status === 'cancelled' ) {
417 ++$reports[ $date_str ]['cancelled'];
418 }
419 }
420 }
421
422 return $reports;
423 }
424
425 /**
426 * Parse date range string into start and end dates
427 *
428 * @param string $date_range
429 *
430 * @return array|null
431 */
432 private function parse_date_range( $date_range ) {
433 if ( empty( $date_range ) ) {
434 return null;
435 }
436
437 $dates = explode( 'to', $date_range );
438 if ( count( $dates ) !== 2 ) {
439 return null;
440 }
441
442 $start = trim( $dates[0] );
443 $end = trim( $dates[1] );
444
445 return array(
446 'start' => $start,
447 'end' => $end,
448 );
449 }
450
451 /**
452 * Get the trend based on the percentage change.
453 *
454 * @param float $percentage_change Percentage change from previous period
455 *
456 * @return string 'positive', 'negative', or 'neutral'
457 */
458 private function get_trend( $percentage_change ) {
459 if ( $percentage_change > 0 ) {
460 return 'positive';
461 } elseif ( $percentage_change < 0 ) {
462 return 'negative';
463 }
464
465 return 'neutral';
466 }
467
468 /**
469 * Calculate the previous period start and end dates given the current period start and end dates.
470 *
471 * @param array $dates Array of start and end dates
472 *
473 * @return array Array of previous period start and end dates
474 */
475 private function get_previous_period_dates( $dates ) {
476 $previous_start = strtotime( $dates['start'] . ' -1 day' );
477 $previous_end = strtotime( $dates['end'] . ' -1 day' );
478
479 return array(
480 'start' => gmdate( 'Y-m-d', $previous_start ),
481 'end' => gmdate( 'Y-m-d', $previous_end ),
482 );
483 }
484
485 /**
486 * Check if user has permission to access dashboard data
487 *
488 * @return bool
489 */
490 public function check_permission() {
491 return current_user_can( 'manage_options' );
492 }
493 }
494