PluginProbe
Booktics – Appointment Booking Calendar for Service Businesses / 1.0.28
Booktics – Appointment Booking Calendar for Service Businesses v1.0.28
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
← All changes | core/dashboard/controllers/dashboard-controller.php +506 -493 1.0.1 → 1.0.28 View file →
@@ -1,493 +1,506 @@
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 -}
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\Guest_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 = sanitize_text_field( $report_data['start_date'] ) . ' to ' . sanitize_text_field( $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 + 'post_status' => array( 'completed', 'approved' ),
191 + 'date_query' => array(
192 + 'after' => $dates['start'],
193 + 'before' => $dates['end'],
194 + 'inclusive' => true,
195 + ),
196 + );
197 + $current_query = apply_filters( 'booktics_appointment_args', $current_query );
198 + $current_results = $appointments->all( $current_query );
199 + $current_bookings = count( $current_results );
200 +
201 + // Calculate previous period dates
202 + $previous_dates = $this->get_previous_period_dates( $dates );
203 +
204 + // Get previous period bookings
205 + $previous_query = array(
206 + 'date_query' => array(
207 + 'after' => $previous_dates['start'],
208 + 'before' => $previous_dates['end'],
209 + ),
210 + 'post_status' => array( 'completed', 'approved' ),
211 + );
212 + $previous_query = apply_filters( 'booktics_appointment_args', $previous_query );
213 + $previous_results = $appointments->all( $previous_query );
214 + $previous_bookings = count( $previous_results );
215 +
216 + // Calculate percentage change
217 + $percentage_change = 0;
218 + $trend = 'neutral';
219 + if ( 0 != $previous_bookings ) {
220 + $percentage_change = ( ( $current_bookings - $previous_bookings ) / $previous_bookings ) * 100;
221 + $trend = $this->get_trend( $percentage_change );
222 + }
223 +
224 + return array(
225 + 'total' => $current_bookings,
226 + 'percentage_changed' => number_format( $percentage_change, 2 ),
227 + 'trend' => $trend,
228 + );
229 + }
230 +
231 + /**
232 + * Get total revenue for a date range
233 + *
234 + * @param string $date_range
235 + *
236 + * @return array
237 + */
238 + private function get_total_revenue( $date_range ) {
239 + $orders = new Order_Model( $this->db );
240 +
241 + $dates = $this->parse_date_range( $date_range );
242 + if ( ! $dates ) {
243 + return array(
244 + 'total' => '0.00',
245 + 'percentage_changed' => '0.00',
246 + 'trend' => 'neutral',
247 + );
248 + }
249 +
250 + // Get order IDs to filter by (if needed)
251 + $order_ids = $this->get_order_ids();
252 +
253 + // Get current period revenue using Order_Model method
254 + $current_revenue = $orders->get_revenue_by_date_range(
255 + $dates['start'],
256 + $dates['end'],
257 + $order_ids
258 + );
259 +
260 + // Calculate previous period dates
261 + $previous_dates = $this->get_previous_period_dates( $dates );
262 +
263 + // Get previous period revenue using Order_Model method
264 + $previous_revenue = $orders->get_revenue_by_date_range(
265 + $previous_dates['start'],
266 + $previous_dates['end'],
267 + $order_ids
268 + );
269 +
270 + // Calculate percentage change
271 + $percentage_change = 0;
272 + $trend = 'neutral';
273 +
274 + // Ensure both values are valid numbers before calculation
275 + if ( is_numeric( $previous_revenue ) && 0 != $previous_revenue && is_numeric( $current_revenue ) ) {
276 + $percentage_change = ( ( $current_revenue - $previous_revenue ) / $previous_revenue ) * 100;
277 +
278 + // Check if the result is a valid number
279 + if ( !is_finite( $percentage_change ) ) {
280 + $percentage_change = 0;
281 + } else {
282 + $trend = $this->get_trend( $percentage_change );
283 + }
284 + }
285 +
286 + return array(
287 + 'total' => round( (float) $current_revenue, 2 ),
288 + 'percentage_changed' => round( (float) $percentage_change, 2 ),
289 + 'trend' => $trend,
290 + );
291 + }
292 +
293 + /**
294 + * Get order ids for a date range
295 + *
296 + * @param array $dates
297 + *
298 + * @return array
299 + */
300 + private function get_order_ids() {
301 + $order_ids = array();
302 + $args = array();
303 + $args = apply_filters( 'booktics_appointment_args', $args );
304 + $appointments = ( new Appointment_Model() )->all( $args );
305 + foreach ( $appointments as $appointment ) {
306 + $order_ids[] = $appointment->order_id;
307 + }
308 +
309 + return $order_ids;
310 + }
311 +
312 + /**
313 + * Get total customers count for a date range
314 + *
315 + * @param string $date_range
316 + *
317 + * @return array
318 + */
319 + private function get_total_customers( $date_range ) {
320 + $db = new Booktics_Database();
321 + $guests = new Guest_Model( $db );
322 +
323 + $dates = $this->parse_date_range( $date_range );
324 + if ( ! $dates ) {
325 + return array(
326 + 'total' => 0,
327 + 'percentage_changed' => '0.00',
328 + 'trend' => 'neutral',
329 + );
330 + }
331 + if ( empty( $dates['start'] ) || empty( $dates['end'] ) ) {
332 + return array(
333 + 'total' => 0,
334 + 'percentage_changed' => '0.00',
335 + 'trend' => 'neutral',
336 + );
337 + }
338 + // Get current period customers
339 + $current_results = $guests->count(
340 + array(
341 + 'created_at' => array( '>=', $dates['start'] . ' 00:00:00' ),
342 + 'created_at' => array( '<=', $dates['end'] . ' 23:59:59' ),
343 + )
344 + );
345 +
346 + // Calculate previous period dates
347 + $previous_dates = $this->get_previous_period_dates( $dates );
348 +
349 + // Get previous period customers
350 + $previous_results = $guests->count(
351 + array(
352 + 'created_at' => array( '>=', $previous_dates['start'] . ' 00:00:00' ),
353 + 'created_at' => array( '<=', $previous_dates['end'] . ' 23:59:59' ),
354 + )
355 + );
356 +
357 + // Calculate percentage change
358 + $percentage_change = 0;
359 + $trend = 'neutral';
360 + if ( 0 != $previous_results ) {
361 + $percentage_change = ( ( $current_results - $previous_results ) / $previous_results ) * 100;
362 + $trend = $this->get_trend( $percentage_change );
363 + }
364 +
365 + return array(
366 + 'total' => $current_results,
367 + 'percentage_changed' => number_format( $percentage_change, 2 ),
368 + 'trend' => $trend,
369 + );
370 + }
371 +
372 + /**
373 + * Get booking reports by date with completed and cancelled counts
374 + *
375 + * @param string $date_range
376 + *
377 + * @return array
378 + */
379 + private function get_booking_reports( $date_range ) {
380 + $appointments = new Appointment_Model();
381 +
382 + $dates = $this->parse_date_range( $date_range );
383 + if ( ! $dates ) {
384 + return array();
385 + }
386 +
387 + // Generate date range array
388 + $start_date = new \DateTime( $dates['start'] );
389 + $end_date = new \DateTime( $dates['end'] );
390 + $interval = new \DateInterval( 'P1D' );
391 + $period = new \DatePeriod( $start_date, $interval, $end_date );
392 +
393 + $reports = array();
394 + foreach ( $period as $date ) {
395 + $date_str = $date->format( 'Y-m-d' );
396 + $reports[ $date_str ] = array(
397 + 'completed' => 0,
398 + 'cancel' => 0,
399 + );
400 + }
401 +
402 + // Query appointments using PostModel's methods
403 + $query = array(
404 + 'post_type' => $appointments->get_post_type(),
405 + 'post_status' => array( 'completed', 'cancelled', 'canceled' ),
406 + 'date_query' => array(
407 + array(
408 + 'after' => $dates['start'],
409 + 'before' => $dates['end'],
410 + 'inclusive' => true,
411 + ),
412 + ),
413 + );
414 + $query = apply_filters( 'booktics_appointment_args', $query );
415 + $appointments = Appointment_Model::all( $query );
416 +
417 + foreach ( $appointments as $appointment ) {
418 + $date_str = $appointment->date;
419 + if ( isset( $reports[ $date_str ] ) ) {
420 + if ( $appointment->status === 'completed' ) {
421 + ++$reports[ $date_str ]['completed'];
422 + } elseif ( $appointment->status === 'cancelled' || $appointment->status === 'canceled' ) { //both spellings are used for backward compatibility
423 + ++$reports[ $date_str ]['cancel'];
424 + }
425 + }
426 + }
427 +
428 + return $reports;
429 + }
430 +
431 + /**
432 + * Parse date range string into start and end dates
433 + *
434 + * @param string $date_range
435 + *
436 + * @return array|null
437 + */
438 + private function parse_date_range( $date_range ) {
439 + if ( empty( $date_range ) ) {
440 + return null;
441 + }
442 +
443 + $dates = explode( 'to', $date_range );
444 + if ( count( $dates ) !== 2 ) {
445 + return null;
446 + }
447 +
448 + $start = trim( $dates[0] );
449 + $end = trim( $dates[1] );
450 +
451 + return array(
452 + 'start' => $start,
453 + 'end' => $end,
454 + );
455 + }
456 +
457 + /**
458 + * Get the trend based on the percentage change.
459 + *
460 + * @param float $percentage_change Percentage change from previous period
461 + *
462 + * @return string 'positive', 'negative', or 'neutral'
463 + */
464 + private function get_trend( $percentage_change ) {
465 + if ( $percentage_change > 0 ) {
466 + return 'positive';
467 + } elseif ( $percentage_change < 0 ) {
468 + return 'negative';
469 + }
470 +
471 + return 'neutral';
472 + }
473 +
474 + /**
475 + * Calculate the previous period start and end dates given the current period start and end dates.
476 + *
477 + * @param array $dates Array of start and end dates
478 + *
479 + * @return array Array of previous period start and end dates
480 + */
481 + private function get_previous_period_dates( $dates ) {
482 + $start_time_in_seconds = strtotime( $dates['start'] );
483 + $end_time_in_seconds = strtotime( $dates['end'] );
484 +
485 + // Calculate the gap in days (inclusive)
486 + $gap_days = round( ( $end_time_in_seconds - $start_time_in_seconds ) / DAY_IN_SECONDS );
487 +
488 + // Subtract the gap from both start and end
489 + $previous_start = strtotime( "-$gap_days days", $start_time_in_seconds );
490 + $previous_end = strtotime( "-$gap_days days", $end_time_in_seconds );
491 +
492 + return array(
493 + 'start' => gmdate( 'Y-m-d', $previous_start ),
494 + 'end' => gmdate( 'Y-m-d', $previous_end ),
495 + );
496 + }
497 +
498 + /**
499 + * Check if user has permission to access dashboard data
500 + *
501 + * @return bool
502 + */
503 + public function check_permission() {
504 + return current_user_can( 'manage_options' );
505 + }
506 +}