Add complete Google Sheets integration system for HVAC Community Events: Core Components: - Google Sheets OAuth 2.0 authentication handler - Google Sheets manager for spreadsheet creation and data export - Admin interface with full UI for configuration and operations - Integration with Master Dashboard data aggregation Features Implemented: - Master Report generation with 4 tabs (Overview, Performance, Events, Revenue) - Event-specific spreadsheets with 3 tabs (Details, Attendees, Financial) - Connection status monitoring and testing - Report history tracking and management - AJAX endpoints for all operations - Responsive admin interface with loading states Integration Points: - Page creation during plugin activation (/google-sheets/) - Access control matching Master Dashboard permissions - Navigation integration from Master Dashboard - CSS loading and template support - Extended Master Dashboard data class with additional methods Configuration: - Template configuration file for Google Cloud setup - OAuth 2.0 flow with token management and refresh - Google Sheets API and Drive API integration - Secure token storage using WordPress options 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
696 lines
No EOL
22 KiB
PHP
696 lines
No EOL
22 KiB
PHP
<?php
|
|
/**
|
|
* HVAC Community Events Master Dashboard Data Handler
|
|
*
|
|
* Retrieves and calculates aggregate data across ALL trainers for the Master Dashboard.
|
|
*
|
|
* @package HVAC Community Events
|
|
* @subpackage Includes
|
|
* @author Ben Reed
|
|
* @version 1.0.0
|
|
*/
|
|
|
|
// Exit if accessed directly.
|
|
if ( ! defined( 'ABSPATH' ) ) {
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* Class HVAC_Master_Dashboard_Data
|
|
*
|
|
* Handles fetching and processing aggregate data for the master dashboard.
|
|
*/
|
|
class HVAC_Master_Dashboard_Data {
|
|
|
|
/**
|
|
* Get the total number of events created by ALL trainers.
|
|
*
|
|
* @return int
|
|
*/
|
|
public function get_total_events_count() {
|
|
global $wpdb;
|
|
|
|
// Get all events from all trainers with hvac_trainer or hvac_master_trainer role
|
|
$trainer_users = $this->get_all_trainer_user_ids();
|
|
|
|
if (empty($trainer_users)) {
|
|
return 0;
|
|
}
|
|
|
|
$user_ids_placeholder = implode(',', array_fill(0, count($trainer_users), '%d'));
|
|
|
|
$count = $wpdb->get_var( $wpdb->prepare(
|
|
"SELECT COUNT(*) FROM {$wpdb->posts}
|
|
WHERE post_type = %s
|
|
AND post_author IN ($user_ids_placeholder)
|
|
AND post_status IN ('publish', 'future', 'draft', 'pending', 'private')",
|
|
array_merge([Tribe__Events__Main::POSTTYPE], $trainer_users)
|
|
) );
|
|
|
|
return (int) $count;
|
|
}
|
|
|
|
/**
|
|
* Get the number of upcoming events for ALL trainers.
|
|
*
|
|
* @return int
|
|
*/
|
|
public function get_upcoming_events_count() {
|
|
global $wpdb;
|
|
$today = date( 'Y-m-d H:i:s' );
|
|
|
|
$trainer_users = $this->get_all_trainer_user_ids();
|
|
|
|
if (empty($trainer_users)) {
|
|
return 0;
|
|
}
|
|
|
|
$user_ids_placeholder = implode(',', array_fill(0, count($trainer_users), '%d'));
|
|
|
|
$count = $wpdb->get_var( $wpdb->prepare(
|
|
"SELECT COUNT(*) FROM {$wpdb->posts} p
|
|
LEFT JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = '_EventStartDate'
|
|
WHERE p.post_type = %s
|
|
AND p.post_author IN ($user_ids_placeholder)
|
|
AND p.post_status IN ('publish', 'future')
|
|
AND (pm.meta_value >= %s OR pm.meta_value IS NULL)",
|
|
array_merge([Tribe__Events__Main::POSTTYPE], $trainer_users, [$today])
|
|
) );
|
|
|
|
return (int) $count;
|
|
}
|
|
|
|
/**
|
|
* Get the number of past events for ALL trainers.
|
|
*
|
|
* @return int
|
|
*/
|
|
public function get_past_events_count() {
|
|
global $wpdb;
|
|
$today = date( 'Y-m-d H:i:s' );
|
|
|
|
$trainer_users = $this->get_all_trainer_user_ids();
|
|
|
|
if (empty($trainer_users)) {
|
|
return 0;
|
|
}
|
|
|
|
$user_ids_placeholder = implode(',', array_fill(0, count($trainer_users), '%d'));
|
|
|
|
$count = $wpdb->get_var( $wpdb->prepare(
|
|
"SELECT COUNT(*) FROM {$wpdb->posts} p
|
|
LEFT JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = '_EventEndDate'
|
|
WHERE p.post_type = %s
|
|
AND p.post_author IN ($user_ids_placeholder)
|
|
AND p.post_status IN ('publish', 'private')
|
|
AND pm.meta_value < %s",
|
|
array_merge([Tribe__Events__Main::POSTTYPE], $trainer_users, [$today])
|
|
) );
|
|
|
|
return (int) $count;
|
|
}
|
|
|
|
/**
|
|
* Get the total number of tickets sold across ALL trainers' events.
|
|
*
|
|
* @return int
|
|
*/
|
|
public function get_total_tickets_sold() {
|
|
global $wpdb;
|
|
|
|
$trainer_users = $this->get_all_trainer_user_ids();
|
|
|
|
if (empty($trainer_users)) {
|
|
return 0;
|
|
}
|
|
|
|
$user_ids_placeholder = implode(',', array_fill(0, count($trainer_users), '%d'));
|
|
|
|
$count = $wpdb->get_var( $wpdb->prepare(
|
|
"SELECT COUNT(*) FROM {$wpdb->posts} p
|
|
INNER JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = '_tribe_tpp_event'
|
|
WHERE p.post_type = %s
|
|
AND pm.meta_value IN (
|
|
SELECT ID FROM {$wpdb->posts}
|
|
WHERE post_type = %s
|
|
AND post_author IN ($user_ids_placeholder)
|
|
AND post_status IN ('publish', 'private')
|
|
)",
|
|
array_merge(['tribe_tpp_attendees', 'tribe_events'], $trainer_users)
|
|
) );
|
|
|
|
return (int) $count;
|
|
}
|
|
|
|
/**
|
|
* Get the total revenue generated across ALL trainers' events.
|
|
*
|
|
* @return float
|
|
*/
|
|
public function get_total_revenue() {
|
|
global $wpdb;
|
|
|
|
$trainer_users = $this->get_all_trainer_user_ids();
|
|
|
|
if (empty($trainer_users)) {
|
|
return 0.00;
|
|
}
|
|
|
|
$user_ids_placeholder = implode(',', array_fill(0, count($trainer_users), '%d'));
|
|
|
|
$revenue = $wpdb->get_var( $wpdb->prepare(
|
|
"SELECT SUM(
|
|
CASE
|
|
WHEN pm_price1.meta_value IS NOT NULL THEN CAST(pm_price1.meta_value AS DECIMAL(10,2))
|
|
WHEN pm_price2.meta_value IS NOT NULL THEN CAST(pm_price2.meta_value AS DECIMAL(10,2))
|
|
WHEN pm_price3.meta_value IS NOT NULL THEN CAST(pm_price3.meta_value AS DECIMAL(10,2))
|
|
ELSE 0
|
|
END
|
|
)
|
|
FROM {$wpdb->posts} p
|
|
INNER JOIN {$wpdb->postmeta} pm_event ON p.ID = pm_event.post_id AND pm_event.meta_key = '_tribe_tpp_event'
|
|
LEFT JOIN {$wpdb->postmeta} pm_price1 ON p.ID = pm_price1.post_id AND pm_price1.meta_key = '_tribe_tpp_ticket_price'
|
|
LEFT JOIN {$wpdb->postmeta} pm_price2 ON p.ID = pm_price2.post_id AND pm_price2.meta_key = '_paid_price'
|
|
LEFT JOIN {$wpdb->postmeta} pm_price3 ON p.ID = pm_price3.post_id AND pm_price3.meta_key = '_tribe_tpp_price'
|
|
WHERE p.post_type = %s
|
|
AND pm_event.meta_value IN (
|
|
SELECT ID FROM {$wpdb->posts}
|
|
WHERE post_type = %s
|
|
AND post_author IN ($user_ids_placeholder)
|
|
AND post_status IN ('publish', 'private')
|
|
)",
|
|
array_merge(['tribe_tpp_attendees', 'tribe_events'], $trainer_users)
|
|
) );
|
|
|
|
return (float) ($revenue ?: 0.00);
|
|
}
|
|
|
|
/**
|
|
* Get trainer statistics - count and individual performance data
|
|
*
|
|
* @return array
|
|
*/
|
|
public function get_trainer_statistics() {
|
|
global $wpdb;
|
|
|
|
$trainer_users = $this->get_all_trainer_user_ids();
|
|
|
|
if (empty($trainer_users)) {
|
|
return ['total_trainers' => 0, 'trainer_data' => []];
|
|
}
|
|
|
|
$user_ids_placeholder = implode(',', array_fill(0, count($trainer_users), '%d'));
|
|
|
|
// Get detailed data for each trainer
|
|
$trainer_data = $wpdb->get_results( $wpdb->prepare(
|
|
"SELECT
|
|
u.ID as trainer_id,
|
|
u.display_name,
|
|
u.user_email,
|
|
COUNT(DISTINCT p.ID) as total_events,
|
|
COUNT(DISTINCT CASE WHEN pm_start.meta_value >= %s THEN p.ID END) as upcoming_events,
|
|
COUNT(DISTINCT CASE WHEN pm_end.meta_value < %s THEN p.ID END) as past_events,
|
|
COALESCE(attendee_stats.total_attendees, 0) as total_attendees,
|
|
COALESCE(revenue_stats.total_revenue, 0) as total_revenue
|
|
FROM {$wpdb->users} u
|
|
LEFT JOIN {$wpdb->posts} p ON u.ID = p.post_author AND p.post_type = %s AND p.post_status IN ('publish', 'future', 'draft', 'pending', 'private')
|
|
LEFT JOIN {$wpdb->postmeta} pm_start ON p.ID = pm_start.post_id AND pm_start.meta_key = '_EventStartDate'
|
|
LEFT JOIN {$wpdb->postmeta} pm_end ON p.ID = pm_end.post_id AND pm_end.meta_key = '_EventEndDate'
|
|
LEFT JOIN (
|
|
SELECT
|
|
trainer_events.post_author,
|
|
COUNT(*) as total_attendees
|
|
FROM {$wpdb->posts} attendees
|
|
INNER JOIN {$wpdb->postmeta} pm_event ON attendees.ID = pm_event.post_id AND pm_event.meta_key = '_tribe_tpp_event'
|
|
INNER JOIN {$wpdb->posts} trainer_events ON pm_event.meta_value = trainer_events.ID
|
|
WHERE attendees.post_type = 'tribe_tpp_attendees'
|
|
AND trainer_events.post_author IN ($user_ids_placeholder)
|
|
GROUP BY trainer_events.post_author
|
|
) attendee_stats ON u.ID = attendee_stats.post_author
|
|
LEFT JOIN (
|
|
SELECT
|
|
trainer_events.post_author,
|
|
SUM(
|
|
CASE
|
|
WHEN pm_price1.meta_value IS NOT NULL THEN CAST(pm_price1.meta_value AS DECIMAL(10,2))
|
|
WHEN pm_price2.meta_value IS NOT NULL THEN CAST(pm_price2.meta_value AS DECIMAL(10,2))
|
|
WHEN pm_price3.meta_value IS NOT NULL THEN CAST(pm_price3.meta_value AS DECIMAL(10,2))
|
|
ELSE 0
|
|
END
|
|
) as total_revenue
|
|
FROM {$wpdb->posts} attendees
|
|
INNER JOIN {$wpdb->postmeta} pm_event ON attendees.ID = pm_event.post_id AND pm_event.meta_key = '_tribe_tpp_event'
|
|
INNER JOIN {$wpdb->posts} trainer_events ON pm_event.meta_value = trainer_events.ID
|
|
LEFT JOIN {$wpdb->postmeta} pm_price1 ON attendees.ID = pm_price1.post_id AND pm_price1.meta_key = '_tribe_tpp_ticket_price'
|
|
LEFT JOIN {$wpdb->postmeta} pm_price2 ON attendees.ID = pm_price2.post_id AND pm_price2.meta_key = '_paid_price'
|
|
LEFT JOIN {$wpdb->postmeta} pm_price3 ON attendees.ID = pm_price3.post_id AND pm_price3.meta_key = '_tribe_tpp_price'
|
|
WHERE attendees.post_type = 'tribe_tpp_attendees'
|
|
AND trainer_events.post_author IN ($user_ids_placeholder)
|
|
GROUP BY trainer_events.post_author
|
|
) revenue_stats ON u.ID = revenue_stats.post_author
|
|
WHERE u.ID IN ($user_ids_placeholder)
|
|
GROUP BY u.ID
|
|
ORDER BY total_revenue DESC",
|
|
array_merge([
|
|
date('Y-m-d H:i:s'), // for upcoming events
|
|
date('Y-m-d H:i:s'), // for past events
|
|
Tribe__Events__Main::POSTTYPE
|
|
], $trainer_users, $trainer_users, $trainer_users)
|
|
) );
|
|
|
|
return [
|
|
'total_trainers' => count($trainer_users),
|
|
'trainer_data' => $trainer_data
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Get the data needed for the events table on the master dashboard.
|
|
* Shows ALL events from ALL trainers with additional trainer information.
|
|
*
|
|
* @param array $args Query arguments
|
|
* @return array Contains 'events' array and 'pagination' data
|
|
*/
|
|
public function get_events_table_data( $args = array() ) {
|
|
// Default arguments
|
|
$defaults = array(
|
|
'status' => 'all',
|
|
'search' => '',
|
|
'orderby' => 'date',
|
|
'order' => 'DESC',
|
|
'page' => 1,
|
|
'per_page' => 10,
|
|
'date_from' => '',
|
|
'date_to' => '',
|
|
'trainer_id' => '' // New filter for specific trainer
|
|
);
|
|
|
|
$args = wp_parse_args( $args, $defaults );
|
|
|
|
return $this->get_events_table_data_direct( $args );
|
|
}
|
|
|
|
/**
|
|
* Get events table data using direct database queries (shows ALL trainer events)
|
|
*/
|
|
private function get_events_table_data_direct( $args ) {
|
|
global $wpdb;
|
|
|
|
$events_data = [];
|
|
$valid_statuses = array( 'publish', 'future', 'draft', 'pending', 'private' );
|
|
$trainer_users = $this->get_all_trainer_user_ids();
|
|
|
|
if (empty($trainer_users)) {
|
|
return [
|
|
'events' => [],
|
|
'pagination' => [
|
|
'total_items' => 0,
|
|
'total_pages' => 0,
|
|
'current_page' => 1,
|
|
'per_page' => $args['per_page'],
|
|
'has_prev' => false,
|
|
'has_next' => false
|
|
]
|
|
];
|
|
}
|
|
|
|
$user_ids_placeholder = implode(',', array_fill(0, count($trainer_users), '%d'));
|
|
|
|
// Build WHERE clauses
|
|
$where_clauses = array(
|
|
'p.post_type = %s',
|
|
"p.post_author IN ($user_ids_placeholder)"
|
|
);
|
|
$where_values = array_merge([Tribe__Events__Main::POSTTYPE], $trainer_users);
|
|
|
|
// Status filter
|
|
if ( 'all' === $args['status'] || ! in_array( $args['status'], $valid_statuses, true ) ) {
|
|
$status_placeholders = implode( ',', array_fill( 0, count( $valid_statuses ), '%s' ) );
|
|
$where_clauses[] = "p.post_status IN ($status_placeholders)";
|
|
$where_values = array_merge( $where_values, $valid_statuses );
|
|
} else {
|
|
$where_clauses[] = 'p.post_status = %s';
|
|
$where_values[] = $args['status'];
|
|
}
|
|
|
|
// Search filter
|
|
if ( ! empty( $args['search'] ) ) {
|
|
$where_clauses[] = 'p.post_title LIKE %s';
|
|
$where_values[] = '%' . $wpdb->esc_like( $args['search'] ) . '%';
|
|
}
|
|
|
|
// Trainer filter
|
|
if ( ! empty( $args['trainer_id'] ) && is_numeric( $args['trainer_id'] ) ) {
|
|
$where_clauses[] = 'p.post_author = %d';
|
|
$where_values[] = (int) $args['trainer_id'];
|
|
}
|
|
|
|
// Date range filters
|
|
if ( ! empty( $args['date_from'] ) ) {
|
|
$where_clauses[] = "pm_start.meta_value >= %s";
|
|
$where_values[] = $args['date_from'] . ' 00:00:00';
|
|
}
|
|
|
|
if ( ! empty( $args['date_to'] ) ) {
|
|
$where_clauses[] = "pm_start.meta_value <= %s";
|
|
$where_values[] = $args['date_to'] . ' 23:59:59';
|
|
}
|
|
|
|
// Build ORDER BY clause
|
|
$order_column = 'p.post_date';
|
|
switch ( $args['orderby'] ) {
|
|
case 'name':
|
|
$order_column = 'p.post_title';
|
|
break;
|
|
case 'status':
|
|
$order_column = 'p.post_status';
|
|
break;
|
|
case 'date':
|
|
$order_column = 'COALESCE(pm_start.meta_value, p.post_date)';
|
|
break;
|
|
case 'trainer':
|
|
$order_column = 'u.display_name';
|
|
break;
|
|
case 'capacity':
|
|
$order_column = 'capacity';
|
|
break;
|
|
case 'sold':
|
|
$order_column = 'sold';
|
|
break;
|
|
case 'revenue':
|
|
$order_column = 'revenue';
|
|
break;
|
|
}
|
|
$order_dir = ( strtoupper( $args['order'] ) === 'ASC' ) ? 'ASC' : 'DESC';
|
|
|
|
// Calculate offset for pagination
|
|
$offset = ( $args['page'] - 1 ) * $args['per_page'];
|
|
|
|
// Build the complete SQL query
|
|
$where_sql = implode( ' AND ', $where_clauses );
|
|
|
|
// First, get total count for pagination
|
|
$count_sql = "SELECT COUNT(DISTINCT p.ID)
|
|
FROM {$wpdb->posts} p
|
|
LEFT JOIN {$wpdb->postmeta} pm_start ON p.ID = pm_start.post_id AND pm_start.meta_key = '_EventStartDate'
|
|
LEFT JOIN {$wpdb->users} u ON p.post_author = u.ID
|
|
WHERE $where_sql";
|
|
|
|
$total_items = $wpdb->get_var( $wpdb->prepare( $count_sql, $where_values ) );
|
|
|
|
// Main query with joins for all needed data including trainer information
|
|
$sql = "SELECT
|
|
p.ID,
|
|
p.post_title,
|
|
p.post_status,
|
|
p.post_date,
|
|
p.post_author,
|
|
u.display_name as trainer_name,
|
|
u.user_email as trainer_email,
|
|
COALESCE(pm_start.meta_value, p.post_date) as event_date,
|
|
COALESCE(
|
|
(SELECT COUNT(*)
|
|
FROM {$wpdb->posts} attendees
|
|
INNER JOIN {$wpdb->postmeta} pm_event ON attendees.ID = pm_event.post_id AND pm_event.meta_key = '_tribe_tpp_event'
|
|
WHERE attendees.post_type = 'tribe_tpp_attendees' AND pm_event.meta_value = p.ID),
|
|
0
|
|
) as sold,
|
|
COALESCE(
|
|
(SELECT SUM(
|
|
CASE
|
|
WHEN pm_price1.meta_value IS NOT NULL THEN CAST(pm_price1.meta_value AS DECIMAL(10,2))
|
|
WHEN pm_price2.meta_value IS NOT NULL THEN CAST(pm_price2.meta_value AS DECIMAL(10,2))
|
|
WHEN pm_price3.meta_value IS NOT NULL THEN CAST(pm_price3.meta_value AS DECIMAL(10,2))
|
|
ELSE 0
|
|
END
|
|
)
|
|
FROM {$wpdb->posts} attendees
|
|
INNER JOIN {$wpdb->postmeta} pm_event ON attendees.ID = pm_event.post_id AND pm_event.meta_key = '_tribe_tpp_event'
|
|
LEFT JOIN {$wpdb->postmeta} pm_price1 ON attendees.ID = pm_price1.post_id AND pm_price1.meta_key = '_tribe_tpp_ticket_price'
|
|
LEFT JOIN {$wpdb->postmeta} pm_price2 ON attendees.ID = pm_price2.post_id AND pm_price2.meta_key = '_paid_price'
|
|
LEFT JOIN {$wpdb->postmeta} pm_price3 ON attendees.ID = pm_price3.post_id AND pm_price3.meta_key = '_tribe_tpp_price'
|
|
WHERE attendees.post_type = 'tribe_tpp_attendees' AND pm_event.meta_value = p.ID),
|
|
0
|
|
) as revenue,
|
|
50 as capacity
|
|
FROM {$wpdb->posts} p
|
|
LEFT JOIN {$wpdb->postmeta} pm_start ON p.ID = pm_start.post_id AND pm_start.meta_key = '_EventStartDate'
|
|
LEFT JOIN {$wpdb->users} u ON p.post_author = u.ID
|
|
WHERE $where_sql
|
|
ORDER BY $order_column $order_dir
|
|
LIMIT %d OFFSET %d";
|
|
|
|
$query_values = array_merge( $where_values, array( $args['per_page'], $offset ) );
|
|
$events = $wpdb->get_results( $wpdb->prepare( $sql, $query_values ) );
|
|
|
|
if ( ! empty( $events ) ) {
|
|
foreach ( $events as $event ) {
|
|
$event_id = $event->ID;
|
|
$start_date_ts = $event->event_date ? strtotime( $event->event_date ) : strtotime( $event->post_date );
|
|
|
|
// Build event data array (matching template expectations with trainer info)
|
|
$events_data[] = array(
|
|
'id' => $event_id,
|
|
'name' => $event->post_title,
|
|
'status' => $event->post_status,
|
|
'start_date_ts' => $start_date_ts,
|
|
'link' => get_permalink( $event_id ),
|
|
'organizer_id' => $event->post_author,
|
|
'trainer_name' => $event->trainer_name,
|
|
'trainer_email' => $event->trainer_email,
|
|
'capacity' => (int) $event->capacity,
|
|
'sold' => (int) $event->sold,
|
|
'revenue' => (float) $event->revenue,
|
|
);
|
|
}
|
|
}
|
|
|
|
// Calculate pagination data
|
|
$total_pages = ceil( $total_items / $args['per_page'] );
|
|
|
|
return array(
|
|
'events' => $events_data,
|
|
'pagination' => array(
|
|
'total_items' => $total_items,
|
|
'total_pages' => $total_pages,
|
|
'current_page' => $args['page'],
|
|
'per_page' => $args['per_page'],
|
|
'has_prev' => $args['page'] > 1,
|
|
'has_next' => $args['page'] < $total_pages
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get all user IDs who have hvac_trainer or hvac_master_trainer role
|
|
*
|
|
* @return array Array of user IDs
|
|
*/
|
|
private function get_all_trainer_user_ids() {
|
|
$trainer_users = get_users(array(
|
|
'role__in' => array('hvac_trainer', 'hvac_master_trainer'),
|
|
'fields' => 'ID'
|
|
));
|
|
|
|
return array_map('intval', $trainer_users);
|
|
}
|
|
|
|
/**
|
|
* Get summary statistics for Google Sheets integration
|
|
*
|
|
* @return array
|
|
*/
|
|
public function get_google_sheets_summary_data() {
|
|
return [
|
|
'events_summary' => $this->get_events_summary_for_sheets(),
|
|
'attendees_summary' => $this->get_attendees_summary_for_sheets(),
|
|
'trainers_summary' => $this->get_trainer_statistics(),
|
|
'ticket_purchases_summary' => $this->get_ticket_purchases_summary_for_sheets()
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Get events summary data formatted for Google Sheets
|
|
*/
|
|
private function get_events_summary_for_sheets() {
|
|
// This will be implemented when we create the Google Sheets integration
|
|
return [
|
|
'total_events' => $this->get_total_events_count(),
|
|
'upcoming_events' => $this->get_upcoming_events_count(),
|
|
'past_events' => $this->get_past_events_count(),
|
|
'total_revenue' => $this->get_total_revenue()
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Get attendees summary data formatted for Google Sheets
|
|
*/
|
|
private function get_attendees_summary_for_sheets() {
|
|
// This will be implemented when we create the Google Sheets integration
|
|
return [
|
|
'total_attendees' => $this->get_total_tickets_sold()
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Get ticket purchases summary data formatted for Google Sheets
|
|
*/
|
|
private function get_ticket_purchases_summary_for_sheets() {
|
|
// This will be implemented when we create the Google Sheets integration
|
|
return [
|
|
'total_tickets_sold' => $this->get_total_tickets_sold(),
|
|
'total_revenue' => $this->get_total_revenue()
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Get completed events count
|
|
*
|
|
* @return int
|
|
*/
|
|
public function get_completed_events_count() {
|
|
return $this->get_past_events_count();
|
|
}
|
|
|
|
/**
|
|
* Get active trainers count
|
|
*
|
|
* @return int
|
|
*/
|
|
public function get_active_trainers_count() {
|
|
return count($this->get_all_trainer_user_ids());
|
|
}
|
|
|
|
/**
|
|
* Get trainer performance data for Google Sheets
|
|
*
|
|
* @return array
|
|
*/
|
|
public function get_trainer_performance_data() {
|
|
$trainer_stats = $this->get_trainer_statistics();
|
|
$performance_data = array();
|
|
|
|
foreach ($trainer_stats['trainer_data'] as $trainer) {
|
|
$performance_data[] = array(
|
|
'name' => $trainer->display_name,
|
|
'events' => $trainer->total_events,
|
|
'tickets' => $trainer->total_attendees,
|
|
'revenue' => $trainer->total_revenue
|
|
);
|
|
}
|
|
|
|
return $performance_data;
|
|
}
|
|
|
|
/**
|
|
* Get all events data formatted for Google Sheets
|
|
*
|
|
* @return array
|
|
*/
|
|
public function get_all_events_data() {
|
|
$events_table_data = $this->get_events_table_data(array(
|
|
'per_page' => 999, // Get all events
|
|
'orderby' => 'date',
|
|
'order' => 'DESC'
|
|
));
|
|
|
|
$formatted_events = array();
|
|
|
|
foreach ($events_table_data['events'] as $event) {
|
|
$formatted_events[] = array(
|
|
'title' => $event['name'],
|
|
'trainer_name' => $event['trainer_name'],
|
|
'date' => date('M j, Y', $event['start_date_ts']),
|
|
'status' => ucfirst($event['status']),
|
|
'tickets' => $event['sold'],
|
|
'revenue' => $event['revenue']
|
|
);
|
|
}
|
|
|
|
return $formatted_events;
|
|
}
|
|
|
|
/**
|
|
* Get monthly revenue data for analytics
|
|
*
|
|
* @return array
|
|
*/
|
|
public function get_monthly_revenue_data() {
|
|
global $wpdb;
|
|
|
|
$trainer_users = $this->get_all_trainer_user_ids();
|
|
|
|
if (empty($trainer_users)) {
|
|
return array();
|
|
}
|
|
|
|
$user_ids_placeholder = implode(',', array_fill(0, count($trainer_users), '%d'));
|
|
|
|
// Get events grouped by month for the last 12 months
|
|
$months_data = $wpdb->get_results( $wpdb->prepare(
|
|
"SELECT
|
|
DATE_FORMAT(pm_start.meta_value, '%%Y-%%m') as month,
|
|
COUNT(DISTINCT p.ID) as event_count
|
|
FROM {$wpdb->posts} p
|
|
LEFT JOIN {$wpdb->postmeta} pm_start ON p.ID = pm_start.post_id AND pm_start.meta_key = '_EventStartDate'
|
|
WHERE p.post_type = %s
|
|
AND p.post_author IN ($user_ids_placeholder)
|
|
AND p.post_status = 'publish'
|
|
AND pm_start.meta_value >= DATE_SUB(NOW(), INTERVAL 12 MONTH)
|
|
GROUP BY DATE_FORMAT(pm_start.meta_value, '%%Y-%%m')
|
|
ORDER BY month DESC",
|
|
array_merge([Tribe__Events__Main::POSTTYPE], $trainer_users)
|
|
) );
|
|
|
|
// Calculate revenue for each month
|
|
$monthly_data = array();
|
|
foreach ($months_data as $month_data) {
|
|
$month_revenue = $this->get_month_revenue($month_data->month, $trainer_users);
|
|
$monthly_data[] = array(
|
|
'month' => date('M Y', strtotime($month_data->month . '-01')),
|
|
'events' => $month_data->event_count,
|
|
'revenue' => $month_revenue
|
|
);
|
|
}
|
|
|
|
return $monthly_data;
|
|
}
|
|
|
|
/**
|
|
* Get revenue for a specific month
|
|
*
|
|
* @param string $month Format: Y-m
|
|
* @param array $trainer_users
|
|
* @return float
|
|
*/
|
|
private function get_month_revenue($month, $trainer_users) {
|
|
global $wpdb;
|
|
|
|
$user_ids_placeholder = implode(',', array_fill(0, count($trainer_users), '%d'));
|
|
|
|
// Get revenue for all events in this month
|
|
$revenue = $wpdb->get_var( $wpdb->prepare(
|
|
"SELECT SUM(
|
|
CASE
|
|
WHEN pm_price1.meta_value IS NOT NULL THEN CAST(pm_price1.meta_value AS DECIMAL(10,2))
|
|
WHEN pm_price2.meta_value IS NOT NULL THEN CAST(pm_price2.meta_value AS DECIMAL(10,2))
|
|
WHEN pm_price3.meta_value IS NOT NULL THEN CAST(pm_price3.meta_value AS DECIMAL(10,2))
|
|
ELSE 0
|
|
END
|
|
)
|
|
FROM {$wpdb->posts} attendees
|
|
INNER JOIN {$wpdb->postmeta} pm_event ON attendees.ID = pm_event.post_id AND pm_event.meta_key = '_tribe_tpp_event'
|
|
INNER JOIN {$wpdb->posts} events ON pm_event.meta_value = events.ID
|
|
LEFT JOIN {$wpdb->postmeta} pm_start ON events.ID = pm_start.post_id AND pm_start.meta_key = '_EventStartDate'
|
|
LEFT JOIN {$wpdb->postmeta} pm_price1 ON attendees.ID = pm_price1.post_id AND pm_price1.meta_key = '_tribe_tpp_ticket_price'
|
|
LEFT JOIN {$wpdb->postmeta} pm_price2 ON attendees.ID = pm_price2.post_id AND pm_price2.meta_key = '_paid_price'
|
|
LEFT JOIN {$wpdb->postmeta} pm_price3 ON attendees.ID = pm_price3.post_id AND pm_price3.meta_key = '_tribe_tpp_price'
|
|
WHERE attendees.post_type = 'tribe_tpp_attendees'
|
|
AND events.post_author IN ($user_ids_placeholder)
|
|
AND DATE_FORMAT(pm_start.meta_value, '%%Y-%%m') = %s",
|
|
array_merge($trainer_users, [$month])
|
|
) );
|
|
|
|
return (float) ($revenue ?: 0.00);
|
|
}
|
|
} |