user_id = (int) $user_id; } /** * Get the total number of events created by the trainer. * * @return int */ public function get_total_events_count() { global $wpdb; // Use direct database query to avoid TEC query hijacking $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->posts} WHERE post_type = %s AND post_author = %d AND post_status IN ('publish', 'future', 'draft', 'pending', 'private')", Tribe__Events__Main::POSTTYPE, $this->user_id ) ); return (int) $count; } /** * Get the number of upcoming events for the trainer. * * @return int */ public function get_upcoming_events_count() { global $wpdb; $today = date( 'Y-m-d H:i:s' ); // Use direct database query to avoid TEC query hijacking $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 = %d AND p.post_status IN ('publish', 'future') AND (pm.meta_value >= %s OR pm.meta_value IS NULL)", Tribe__Events__Main::POSTTYPE, $this->user_id, $today ) ); return (int) $count; } /** * Get the number of past events for the trainer. * * @return int */ public function get_past_events_count() { global $wpdb; $today = date( 'Y-m-d H:i:s' ); // Use direct database query to avoid TEC query hijacking $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 = %d AND p.post_status IN ('publish', 'private') AND pm.meta_value < %s", Tribe__Events__Main::POSTTYPE, $this->user_id, $today ) ); return (int) $count; } /** * Get the total number of tickets sold across all the trainer's events. * * @return int */ public function get_total_tickets_sold() { global $wpdb; // Use direct database query to count attendees for user's events $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->posts} p WHERE p.post_type = %s AND p.post_parent IN ( SELECT ID FROM {$wpdb->posts} WHERE post_type = %s AND post_author = %d AND post_status IN ('publish', 'private') )", 'tribe_tpp_attendees', 'tribe_events', $this->user_id ) ); return (int) $count; } /** * Get the total revenue generated across all the trainer's events. * * @return float */ public function get_total_revenue() { global $wpdb; // Use direct database query to sum revenue from attendees for user's events $revenue = $wpdb->get_var( $wpdb->prepare( "SELECT SUM(CAST(pm.meta_value AS DECIMAL(10,2))) FROM {$wpdb->posts} p LEFT JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = '_tribe_tpp_ticket_price' WHERE p.post_type = %s AND p.post_parent IN ( SELECT ID FROM {$wpdb->posts} WHERE post_type = %s AND post_author = %d AND post_status IN ('publish', 'private') )", 'tribe_tpp_attendees', 'tribe_events', $this->user_id ) ); return (float) ($revenue ?: 0.00); } /** * Get the annual revenue target set by the trainer. * * @return float|null Returns the target as a float, or null if not set. */ public function get_annual_revenue_target() { $target = get_user_meta( $this->user_id, 'annual_revenue_target', true ); return ! empty( $target ) && is_numeric( $target ) ? (float) $target : null; } /** * Get the data needed for the events table on the dashboard. * * @param string $filter_status The status to filter events by ('all', 'publish', 'future', 'draft', 'pending', 'private'). Defaults to 'all'. * @return array An array of event data arrays/objects, each containing keys like: id, status, name, link, date, organizer, capacity, sold, revenue. */ public function get_events_table_data( $filter_status = 'all' ) { // Use direct database approach since TEC interferes with WP_Query return $this->get_events_table_data_direct( $filter_status ); } /** * Get events table data using direct database queries (bypassing TEC query interference) */ private function get_events_table_data_direct( $filter_status = 'all' ) { global $wpdb; $events_data = []; $valid_statuses = array( 'publish', 'future', 'draft', 'pending', 'private' ); // Build status filter for SQL if ( 'all' === $filter_status || ! in_array( $filter_status, $valid_statuses, true ) ) { $status_placeholders = implode( ',', array_fill( 0, count( $valid_statuses ), '%s' ) ); $status_values = $valid_statuses; } else { $status_placeholders = '%s'; $status_values = array( $filter_status ); } // Use direct database query to get events (bypassing TEC query modifications) $sql = "SELECT ID, post_title, post_status, post_date FROM {$wpdb->posts} WHERE post_type = %s AND post_author = %d AND post_status IN ($status_placeholders) ORDER BY post_date DESC"; $query_params = array_merge( array( 'tribe_events', $this->user_id ), $status_values ); $events = $wpdb->get_results( $wpdb->prepare( $sql, $query_params ) ); if ( ! empty( $events ) ) { foreach ( $events as $event ) { $event_id = $event->ID; // Get event start date $start_date = get_post_meta( $event_id, '_EventStartDate', true ); $start_date_ts = $start_date ? strtotime( $start_date ) : time(); // Get sold count from attendees $sold = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->posts} WHERE post_type = 'tribe_tpp_attendees' AND post_parent = %d", $event_id ) ); // Get revenue from attendees $revenue = $wpdb->get_var( $wpdb->prepare( "SELECT SUM(CAST(pm.meta_value AS DECIMAL(10,2))) FROM {$wpdb->posts} p LEFT JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = '_tribe_tpp_ticket_price' WHERE p.post_type = 'tribe_tpp_attendees' AND p.post_parent = %d", $event_id ) ); // Build event data array (matching template expectations) $events_data[] = array( 'id' => $event_id, 'name' => $event->post_title, // Template expects 'name' 'status' => $event->post_status, 'start_date_ts' => $start_date_ts, // Template expects this 'link' => get_permalink( $event_id ), 'organizer_id' => $this->user_id, // Template expects this 'capacity' => 50, // Default capacity 'sold' => (int) ($sold ?: 0), 'revenue' => (float) ($revenue ?: 0.00), ); } } return $events_data; if ( ! empty( $events ) ) { foreach ( $events as $event ) { $event_id = $event->ID; $event_id = get_the_ID(); // Get Capacity - Sum capacity of all tickets for this event $total_capacity = 0; if ( function_exists( 'tribe_get_tickets' ) ) { $tickets = tribe_get_tickets( $event_id ); if ( $tickets ) { foreach ( $tickets as $ticket ) { $capacity = $ticket->capacity(); // -1 often means unlimited capacity for Tribe Tickets if ( $capacity === -1 ) { $total_capacity = -1; // Mark as unlimited break; // No need to sum further if one is unlimited } if ( is_numeric( $capacity ) ) { $total_capacity += $capacity; } } } } // Get sold and revenue counts, checking for both standard and alternative meta fields $sold = get_post_meta( $event_id, '_tribe_tickets_sold', true ); if (!is_numeric($sold)) { $sold = get_post_meta( $event_id, '_tribe_ticket_sold_count', true ); // If still no valid count, calculate from attendees if (!is_numeric($sold)) { $sold = $this->count_event_attendees($event_id); if ($sold > 0) { update_post_meta($event_id, '_tribe_tickets_sold', $sold); } } } $revenue = get_post_meta( $event_id, '_tribe_revenue_total', true ); if (!is_numeric($revenue)) { $revenue = $this->calculate_event_revenue($event_id); if ($revenue > 0) { update_post_meta($event_id, '_tribe_revenue_total', $revenue); } } $events_data[] = array( 'id' => $event_id, 'status' => get_post_status( $event_id ), 'name' => get_the_title(), // Return raw data instead of calling TEC functions here 'link' => get_permalink( $event_id ), // Use standard WP permalink 'start_date_ts' => strtotime( get_post_meta( $event_id, '_EventStartDate', true ) ), // Return timestamp 'organizer_id' => (int) get_post_meta( $event_id, '_EventOrganizerID', true ), // Return organizer ID 'capacity' => ( $total_capacity === -1 ) ? 'Unlimited' : (int) $total_capacity, 'sold' => is_numeric( $sold ) ? (int) $sold : 0, 'revenue' => is_numeric( $revenue ) ? (float) $revenue : 0.0, ); } wp_reset_postdata(); // Restore original Post Data } return $events_data; } /** * Count the number of attendees for an event by querying attendee posts * * @param int $event_id Event ID to count attendees for * @return int Number of attendees found */ private function count_event_attendees( $event_id ) { $attendee_count = 0; // Check if Event Tickets is active if (class_exists('Tribe__Tickets__Tickets_Handler') && method_exists(Tribe__Tickets__Tickets_Handler::instance(), 'get_attendees_by_id')) { $attendees = Tribe__Tickets__Tickets_Handler::instance()->get_attendees_by_id($event_id); if (is_array($attendees)) { $attendee_count = count($attendees); } } else { // Fallback to direct query if Event Tickets not available $attendees_query = new WP_Query([ 'post_type' => 'tribe_tpp_attendees', 'posts_per_page' => -1, 'fields' => 'ids', 'meta_query' => [ [ 'key' => '_tribe_tpp_event', 'value' => $event_id, 'compare' => '=', ], ], ]); $attendee_count = $attendees_query->found_posts; } return $attendee_count; } /** * Calculate total revenue for an event by summing ticket prices * * @param int $event_id Event ID to calculate revenue for * @return float Total revenue calculated */ private function calculate_event_revenue( $event_id ) { $total_revenue = 0.0; // Check if Event Tickets is active if (class_exists('Tribe__Tickets__Tickets_Handler') && method_exists(Tribe__Tickets__Tickets_Handler::instance(), 'get_attendees_by_id')) { $attendees = Tribe__Tickets__Tickets_Handler::instance()->get_attendees_by_id($event_id); if (is_array($attendees)) { foreach ($attendees as $attendee) { // Extract price - structure might vary based on ticket provider $price = 0; if (isset($attendee['price']) && is_numeric($attendee['price'])) { $price = (float)$attendee['price']; } elseif (isset($attendee['price_paid']) && is_numeric($attendee['price_paid'])) { $price = (float)$attendee['price_paid']; } $total_revenue += $price; } } } else { // Fallback to direct calculation if Event Tickets not available // First get all tickets for this event to get prices $tickets_query = new WP_Query([ 'post_type' => 'tribe_tpp_tickets', 'posts_per_page' => -1, 'meta_query' => [ [ 'key' => '_tribe_tpp_for_event', 'value' => $event_id, 'compare' => '=', ], ], ]); if ($tickets_query->have_posts()) { while ($tickets_query->have_posts()) { $tickets_query->the_post(); $ticket_id = get_the_ID(); $price = get_post_meta($ticket_id, '_price', true); $sold = get_post_meta($ticket_id, '_tribe_tpp_sold', true); if (is_numeric($price) && is_numeric($sold)) { $total_revenue += ((float)$price * (int)$sold); } } wp_reset_postdata(); } } return $total_revenue; } } // End class HVAC_Dashboard_Data